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:
@@ -1151,6 +1151,91 @@ test('agent run awaits session Finish before succeeding when proxy supports it',
|
||||
assert.equal(finishEvents.length, 1);
|
||||
});
|
||||
|
||||
test('terminal agent run records structured experience on success', async () => {
|
||||
const pool = createFakePool();
|
||||
const recorded = [];
|
||||
const gateway = createAgentRunGateway({
|
||||
pool,
|
||||
userAuth: {},
|
||||
tkmindProxy: {
|
||||
async startSessionForUser() {
|
||||
return { id: 'session-exp-1' };
|
||||
},
|
||||
async submitSessionReplyAndAwaitFinishForUser() {
|
||||
return {
|
||||
ok: true,
|
||||
finishEvent: { type: 'Finish' },
|
||||
toolEvidence: { calls: ['edit_file'] },
|
||||
};
|
||||
},
|
||||
},
|
||||
experienceService: {
|
||||
async record(payload) {
|
||||
recorded.push(payload);
|
||||
return { id: 'exp-1', ...payload };
|
||||
},
|
||||
},
|
||||
retryDelaysMs: [],
|
||||
});
|
||||
|
||||
const run = await gateway.createRun('user-1', {
|
||||
requestId: 'req-exp-1',
|
||||
userMessage: {
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: '修复 SSE 断线' }],
|
||||
metadata: { displayText: '修复 SSE 断线' },
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded');
|
||||
assert.equal(recorded.length, 1);
|
||||
assert.equal(recorded[0].kind, 'task_outcome');
|
||||
assert.equal(recorded[0].problem, '修复 SSE 断线');
|
||||
assert.equal(recorded[0].evidence.provenance.run_id, run.id);
|
||||
assert.match(recorded[0].body, /edit_file/);
|
||||
});
|
||||
|
||||
test('terminal agent run records structured experience on failure', async () => {
|
||||
const pool = createFakePool();
|
||||
const recorded = [];
|
||||
const gateway = createAgentRunGateway({
|
||||
pool,
|
||||
userAuth: {},
|
||||
tkmindProxy: {
|
||||
async startSessionForUser() {
|
||||
return { id: 'session-exp-fail' };
|
||||
},
|
||||
async submitSessionReplyAndAwaitFinishForUser() {
|
||||
const error = new Error('goosed unavailable');
|
||||
error.code = 'GOOSED_UNAVAILABLE';
|
||||
error.retryable = false;
|
||||
throw error;
|
||||
},
|
||||
},
|
||||
experienceService: {
|
||||
async record(payload) {
|
||||
recorded.push(payload);
|
||||
return { id: 'exp-fail', ...payload };
|
||||
},
|
||||
},
|
||||
retryDelaysMs: [],
|
||||
});
|
||||
|
||||
const run = await gateway.createRun('user-1', {
|
||||
requestId: 'req-exp-fail',
|
||||
userMessage: {
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: '部署 headscale' }],
|
||||
metadata: { displayText: '部署 headscale' },
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => pool.runs.get(run.id)?.status === 'failed');
|
||||
assert.equal(recorded.length, 1);
|
||||
assert.equal(recorded[0].result, 'failure');
|
||||
assert.match(recorded[0].body, /goosed unavailable/);
|
||||
});
|
||||
|
||||
test('terminal agent run quiesces session extensions after preserving Finish', async () => {
|
||||
const pool = createFakePool();
|
||||
const quiesced = [];
|
||||
|
||||
Reference in New Issue
Block a user