fix: persist direct chat transcript before Goosed escalation

Prevent portal direct-chat history from being lost when snapshot cache
is invalidated on Goosed submit by writing non-empty messages to DB first,
and stop caching empty Goose placeholder messages in snapshots.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
john
2026-07-05 11:16:55 +08:00
parent 614837e199
commit bdeab23b83
6 changed files with 311 additions and 5 deletions
+134
View File
@@ -640,6 +640,140 @@ test('agent run escalates direct sessions to a new backend session when forced',
assert.ok(pool.events.some((event) => event.eventType === 'direct_session_escalated_to_deep_reasoning'));
});
test('agent run persists direct session transcript before escalating to goosed', async () => {
const pool = createFakePool();
const submitted = [];
const saved = [];
const removed = [];
const gateway = createAgentRunGateway({
pool,
tkmindProxy: {
async startSessionForUser(userId) {
assert.equal(userId, 'user-1');
return { id: 'deep-session-1' };
},
async submitSessionReplyForUser(userId, sessionId, requestId, userMessage, options = {}) {
submitted.push({ userId, sessionId, requestId, userMessage, options });
},
},
sessionSnapshotService: {
async get(sessionId) {
if (sessionId !== 'h5direct_existing') return null;
return {
messages: [
{ role: 'user', content: [{ type: 'text', text: '中考政策' }] },
{ role: 'assistant', content: [{ type: 'text', text: '政策摘要' }] },
],
};
},
async remove(sessionId) {
removed.push(sessionId);
},
},
conversationMemoryService: {
async saveConversationMessages(sessionId, userId, messages) {
saved.push({ sessionId, userId, messages });
return messages;
},
},
retryDelaysMs: [],
});
const run = await gateway.createRun('user-1', {
sessionId: 'h5direct_existing',
requestId: 'req-force-deep-transcript',
userMessage: { role: 'user', content: [{ type: 'text', text: '帮我生成页面 public/a.html' }] },
forceDeepReasoning: true,
});
await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded');
assert.equal(saved.length, 1);
assert.equal(saved[0].sessionId, 'deep-session-1');
assert.equal(saved[0].messages.length, 2);
assert.ok(pool.events.some((event) => event.eventType === 'direct_session_transcript_persisted'));
assert.equal(submitted[0].sessionId, 'deep-session-1');
assert.equal(removed.length, 1);
assert.equal(removed[0], 'deep-session-1');
});
test('agent run persists portal direct snapshot before goosed submit on same session', async () => {
const pool = createFakePool();
const submitted = [];
const saved = [];
const gateway = createAgentRunGateway({
pool,
userAuth: {
async getUserCapabilities() {
return { grantedSkills: ['static-page-publish'] };
},
},
tkmindProxy: {
async submitSessionReplyForUser(userId, sessionId, requestId, userMessage) {
submitted.push({ userId, sessionId, requestId, userMessage });
},
},
sessionSnapshotService: {
async get(sessionId) {
if (sessionId !== '20260705_2') return null;
return {
messages: [
{ role: 'user', content: [{ type: 'text', text: '深度搜索' }] },
{ role: 'assistant', content: [{ type: 'text', text: '搜索结果' }], metadata: { source: 'portal-direct-chat' } },
],
};
},
async remove(sessionId) {
assert.equal(sessionId, '20260705_2');
},
},
conversationMemoryService: {
async saveConversationMessages(sessionId, userId, messages) {
saved.push({ sessionId, userId, messages });
return messages;
},
},
chatIntentRouter: {
isEnabled() {
return true;
},
async classify() {
return {
route: 'agent_orchestration',
confidence: 0.93,
reason: '需要生成页面',
suggestedSkill: 'static-page-publish',
source: 'llm',
};
},
applyAgentOrchestration(userMessage, classification) {
const displayText = userMessage?.content?.[0]?.text ?? '';
return {
...userMessage,
content: [{
type: 'text',
text: `【Memind 任务编排】${classification.reason}\n用户任务:${displayText}`,
}],
};
},
},
retryDelaysMs: [],
});
const run = await gateway.createRun('user-1', {
sessionId: '20260705_2',
requestId: 'req-portal-direct-persist',
userMessage: { role: 'user', content: [{ type: 'text', text: '生成报告' }] },
});
await waitFor(() => pool.runs.get(run.id)?.status === 'succeeded');
assert.equal(saved.length, 1);
assert.equal(saved[0].sessionId, '20260705_2');
assert.equal(saved[0].messages.length, 2);
assert.ok(pool.events.some((event) => event.eventType === 'portal_direct_transcript_persisted'));
assert.equal(submitted.length, 1);
assert.equal(submitted[0].sessionId, '20260705_2');
});
test('agent run with code tool mode starts and submits with code policy', async () => {
const pool = createFakePool();
const submitted = [];