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:
@@ -231,6 +231,8 @@ function createFakePool({ sessionDeliverables = {}, workspaceDeliverables = {} }
|
||||
id,
|
||||
userId,
|
||||
sessionId,
|
||||
goalRunId,
|
||||
goalCheckpointId,
|
||||
requestId,
|
||||
userMessageJson,
|
||||
createdAt,
|
||||
@@ -242,6 +244,8 @@ function createFakePool({ sessionDeliverables = {}, workspaceDeliverables = {} }
|
||||
id,
|
||||
user_id: userId,
|
||||
agent_session_id: sessionId,
|
||||
goal_run_id: goalRunId,
|
||||
goal_checkpoint_id: goalCheckpointId,
|
||||
request_id: requestId,
|
||||
status: 'queued',
|
||||
attempts: 0,
|
||||
@@ -2199,6 +2203,92 @@ test('agent run uses direct chat on regular agent sessions when llm routes direc
|
||||
assert.ok(pool.events.some((event) => event.eventType === 'direct_chat_completed'));
|
||||
});
|
||||
|
||||
test('agent run reuses portal session when direct chat fails on first turn', async () => {
|
||||
const previousNormalized = process.env.MEMIND_ROUTER_NORMALIZED_DECISION;
|
||||
process.env.MEMIND_ROUTER_NORMALIZED_DECISION = '1';
|
||||
const pool = createFakePool();
|
||||
const startedSessions = [];
|
||||
const submitted = [];
|
||||
try {
|
||||
const gateway = createAgentRunGateway({
|
||||
pool,
|
||||
userAuth: {},
|
||||
tkmindProxy: {
|
||||
async startSessionForUser() {
|
||||
startedSessions.push('called');
|
||||
return { id: '20260731_12' };
|
||||
},
|
||||
async submitSessionReplyAndAwaitFinishForUser(userId, sessionId, requestId, userMessage) {
|
||||
submitted.push({ userId, sessionId, requestId, userMessage });
|
||||
return {
|
||||
ok: true,
|
||||
finishEvent: { type: 'Finish' },
|
||||
tokenState: { totalTokens: 8 },
|
||||
};
|
||||
},
|
||||
},
|
||||
chatIntentRouter: {
|
||||
isEnabled() {
|
||||
return true;
|
||||
},
|
||||
async classify() {
|
||||
return {
|
||||
route: 'direct_chat',
|
||||
confidence: 0.95,
|
||||
reason: '简单寒暄或连通性测试',
|
||||
source: 'rule',
|
||||
decision: {
|
||||
mode: 'sse',
|
||||
flags: [],
|
||||
route: 'chat',
|
||||
session_hint: 'new',
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
directChatService: {
|
||||
canHandle({ routingDecision }) {
|
||||
return routingDecision === 'direct_chat';
|
||||
},
|
||||
explainCanHandle({ routingDecision }) {
|
||||
return { ok: routingDecision === 'direct_chat', reason: null };
|
||||
},
|
||||
async run() {
|
||||
throw new Error('fetch failed');
|
||||
},
|
||||
getStatus() {
|
||||
return { enabled: true };
|
||||
},
|
||||
},
|
||||
retryDelaysMs: [],
|
||||
});
|
||||
|
||||
const run = await gateway.createRun('user-1', {
|
||||
sessionId: '20260731_11',
|
||||
requestId: 'req-direct-fallback-reuse',
|
||||
userMessage: {
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: 'hi' }],
|
||||
metadata: {
|
||||
displayText: 'hi',
|
||||
memindRun: { sessionMessageCount: 0 },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded');
|
||||
assert.equal(pool.runs.get(run.id).agent_session_id, '20260731_11');
|
||||
assert.equal(startedSessions.length, 0);
|
||||
assert.equal(submitted.length, 1);
|
||||
assert.equal(submitted[0].sessionId, '20260731_11');
|
||||
assert.ok(pool.events.some((event) => event.eventType === 'direct_chat_failed'));
|
||||
assert.equal(pool.events.some((event) => event.eventType === 'session_started'), false);
|
||||
} finally {
|
||||
if (previousNormalized == null) delete process.env.MEMIND_ROUTER_NORMALIZED_DECISION;
|
||||
else process.env.MEMIND_ROUTER_NORMALIZED_DECISION = previousNormalized;
|
||||
}
|
||||
});
|
||||
|
||||
test('agent run invalidates portal direct chat snapshot before submitting to goosed', async () => {
|
||||
const pool = createFakePool({
|
||||
sessionDeliverables: {
|
||||
|
||||
Reference in New Issue
Block a user