61 lines
6.4 KiB
JavaScript
61 lines
6.4 KiB
JavaScript
// Deterministic UI data. Native/OMP execution is verified separately by the runtime regressions.
|
|
const now = Date.now()
|
|
const message = (id, role, content, extra = {}) => ({ id, role, content, createdAt: now, status: 'done', ...extra })
|
|
const tool = (id, name, args, result) => ({ id, name, args, argsRaw: JSON.stringify(args), status: 'ok', result, output: typeof result === 'string' ? result : JSON.stringify(result) })
|
|
const tools = [
|
|
tool('read', 'read_file', { path: 'src/example.ts', offset: 4, limit: 2 }, { content: 'export const value = 1', startLine: 4, endLine: 5, totalLines: 8 }),
|
|
tool('command', 'Bash', { command: 'npm run build' }, { stdout: 'Build complete', stderr: '', exitCode: 0 }),
|
|
tool('web', 'web_search', { query: 'documentation' }, { results: [{ title: 'Documentation', url: 'https://example.com', snippet: 'Example documentation result.' }] }),
|
|
tool('mcp', 'mcp_call_tool', { server_name: 'local', tool_name: 'inspect', arguments: {} }, { content: 'MCP result' }),
|
|
tool('unknown', 'custom_unknown', { input: 'x' }, 'Unknown-tool fallback output')
|
|
]
|
|
tools.push({ ...tool('plan-artifact', 'exit_plan_mode', {}, 'Plan ready'), metadata: { acp_request_permission: { planInfo: { planFilePath: 'plans/not-materialized.md', plan: '# 尚未落盘的计划\n\n- [ ] 验证内存内容预览\n- [ ] 保留原始 Markdown' } } } })
|
|
export const conversationCompleteFixture = {
|
|
sessions: [
|
|
{ id: 'conversation-complete', title: '八类对话组件验证', createdAt: now, updatedAt: now },
|
|
{ id: 'parallel-session', title: '并行会话验证', createdAt: now - 1, updatedAt: now - 1 }
|
|
],
|
|
messages: {
|
|
'conversation-complete': [
|
|
message('complete-user', 'user', '对齐问答、审批、工具、计划和消息操作。', { requestId: 'complete-request' }),
|
|
message('complete-assistant', 'assistant', '已完成组件恢复。', { requestId: 'complete-request', tools, parts: [
|
|
{ type: 'reasoning', id: 'thinking', text: '先读取实现,再检查状态和宿主回调。', finished: true },
|
|
...tools.map((item) => ({ type: 'tool', toolId: item.id })),
|
|
{ type: 'plan', plan: { id: 'plan', title: '对齐计划', steps: [{ id: '1', text: '恢复组件', status: 'completed' }, { id: '2', text: '接入宿主', status: 'in_progress' }] } },
|
|
{ type: 'task', task: { id: 'task', title: '检查工具状态', description: '验证成功、失败和取消状态。', status: 'success', summary: '**检查完成**,状态可恢复。' } },
|
|
{ type: 'text', text: '输入“问答验证”或“审批验证”检查完整交互。支持在运行时切换另一个会话并保留各自草稿。' }
|
|
] })
|
|
],
|
|
'parallel-session': [message('parallel-user', 'user', '第二个独立会话。'), message('parallel-assistant', 'assistant', '此处的草稿和运行状态独立保存。')]
|
|
},
|
|
files: { 'src/example.ts': 'line 1\nline 2\nline 3\nexport const value = 1\nline 5\nline 6\nline 7\nline 8' }
|
|
}
|
|
|
|
export const conversationCompleteBridge = `<script>
|
|
(()=>{
|
|
const fixture=window.__YCODE_FIXTURE__, listeners=new Set(), pending=new Map();let seq=0;
|
|
const emit=(event)=>listeners.forEach(listener=>listener(event));
|
|
const finish=(run,text)=>{pending.delete(run.runId);emit({sessionId:run.sessionId,runId:run.runId,type:'text',delta:text});emit({sessionId:run.sessionId,runId:run.runId,type:'done',content:text,tools:[]})};
|
|
window.ycode.chat.onEvent=(listener)=>{listeners.add(listener);return ()=>listeners.delete(listener)};
|
|
window.ycode.chat.send=async(payload)=>{
|
|
const run={sessionId:payload.sessionId,runId:'fixture-run-'+(++seq)}, assistantMessageId=run.runId+'-assistant';
|
|
const userMessage={id:run.runId+'-user',requestId:run.runId,planId:payload.planId,role:'user',content:payload.text,attachments:payload.attachments,createdAt:Date.now(),status:'done'};
|
|
const row=fixture.messages[run.sessionId]??(fixture.messages[run.sessionId]=[]);
|
|
let resultMessages;
|
|
if(payload.action){let index=row.findIndex(item=>item.id===payload.fromMessageId);if(payload.action==='regenerate')index=row.slice(0,index).findLastIndex(item=>item.role==='user');row.splice(index);resultMessages=[...row,userMessage,{id:assistantMessageId,requestId:run.runId,role:'assistant',content:'',status:'streaming',createdAt:Date.now()}]}
|
|
row.push(userMessage);
|
|
setTimeout(()=>{
|
|
if(/问答/.test(payload.text)){const interaction={id:run.runId+'-question',runId:run.runId,toolCallId:'fixture-question',kind:'question',title:'选择验证方式',status:'pending',questions:[{id:'choice',question:'选择哪种验证方式?',options:['源码和状态验证','浏览器交互验证']}]};pending.set(run.runId,{...run,interaction});emit({...run,type:'interaction',interaction})}
|
|
else if(/审批/.test(payload.text)){const interaction={id:run.runId+'-approval',requestId:run.runId+'-approval',runId:run.runId,toolCallId:'fixture-write',kind:'approval',title:'写入示例文件',status:'pending',options:[{optionId:'allow-once',name:'允许一次',kind:'allow_once'},{optionId:'reject-once',name:'拒绝',kind:'reject_once'}]};pending.set(run.runId,{...run,interaction});emit({...run,type:'interaction',interaction})}
|
|
else finish(run,'验证回复:'+payload.text);
|
|
},50);
|
|
return {...run,assistantMessageId,userMessage,...(resultMessages?{messages:resultMessages}:{})}
|
|
};
|
|
window.ycode.chat.respond=async(runId,id,response)=>{const run=pending.get(runId);if(!run||run.interaction.id!==id)return {accepted:false};const interaction={...run.interaction,status:response.kind==='approval'&&response.optionId==='reject-once'?'rejected':'answered',...(response.kind==='question'?{answer:response.answer,skipped:response.skipped}:{selectedOptionId:response.optionId})};emit({...run,type:'interaction',interaction});finish(run,response.kind==='question'?'已收到回答并继续。':'审批已回传并继续。');return {accepted:true}};
|
|
window.ycode.chat.cancel=async(runId)=>{const run=pending.get(runId);if(run){pending.delete(runId);emit({...run,type:'cancelled'})}return {cancelled:!!run}};
|
|
window.ycode.chat.feedback=async()=>{};
|
|
window.ycode.attachments={pick:async()=>[{id:'fixture-attachment',name:'example.txt',mimeType:'text/plain',size:15}],stage:async(input)=>({id:'fixture-staged-'+(++seq),name:input.name,mimeType:input.mimeType,size:input.dataBase64.length}),remove:async()=>{}};
|
|
window.ycode.speech={transcribe:async()=>{throw new Error('UI fixture does not call a speech service')}};
|
|
})();
|
|
</script>`
|