feat(goal-run): add multi-checkpoint goal orchestration with H5 and admin surfaces.
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>
This commit is contained in:
@@ -90,6 +90,153 @@ test('admin memory-v2 config routes expose config and runtime state', async () =
|
||||
}
|
||||
});
|
||||
|
||||
test('admin goal-runs routes expose summary, list, and detail', async () => {
|
||||
const router = createAdminApi({
|
||||
jsonBody: express.json(),
|
||||
getToken() {
|
||||
return 'token-admin';
|
||||
},
|
||||
userAuth: {
|
||||
async getMe(token) {
|
||||
if (token !== 'token-admin') return null;
|
||||
return { id: 'admin-1', role: 'admin' };
|
||||
},
|
||||
},
|
||||
goalRunAdminOpsService: {
|
||||
getRuntime() {
|
||||
return { enabled: true, canaryUserIds: ['user-1'], canaryMode: true };
|
||||
},
|
||||
async countByStatus() {
|
||||
return { active: 2, completed: 1 };
|
||||
},
|
||||
async listGoals() {
|
||||
return [{
|
||||
id: 'goal-1',
|
||||
userId: 'user-1',
|
||||
username: 'john2',
|
||||
title: '产品规划',
|
||||
intentSummary: '分阶段完成',
|
||||
status: 'active',
|
||||
priority: 5,
|
||||
sourceChannel: 'h5',
|
||||
sourceSessionId: null,
|
||||
currentCheckpointId: 'cp-1',
|
||||
checkpointCount: 2,
|
||||
activeAgentRunCount: 1,
|
||||
createdAt: 1000,
|
||||
updatedAt: 2000,
|
||||
completedAt: null,
|
||||
}];
|
||||
},
|
||||
async getGoalDetail(goalRunId) {
|
||||
if (goalRunId !== 'goal-1') return null;
|
||||
return {
|
||||
id: 'goal-1',
|
||||
userId: 'user-1',
|
||||
username: 'john2',
|
||||
title: '产品规划',
|
||||
intentSummary: '分阶段完成',
|
||||
status: 'active',
|
||||
priority: 5,
|
||||
sourceChannel: 'h5',
|
||||
sourceSessionId: null,
|
||||
sourceMessageId: null,
|
||||
currentCheckpointId: 'cp-1',
|
||||
context: null,
|
||||
memorySnapshot: null,
|
||||
createdAt: 1000,
|
||||
updatedAt: 2000,
|
||||
completedAt: null,
|
||||
checkpoints: [{
|
||||
id: 'cp-1',
|
||||
goalRunId: 'goal-1',
|
||||
sequence: 1,
|
||||
title: '调研',
|
||||
description: null,
|
||||
status: 'running',
|
||||
agentRunId: 'run-1',
|
||||
outputSummary: null,
|
||||
userFeedback: null,
|
||||
approvedAt: null,
|
||||
createdAt: 1000,
|
||||
updatedAt: 2000,
|
||||
startedAt: 1500,
|
||||
completedAt: null,
|
||||
}],
|
||||
agentRuns: [{
|
||||
id: 'run-1',
|
||||
status: 'running',
|
||||
requestId: 'req-1',
|
||||
goalCheckpointId: 'cp-1',
|
||||
createdAt: 1000,
|
||||
updatedAt: 2000,
|
||||
completedAt: null,
|
||||
}],
|
||||
canaryEnabled: true,
|
||||
};
|
||||
},
|
||||
async approveCheckpoint({ goalRunId, checkpointId, feedback, reviewedBy }) {
|
||||
assert.equal(goalRunId, 'goal-1');
|
||||
assert.equal(checkpointId, 'cp-1');
|
||||
assert.equal(feedback, 'looks good');
|
||||
assert.equal(reviewedBy, 'admin-1');
|
||||
return { id: 'goal-1', status: 'active', checkpoints: [] };
|
||||
},
|
||||
async cancelGoal({ goalRunId, reviewedBy }) {
|
||||
assert.equal(goalRunId, 'goal-1');
|
||||
assert.equal(reviewedBy, 'admin-1');
|
||||
return { id: 'goal-1', status: 'cancelled', checkpoints: [] };
|
||||
},
|
||||
},
|
||||
});
|
||||
const server = await startTestServer(router);
|
||||
try {
|
||||
const summaryRes = await fetch(`${server.baseUrl}/admin-api/goal-runs/summary`, {
|
||||
headers: { cookie: 'h5_user_session=token-admin' },
|
||||
});
|
||||
assert.equal(summaryRes.status, 200);
|
||||
const summaryBody = await summaryRes.json();
|
||||
assert.equal(summaryBody.counts.active, 2);
|
||||
assert.equal(summaryBody.runtime.enabled, true);
|
||||
|
||||
const listRes = await fetch(`${server.baseUrl}/admin-api/goal-runs`, {
|
||||
headers: { cookie: 'h5_user_session=token-admin' },
|
||||
});
|
||||
assert.equal(listRes.status, 200);
|
||||
const listBody = await listRes.json();
|
||||
assert.equal(listBody.items[0].id, 'goal-1');
|
||||
|
||||
const detailRes = await fetch(`${server.baseUrl}/admin-api/goal-runs/goal-1`, {
|
||||
headers: { cookie: 'h5_user_session=token-admin' },
|
||||
});
|
||||
assert.equal(detailRes.status, 200);
|
||||
const detailBody = await detailRes.json();
|
||||
assert.equal(detailBody.goal.checkpoints.length, 1);
|
||||
|
||||
const approveRes = await fetch(`${server.baseUrl}/admin-api/goal-runs/goal-1/checkpoints/cp-1/approve`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
cookie: 'h5_user_session=token-admin',
|
||||
},
|
||||
body: JSON.stringify({ feedback: 'looks good' }),
|
||||
});
|
||||
assert.equal(approveRes.status, 200);
|
||||
|
||||
const cancelRes = await fetch(`${server.baseUrl}/admin-api/goal-runs/goal-1/cancel`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
cookie: 'h5_user_session=token-admin',
|
||||
},
|
||||
body: JSON.stringify({}),
|
||||
});
|
||||
assert.equal(cancelRes.status, 200);
|
||||
} finally {
|
||||
await server.close();
|
||||
}
|
||||
});
|
||||
|
||||
test('admin orchestrator routes expose a versioned plug-in control plane', async () => {
|
||||
const updates = [];
|
||||
const state = {
|
||||
|
||||
Reference in New Issue
Block a user