feat(mindspace): enforce PostgreSQL user data delivery
This commit is contained in:
+47
-14
@@ -16,7 +16,7 @@ import {
|
||||
SESSION_FINISHED_STALE_GRACE_MS,
|
||||
tryRecoverRunFromDeliverables,
|
||||
} from './agent-run-deliverable-check.mjs';
|
||||
import { isPageDataIntent } from './chat-skills.mjs';
|
||||
import { isPageDataIntent, isPageGenerationIntent } from './chat-skills.mjs';
|
||||
|
||||
const DEFAULT_RUN_RETRY_DELAYS_MS = [1500, 5000, 15000];
|
||||
const TERMINAL_STATUSES = new Set(['succeeded', 'failed']);
|
||||
@@ -262,6 +262,7 @@ export function createAgentRunGateway({
|
||||
syncUserPagesOnSuccess = null,
|
||||
observePersonalMemoryOnSuccess = null,
|
||||
isSessionExternallyBusy = null,
|
||||
validateRunDeliverables = null,
|
||||
retryDelaysMs = DEFAULT_RUN_RETRY_DELAYS_MS,
|
||||
autoDispatch = envFlag(process.env.MEMIND_AGENT_RUN_AUTODISPATCH, true),
|
||||
maxConcurrentRuns = positiveInteger(
|
||||
@@ -420,7 +421,7 @@ export function createAgentRunGateway({
|
||||
|
||||
async function prepareRunDeliverables({ userId, sessionId, runId, runStartedAtMs = null }) {
|
||||
if (typeof syncUserPagesOnSuccess !== 'function') return;
|
||||
await syncUserPagesOnSuccess({
|
||||
return await syncUserPagesOnSuccess({
|
||||
userId,
|
||||
sessionId,
|
||||
runId,
|
||||
@@ -619,7 +620,7 @@ export function createAgentRunGateway({
|
||||
billed: Boolean(result.billing?.ok),
|
||||
});
|
||||
await appendRunSnapshot(runId);
|
||||
return { sessionId: result.sessionId };
|
||||
return { sessionId: result.sessionId, routing };
|
||||
} catch (err) {
|
||||
await appendEvent(runId, 'direct_chat_failed', {
|
||||
code: err?.code ?? null,
|
||||
@@ -673,7 +674,7 @@ export function createAgentRunGateway({
|
||||
});
|
||||
throw err;
|
||||
}
|
||||
return { sessionId: row.agent_session_id ?? null };
|
||||
return { sessionId: row.agent_session_id ?? null, routing };
|
||||
}
|
||||
|
||||
let sessionId = resolveGatewayAgentSessionId({
|
||||
@@ -769,7 +770,7 @@ export function createAgentRunGateway({
|
||||
sessionId,
|
||||
err,
|
||||
})) {
|
||||
return { sessionId };
|
||||
return { sessionId, routing };
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
@@ -785,16 +786,17 @@ export function createAgentRunGateway({
|
||||
},
|
||||
);
|
||||
}
|
||||
return { sessionId };
|
||||
return { sessionId, routing };
|
||||
}
|
||||
|
||||
async function finalizeSuccessfulRun(runId, row, sessionId) {
|
||||
async function finalizeSuccessfulRun(runId, row, sessionId, { routing = null } = {}) {
|
||||
let deliveryResult = null;
|
||||
if (typeof syncUserPagesOnSuccess === 'function') {
|
||||
deliveryResult = await syncUserPagesOnSuccess({
|
||||
userId: row.user_id,
|
||||
sessionId,
|
||||
runId,
|
||||
runStartedAtMs: row.started_at ?? null,
|
||||
});
|
||||
}
|
||||
const pageDataErrors = Array.isArray(deliveryResult?.pageDataBind?.errors)
|
||||
@@ -806,17 +808,48 @@ export function createAgentRunGateway({
|
||||
error.retryable = false;
|
||||
throw error;
|
||||
}
|
||||
if (isPageDataIntent(extractRunMessageText(row))) {
|
||||
const runMessageText = extractRunMessageText(row);
|
||||
const pageDataIntent = isPageDataIntent(runMessageText)
|
||||
|| routing?.suggestedSkill === 'page-data-collect';
|
||||
const pageGenerationIntent = isPageGenerationIntent(runMessageText)
|
||||
|| routing?.suggestedSkill === 'static-page-publish';
|
||||
const requiresPageDeliverable = pageDataIntent || pageGenerationIntent;
|
||||
let deliverables = null;
|
||||
if (requiresPageDeliverable || typeof validateRunDeliverables === 'function') {
|
||||
const latest = await getRunById(runId);
|
||||
const deliverables = await prepareAndDetectSessionDeliverables({
|
||||
deliverables = await prepareAndDetectSessionDeliverables({
|
||||
pool,
|
||||
userId: row.user_id,
|
||||
sessionId,
|
||||
runStartedAtMs: latest?.started_at ?? row.started_at ?? null,
|
||||
workspaceRelativePaths: deliveryResult?.pageDataRelativePaths ?? [],
|
||||
});
|
||||
if (deliverables.pageCount < 1) {
|
||||
const error = new Error('Page Data 任务未生成可交付页面,不能标记成功');
|
||||
error.code = 'PAGE_DATA_DELIVERABLE_MISSING';
|
||||
if (requiresPageDeliverable && deliverables.pageCount < 1) {
|
||||
const error = new Error(
|
||||
pageDataIntent
|
||||
? 'Page Data 任务未生成可交付页面,不能标记成功'
|
||||
: '页面任务未生成 public HTML 交付物,不能标记成功',
|
||||
);
|
||||
error.code = pageDataIntent
|
||||
? 'PAGE_DATA_DELIVERABLE_MISSING'
|
||||
: 'PUBLIC_PAGE_DELIVERABLE_MISSING';
|
||||
error.retryable = false;
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
if (typeof validateRunDeliverables === 'function') {
|
||||
const validation = await validateRunDeliverables({
|
||||
userId: row.user_id,
|
||||
sessionId,
|
||||
runId,
|
||||
deliverables: deliverables ?? { pageCount: 0, publicationCount: 0, pages: [] },
|
||||
});
|
||||
const errors = Array.isArray(validation?.errors) ? validation.errors : [];
|
||||
if (errors.length > 0) {
|
||||
const error = new Error(
|
||||
`页面交付违反数据存储策略:${errors.map((item) => item?.message ?? item?.code ?? 'unknown').join('; ')}`,
|
||||
);
|
||||
error.code = 'DELIVERABLE_DATA_STORAGE_FORBIDDEN';
|
||||
error.retryable = false;
|
||||
throw error;
|
||||
}
|
||||
@@ -856,8 +889,8 @@ export function createAgentRunGateway({
|
||||
const stopHeartbeat = startRunHeartbeat(runId, { attempt: nextAttempt });
|
||||
|
||||
try {
|
||||
const { sessionId } = await runWithTimeout(runId, () => executeRun(row, runId));
|
||||
await finalizeSuccessfulRun(runId, row, sessionId);
|
||||
const { sessionId, routing } = await runWithTimeout(runId, () => executeRun(row, runId));
|
||||
await finalizeSuccessfulRun(runId, row, sessionId, { routing });
|
||||
} catch (err) {
|
||||
const latest = await getRunById(runId);
|
||||
const recoverySessionId = latest?.agent_session_id ?? row.agent_session_id ?? null;
|
||||
|
||||
Reference in New Issue
Block a user