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:
@@ -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 = [];
|
||||
|
||||
Reference in New Issue
Block a user