Add Experience V1 schema migration and agent-run completion extractor.

Extend h5_experience with structured fields, wire mindspace-agent-runner and agent-run-gateway to persist task_outcome records with provenance, and add local migration and verification scripts.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-09-02 10:26:46 +08:00
parent 908db04b67
commit b923e54eff
19 changed files with 1340 additions and 131 deletions
+24
View File
@@ -49,6 +49,7 @@ import {
isDirectEscalationContextEnabledForUser,
resolveDirectEscalationContextPolicy,
} from './chat-task-intent-config.mjs';
import { extractAgentRunExperience } from './experience-extractor.mjs';
const DEFAULT_RUN_RETRY_DELAYS_MS = [1500, 5000, 15000];
const TERMINAL_STATUSES = new Set(['succeeded', 'failed']);
@@ -849,6 +850,7 @@ export function createAgentRunGateway({
conversationMemoryService = null,
syncUserPagesOnSuccess = null,
observePersonalMemoryOnSuccess = null,
experienceService = null,
observeWorkflowRun = null,
observeWorkflowValidation = null,
isSessionExternallyBusy = null,
@@ -905,6 +907,7 @@ export function createAgentRunGateway({
const queuedDispatches = [];
const queuedDispatchSet = new Set();
const suggestionRepairTriggered = new Set();
const runExecutionContext = new Map();
function enqueueRun(runId) {
if (!runId || inFlight.has(runId) || queuedDispatchSet.has(runId)) return false;
@@ -2566,6 +2569,7 @@ export function createAgentRunGateway({
try {
const execution = await runWithTimeout(runId, () => executeRun(row, runId));
runExecutionContext.set(runId, execution);
await finalizeSuccessfulRun(runId, row, execution.sessionId, execution);
} catch (err) {
const latest = await getRunById(runId);
@@ -2669,6 +2673,26 @@ export function createAgentRunGateway({
stopHeartbeat();
const terminalRun = await getRunById(runId).catch(() => null);
if (terminalRun && TERMINAL_STATUSES.has(terminalRun.status)) {
const executionContext = runExecutionContext.get(runId) ?? null;
runExecutionContext.delete(runId);
if (experienceService?.record) {
await extractAgentRunExperience({
experienceService,
pool,
row: terminalRun,
runId,
status: terminalRun.status,
sessionId: terminalRun.agent_session_id ?? row.agent_session_id ?? null,
errorMessage: terminalRun.error_message ?? null,
toolEvidence: executionContext?.toolEvidence ?? null,
routing: executionContext?.routing ?? null,
}).catch((err) => {
console.warn(
'[AgentRun] experience extraction failed:',
err instanceof Error ? err.message : err,
);
});
}
if (goalRunService?.onAgentRunCompleted && terminalRun.goal_checkpoint_id) {
await goalRunService.onAgentRunCompleted({
agentRunId: runId,