feat(coding): keep one ongoing conversation per delivered agent

This commit is contained in:
2026-09-28 14:19:02 +08:00
parent e54f3d234d
commit 7aa81da616
23 changed files with 1205 additions and 160 deletions

View File

@@ -539,6 +539,23 @@ async function installCodingFirstChatHost(
}
if (path === '/api/coding/teacher/teachers') return respond({ items: teachers });
if (path === '/api/coding/teacher/config') return respond({enabled:true,revision:1,published_version:1,definition:teacherDefinition});
const agentChat = path.match(/^\/api\/coding\/agent-conversations\/([^/?]+)(?:\/(messages|seen))?(?:\?.*)?$/);
if (agentChat) {
const agentId = agentChat[1], action = agentChat[2];
const selected = teachers.find(item => item.teacher_id === agentId)!;
const current = consultationTopics[agentId] ?? {schemaVersion:1,revision:0,id:'chat-' + agentId,accountId:'e2e',projectId:project.id,sourceConversationId:'project',definition:selected.definition,version:selected.version,createdAt:now,updatedAt:now,requests:[],conversation:{agentId,segmentTurns:0,discussions:{}}};
if (!action && method === 'GET') return respond({topic: consultationTopics[agentId] ?? null, before:null});
if (action === 'seen') {
consultationTopics[agentId] = {...current, revision:Number(current.revision)+1,conversation:{...current.conversation as object,seenThrough:body!.requestId}};
return respond(consultationTopics[agentId]);
}
if (action === 'messages' && method === 'POST') {
const suggestions = body!.intent === 'suggestions';
const response = suggestions ? '我们可以从你最近试过的地方聊起。' : body!.intent === 'guided-help' ? '你最近做的哪一步,让你停下来想了一会儿?' : '先理解状态如何随点击变化,再修改代码。';
consultationTopics[agentId] = {...current,revision:Number(current.revision)+1,requests:[...(current.requests as unknown[]),{id:body!.requestId,projectId:body!.projectId,sourceConversationId:body!.sourceConversationId,text:body!.text,intent:body!.intent,references:body!.references??[],createdAt:new Date().toISOString(),sourceCursor:{workerGeneration:1,seq:1},sourceCapturedAt:now,includedSourceMessageIds:[],omittedMessages:0,truncatedMessages:1,status:'completed',response,...(suggestions?{suggestedQuestions:['怎样观察别人玩游戏?','我该先试哪个想法?']}:{})}]};
return respond(consultationTopics[agentId],202);
}
}
const consultationPath = path.match(/\/(agent|teacher)-topics(?:\/|$)/)?.[1];
if (consultationPath) {
const topicBase = `/${consultationPath}-topics`;
@@ -1831,7 +1848,10 @@ test('project consultations preserve student drafts and switch between work and
expect(requests.filter(item=>item.path.endsWith('/prompt')&&item.method==='POST')).toHaveLength(0);
const consultationRequests=requests.filter(item=>item.path.endsWith('/messages')&&item.method==='POST');
expect(consultationRequests).toHaveLength(3);
expect(requests.find(item => item.path.endsWith('/agent-topics') && item.method === 'POST')?.body?.teacherVersion).toBe(9);
expect(requests.filter(item => item.path.endsWith('/agent-topics') && item.method === 'POST')).toHaveLength(0);
expect(consultationRequests.every(item => item.path === '/api/coding/agent-conversations/teacher-algorithm/messages')).toBe(true);
await expect(teacher.getByRole('button', { name: '智能体新话题' })).toHaveCount(0);
await expect(teacher.getByRole('combobox')).toHaveCount(0);
await expect(teacher.getByRole('heading', { name: '朋友', exact: true })).toBeVisible();
expect(consultationRequests[0].body).toMatchObject({intent:'suggestions',text:'帮我看看'});
expect(consultationRequests[1].body).toMatchObject({text:'怎样观察别人玩游戏?'});