import assert from 'node:assert/strict' import { renderToStaticMarkup } from 'react-dom/server' import type { ChatMessage } from '../src/shared/types' import { projectQuestionAnswers } from '../src/renderer/src/workbuddy/conversation/question-projection' import { buildCompletedFoldModel } from '../src/renderer/src/workbuddy/conversation/completed-fold' import { CompletedFold } from '../src/renderer/src/workbuddy/conversation/CompletedFold' import { StreamingFooter, resolveRuntimeStatusText, isLoadingTipPaused } from '../src/renderer/src/workbuddy/conversation/StreamingFooter' import MessageBubble from '../src/renderer/src/components/MessageBubble' const message: ChatMessage = { id:'trace', role:'assistant', content:'A B', createdAt:1, status:'streaming', parts:[ {type:'text',text:'A'}, {type:'reasoning',id:'r1',text:'thought one'}, {type:'tool',toolId:'t1'}, {type:'text',text:'B'}, {type:'reasoning',id:'r2',text:'thought two'}, {type:'tool',toolId:'t2'} ], tools: ['t1','t2'].map(id=>({id,name:'Read',args:{file_path:id},argsRaw:'{}',status:'ok'})) } const model=buildCompletedFoldModel(message.parts!,{turnFinished:false})! assert.deepEqual(model.summaryGroups.map(group=>[group.startIndex,group.shouldFold]),[[1,true],[4,false]]) const live=renderToStaticMarkup( ) assert.equal((live.match(/cr-live-event-feed/g)||[]).length,1) assert.match(live,/data-activity-tool-id="t1"/) assert.match(live.replace(/<[^>]*>/g,''),/thought two/) assert.equal((live.match(/data-tool-name="Read"/g)||[]).length,2) const expanded=renderToStaticMarkup({p.type}}/> ) assert.deepEqual([...expanded.matchAll(/data-index="(\d+)"/g)].map(m=>Number(m[1])),[0,1,2,3,4,5]) const question: ChatMessage={...message,parts:[{type:'tool',toolId:'q'},{type:'text',text:'after'}],tools:[{id:'q',name:'AskUserQuestion',args:{},argsRaw:'{}',status:'ok'}],interactions:[{kind:'question',id:'i',runId:'r',toolCallId:'q',title:'Q',status:'answered',questions:[],answer:{questions:[{id:'q1',question:'Choose',answers:['yes']}]}}]} const projection=projectQuestionAnswers(question) assert.equal(projection.unpositioned.length,0) assert.deepEqual(projection.tools[0].metadata?.questionAnswer,question.interactions![0].kind==='question'&&question.interactions![0].answer) assert.equal(question.tools![0].metadata,undefined,'projection must not mutate persisted source') assert.equal(projectQuestionAnswers({...question,tools:[],parts:[{type:'text',text:'old'}]}).unpositioned.length,1) assert.equal(projectQuestionAnswers({...question,interactions:[...question.interactions!,...question.interactions!]}).tools.length,1) assert.equal(resolveRuntimeStatusText('准备项目',{source:'agent',phase:'tool',toolName:'Read'}),'准备项目') assert.equal(resolveRuntimeStatusText(null,{source:'interaction',phase:'waiting-answer'}),'等待回答') assert.equal(resolveRuntimeStatusText(null,{source:'agent',phase:'finishing'}),'正在收尾') assert.equal(renderToStaticMarkup(),'') assert.match(renderToStaticMarkup(),/等待授权/) console.log('alignment-conversation: local groups, original expanded order, question projection and truthful status passed') assert.equal(isLoadingTipPaused({hovered:false,focused:true}),true) assert.equal(isLoadingTipPaused({hovered:true,focused:false}),true) assert.equal(isLoadingTipPaused({hovered:false,focused:false}),false)