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:
@@ -0,0 +1,97 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
buildAgentRunExperienceBody,
|
||||
buildAgentRunExperiencePayload,
|
||||
extractAgentRunProblem,
|
||||
extractAgentRunExperience,
|
||||
} from './experience-extractor.mjs';
|
||||
|
||||
function sampleRow(overrides = {}) {
|
||||
return {
|
||||
user_id: 'user-1',
|
||||
user_message_json: JSON.stringify({
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: '帮我修复 SSE 断线问题' }],
|
||||
metadata: {
|
||||
displayText: '帮我修复 SSE 断线问题',
|
||||
memindRun: {
|
||||
toolMode: 'chat',
|
||||
selectedChatSkill: 'page-data-collect',
|
||||
},
|
||||
},
|
||||
}),
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
test('extractAgentRunProblem prefers displayText', () => {
|
||||
assert.equal(
|
||||
extractAgentRunProblem(sampleRow()),
|
||||
'帮我修复 SSE 断线问题',
|
||||
);
|
||||
});
|
||||
|
||||
test('buildAgentRunExperiencePayload maps succeeded runs with evidence provenance', () => {
|
||||
const payload = buildAgentRunExperiencePayload({
|
||||
row: sampleRow(),
|
||||
runId: 'run-1',
|
||||
status: 'succeeded',
|
||||
sessionId: 'sess-1',
|
||||
routing: { route: 'agent_orchestration', suggestedSkill: 'page-data-collect' },
|
||||
toolEvidence: { calls: ['edit_file'] },
|
||||
});
|
||||
assert.equal(payload.kind, 'task_outcome');
|
||||
assert.equal(payload.result, 'success');
|
||||
assert.equal(payload.problem, '帮我修复 SSE 断线问题');
|
||||
assert.equal(payload.sourceSessionId, 'sess-1');
|
||||
assert.equal(payload.evidence.provenance.run_id, 'run-1');
|
||||
assert.match(payload.body, /edit_file/);
|
||||
});
|
||||
|
||||
test('buildAgentRunExperiencePayload maps failed runs to failure result', () => {
|
||||
const payload = buildAgentRunExperiencePayload({
|
||||
row: sampleRow(),
|
||||
runId: 'run-2',
|
||||
status: 'failed',
|
||||
errorMessage: 'delivery failed',
|
||||
});
|
||||
assert.equal(payload.result, 'failure');
|
||||
assert.equal(payload.body, 'delivery failed');
|
||||
});
|
||||
|
||||
test('buildAgentRunExperienceBody falls back for successful runs without tool evidence', () => {
|
||||
const body = buildAgentRunExperienceBody({
|
||||
status: 'succeeded',
|
||||
routing: { route: 'direct_chat' },
|
||||
});
|
||||
assert.match(body, /Agent run completed successfully/);
|
||||
});
|
||||
|
||||
test('extractAgentRunExperience records through experience service', async () => {
|
||||
const recorded = [];
|
||||
const pool = {
|
||||
async query() {
|
||||
return [[{
|
||||
event_type: 'session_finished',
|
||||
data_json: JSON.stringify({ toolCalls: ['write_file'] }),
|
||||
}]];
|
||||
},
|
||||
};
|
||||
const saved = await extractAgentRunExperience({
|
||||
experienceService: {
|
||||
async record(payload) {
|
||||
recorded.push(payload);
|
||||
return { id: 'exp-1', ...payload };
|
||||
},
|
||||
},
|
||||
pool,
|
||||
row: sampleRow(),
|
||||
runId: 'run-3',
|
||||
status: 'succeeded',
|
||||
sessionId: 'sess-3',
|
||||
});
|
||||
assert.equal(recorded.length, 1);
|
||||
assert.equal(saved.id, 'exp-1');
|
||||
assert.match(recorded[0].body, /write_file/);
|
||||
});
|
||||
Reference in New Issue
Block a user