import assert from 'node:assert/strict'; import {readFileSync} from 'node:fs'; import ts from '../apps/web/node_modules/typescript/lib/typescript.js'; async function moduleFrom(path){ const {outputText}=ts.transpileModule(readFileSync(new URL(path,import.meta.url),'utf8'),{compilerOptions:{module:ts.ModuleKind.ESNext,target:ts.ScriptTarget.ES2022}}); return import(`data:text/javascript;base64,${Buffer.from(outputText).toString('base64')}`); } const {applyReplyEvent}=await moduleFrom('../apps/web/src/reply-stream.ts'); const {subscribeToSession}=await moduleFrom('../apps/web/src/live.ts'); let state={},listeners={}; const feed=subscribeToSession('thread',event=>state=applyReplyEvent(state,event),{source:()=>({addEventListener:(kind,fn)=>listeners[kind]=fn,close(){}})}); const emit=(kind,id,payload)=>listeners[kind]({lastEventId:String(id),data:JSON.stringify(payload)}); emit('reply.started',1,{runId:'a',botId:'bot-a',generation:'first'}); emit('reply.delta',2,{runId:'a',generation:'first',text:'你好'}); emit('reply.delta',2,{runId:'a',generation:'first',text:'你好'}); assert.equal(state.a.text,'你好','reconnect replay is not appended twice'); emit('reply.started',3,{runId:'b',botId:'bot-b',generation:'parallel'}); emit('reply.delta',4,{runId:'b',generation:'parallel',text:'另一位'}); emit('reply.started',5,{runId:'a',botId:'bot-a',generation:'retry'}); emit('reply.delta',6,{runId:'a',generation:'first',text:'late failed attempt'}); assert.equal(state.a.text,'你好','already-shown text is never blanked');assert.equal(state.b.text,'另一位'); emit('reply.delta',7,{runId:'a',generation:'retry',text:'重試回覆'}); assert.equal(state.a.text,'你好\n\n重試回覆'); emit('reply.reset',8,{runId:'a',generation:'first'});assert.equal(state.a.text,'你好\n\n重試回覆'); emit('run.paused',9,{runId:'a'});assert.equal(state.a.text,'你好\n\n重試回覆','shown text is kept when the run parks'); emit('message.created',10,{runId:'b',role:'user',id:'steering',body:'keep going'});assert.equal(state.b.text,'另一位'); emit('message.created',11,{runId:'b',role:'assistant',id:'answer',body:'完整回覆'});assert.equal(state.b.messageId,'answer');assert.equal(state.b.text,'另一位\n\n完整回覆'); emit('run.completed',12,{runId:'b'});assert.equal(state.b.messageId,'answer','keep final text until transcript fetch catches up'); emit('reply.started',13,{runId:'c',botId:'bot-c',generation:'last'}); emit('session.cleared',14,{});assert.deepEqual(state,{}); emit('reply.started',15,{runId:'stop',botId:'bot-a',generation:'stopping'}); emit('reply.delta',16,{runId:'stop',generation:'stopping',text:'partial'}); emit('run.cancelled',17,{runId:'stop'});assert.equal(state.stop.text,'partial','stop does not yank tokens that already rendered'); emit('reply.started',18,{runId:'quiet',botId:'bot-a',generation:'task',quiet:true}); emit('tool.started',19,{runId:'quiet',step:'點頁面上的按鈕'}); assert.equal(state.quiet.text,'','tool activity does not become a chat message'); emit('message.created',20,{runId:'quiet',role:'assistant',id:'milestone',body:'第一份資料已核對。'}); assert.equal(state.quiet.text,'第一份資料已核對。','explicit milestones remain visible'); feed.close();console.log('Reply stream: replay, retry isolation, concurrent agents, pause, final message and clear passed');