666db0b939
Persist goal runs in MySQL, bind agent runs to checkpoints, expose awaiting-approval UX in chat, and add admin inspection routes with local verify scripts. Co-authored-by: Cursor <cursoragent@cursor.com>
123 lines
3.8 KiB
JavaScript
123 lines
3.8 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { createAgentRunGateway } from './agent-run-gateway.mjs';
|
|
import { buildAgentOrchestrationAgentText } from './chat-intent-router.mjs';
|
|
|
|
function createGoalRunPool() {
|
|
const runs = new Map();
|
|
const events = [];
|
|
return {
|
|
runs,
|
|
events,
|
|
async query(sql, params = []) {
|
|
if (sql.includes('SELECT * FROM h5_agent_runs WHERE user_id = ? AND request_id = ?')) {
|
|
const [userId, requestId] = params;
|
|
const row = [...runs.values()].find(
|
|
(item) => item.user_id === userId && item.request_id === requestId,
|
|
);
|
|
return [[row].filter(Boolean)];
|
|
}
|
|
if (sql.includes('agent_session_id = ?') && sql.includes("status NOT IN ('succeeded', 'failed')")) {
|
|
return [[]];
|
|
}
|
|
if (sql.includes('INSERT INTO h5_agent_runs')) {
|
|
const [
|
|
id,
|
|
userId,
|
|
sessionId,
|
|
goalRunId,
|
|
goalCheckpointId,
|
|
requestId,
|
|
userMessageJson,
|
|
createdAt,
|
|
updatedAt,
|
|
] = params;
|
|
runs.set(id, {
|
|
id,
|
|
user_id: userId,
|
|
agent_session_id: sessionId,
|
|
goal_run_id: goalRunId,
|
|
goal_checkpoint_id: goalCheckpointId,
|
|
request_id: requestId,
|
|
status: 'queued',
|
|
attempts: 0,
|
|
user_message_json: userMessageJson,
|
|
created_at: createdAt,
|
|
updated_at: updatedAt,
|
|
});
|
|
return [{ affectedRows: 1 }];
|
|
}
|
|
if (sql.includes('INSERT INTO h5_agent_run_events')) {
|
|
events.push({
|
|
runId: params[1],
|
|
eventType: params[2],
|
|
dataJson: params[3],
|
|
});
|
|
return [{ affectedRows: 1 }];
|
|
}
|
|
if (sql.includes('SELECT * FROM h5_agent_runs WHERE id = ? LIMIT 1')) {
|
|
return [[runs.get(params[0])].filter(Boolean)];
|
|
}
|
|
if (sql.includes('INSERT INTO h5_agent_run_snapshots')) {
|
|
return [{ affectedRows: 1 }];
|
|
}
|
|
throw new Error(`Unexpected SQL: ${sql.slice(0, 120)}`);
|
|
},
|
|
};
|
|
}
|
|
|
|
test('createRun persists goal_run_id and goal_checkpoint_id', async () => {
|
|
const pool = createGoalRunPool();
|
|
const attachCalls = [];
|
|
const gateway = createAgentRunGateway({
|
|
pool,
|
|
userAuth: {},
|
|
tkmindProxy: {},
|
|
autoDispatch: false,
|
|
goalRunService: {
|
|
async attachAgentRunToCheckpoint(input) {
|
|
attachCalls.push(input);
|
|
return true;
|
|
},
|
|
},
|
|
});
|
|
|
|
const run = await gateway.createRun('user-1', {
|
|
requestId: 'req-goal-1',
|
|
userMessage: { role: 'user', content: [{ type: 'text', text: '分阶段任务' }] },
|
|
goalRunId: 'goal-1',
|
|
goalCheckpointId: 'cp-1',
|
|
});
|
|
|
|
assert.equal(run.requestId, 'req-goal-1');
|
|
const stored = [...pool.runs.values()][0];
|
|
assert.equal(stored.goal_run_id, 'goal-1');
|
|
assert.equal(stored.goal_checkpoint_id, 'cp-1');
|
|
assert.deepEqual(attachCalls, [{
|
|
checkpointId: 'cp-1',
|
|
agentRunId: stored.id,
|
|
}]);
|
|
const queuedEvent = pool.events.find((event) => event.eventType === 'queued');
|
|
assert.match(String(queuedEvent?.dataJson ?? ''), /"goalRunId":"goal-1"/);
|
|
});
|
|
|
|
test('buildAgentOrchestrationAgentText injects goal context before memory context', () => {
|
|
const text = buildAgentOrchestrationAgentText({
|
|
displayText: '继续推进',
|
|
classification: { reason: '长期任务续作' },
|
|
goalContext: {
|
|
injectionEnabled: true,
|
|
envelope: '【Goal Context】\n目标:产品规划\n当前阶段:调研(进行中)',
|
|
},
|
|
memoryContext: {
|
|
injectionEnabled: true,
|
|
memories: [{ label: '偏好', text: '简洁中文' }],
|
|
},
|
|
});
|
|
const goalIndex = text.indexOf('【Goal Context】');
|
|
const memoryIndex = text.indexOf('[Memory Context]');
|
|
assert.ok(goalIndex >= 0);
|
|
assert.ok(memoryIndex > goalIndex);
|
|
assert.match(text, /目标:产品规划/);
|
|
});
|