From 6fa350267a60691125466c5ca76a9d8f7d6f5fb1 Mon Sep 17 00:00:00 2001 From: kyugao Date: Thu, 6 Aug 2026 00:50:21 +0800 Subject: [PATCH] fix(iteration): align reference miniapp tool names --- .../task-dashboard/task-dashboard-miniapp.ts | 12 ++++++---- .../whiteboard/whiteboard-miniapp.ts | 2 +- .../app-management/reference-miniapps.ts | 22 +++++++++---------- .../coordination/lineup-runtime.test.ts | 12 +++++----- .../coordination/miniapp-tool-state.test.ts | 4 ++-- .../runtime/coordination/tool-router.test.ts | 4 ++-- .../persistence/conversation-store.test.ts | 2 +- .../runtime/surfaces/isolated-surface-host.ts | 4 ++-- .../03.sdk_and_coreapp/04.acceptance_review.md | 12 +++++----- 9 files changed, 39 insertions(+), 35 deletions(-) diff --git a/tauri/src/core-apps/task-dashboard/task-dashboard-miniapp.ts b/tauri/src/core-apps/task-dashboard/task-dashboard-miniapp.ts index ba3e875..6438acf 100644 --- a/tauri/src/core-apps/task-dashboard/task-dashboard-miniapp.ts +++ b/tauri/src/core-apps/task-dashboard/task-dashboard-miniapp.ts @@ -22,7 +22,7 @@ export class TaskDashboardMiniApp { private async handleTool(call: { call_id: string; tool_id: string; input: JsonObject }): Promise { await this.sdk.tools.reportProgress(call.call_id, { status: "running", percent: 10, detail: call.tool_id }); - if (call.tool_id === "task.create") { + if (call.tool_id === "task-dashboard.open") { const title = typeof call.input.title === "string" ? call.input.title : "未命名任务"; const taskID = typeof call.input.task_id === "string" ? call.input.task_id : `task-${call.call_id}`; this.createTask(taskID, { title, status: "open" }); @@ -30,8 +30,12 @@ export class TaskDashboardMiniApp { await this.sdk.tools.complete(call.call_id, { task_id: taskID }); return; } - if (call.tool_id === "task.list") { - await this.sdk.tools.complete(call.call_id, { tasks: [...this.tasks.entries()].map(([task_id, task]) => ({ task_id, ...task })) }); + if (call.tool_id === "task-dashboard.update") { + const taskID = typeof call.input.task_id === "string" ? call.input.task_id : `task-${call.call_id}`; + const previous = this.tasks.get(taskID) ?? {}; + const next = { ...previous, ...(typeof call.input.status === "string" ? { status: call.input.status } : {}), ...(typeof call.input.progress === "number" ? { progress: call.input.progress } : {}) }; + this.tasks.set(taskID, next); + await this.sdk.tools.complete(call.call_id, { task_id: taskID, task: next }); return; } await this.sdk.tools.fail(call.call_id, { code: "tool_not_supported", message: `不支持的任务工具:${call.tool_id}` }); @@ -45,7 +49,7 @@ export class TaskDashboardMiniApp { public async completeTask(callID: string, taskID: string): Promise { const task = this.tasks.get(taskID) ?? {}; await this.sdk.tools.reportProgress(callID, { status: "completed", percent: 100 }); - await this.sdk.tools.complete(callID, { task_id: taskID, task }); + await this.sdk.tools.complete(callID, { status: "completed", task_id: taskID, task }); } public stop(): void { diff --git a/tauri/src/core-apps/whiteboard/whiteboard-miniapp.ts b/tauri/src/core-apps/whiteboard/whiteboard-miniapp.ts index 332a2f0..a67ee95 100644 --- a/tauri/src/core-apps/whiteboard/whiteboard-miniapp.ts +++ b/tauri/src/core-apps/whiteboard/whiteboard-miniapp.ts @@ -24,7 +24,7 @@ export class WhiteboardMiniApp { await this.sdk.tools.complete(call.call_id, { status: "completed" }); return; } - if (call.tool_id === "whiteboard.export") { + if (call.tool_id === "whiteboard.submit") { const artifactID = typeof call.input.artifact_id === "string" ? call.input.artifact_id : `whiteboard-${call.call_id}`; await this.sdk.tools.reportProgress(call.call_id, { status: "exporting", percent: 80 }); await this.sdk.surfaces.request("patch", { state: this.state, artifact: { artifact_id: artifactID } }); diff --git a/tauri/src/runtime/app-management/reference-miniapps.ts b/tauri/src/runtime/app-management/reference-miniapps.ts index 5e4f727..98737e1 100644 --- a/tauri/src/runtime/app-management/reference-miniapps.ts +++ b/tauri/src/runtime/app-management/reference-miniapps.ts @@ -10,20 +10,20 @@ export const TASK_DASHBOARD_MANIFEST: MiniAppManifestV1 = { host: { min_version: "1.0.0", surface_required: true }, tools: [ { - id: "task.create", + id: "task-dashboard.open", version: 1, - handling: "operation", + handling: "launch", target: { app_scope: "task-dashboard", requires_foreground: true, restore_previous_focus: true }, - input_schema: { type: "object", required: ["title"], properties: { title: { type: "string", minLength: 1, maxLength: 200 } }, additionalProperties: false }, - output_schema: { type: "object", required: ["task_id"], properties: { task_id: { type: "string", minLength: 1 } }, additionalProperties: false }, + input_schema: { type: "object", required: ["title"], properties: { task_id: { type: "string", minLength: 1 }, title: { type: "string", minLength: 1, maxLength: 200 }, initial_state: { type: "object" } }, additionalProperties: false }, + output_schema: { type: "object", required: ["status", "task_id"], properties: { status: { type: "string", enum: ["completed", "cancelled", "failed"] }, task_id: { type: "string", minLength: 1 }, task: { type: "object" } }, additionalProperties: false }, }, { - id: "task.list", + id: "task-dashboard.update", version: 1, handling: "direct", target: { app_scope: "task-dashboard", requires_foreground: false, restore_previous_focus: false }, - input_schema: { type: "object", additionalProperties: false }, - output_schema: { type: "object" }, + input_schema: { type: "object", required: ["task_id"], properties: { task_id: { type: "string", minLength: 1 }, progress: { type: "number", minimum: 0, maximum: 100 }, status: { type: "string" } }, additionalProperties: false }, + output_schema: { type: "object", required: ["task_id"], properties: { task_id: { type: "string", minLength: 1 }, task: { type: "object" } }, additionalProperties: false }, }, ], }; @@ -41,16 +41,16 @@ export const WHITEBOARD_MANIFEST: MiniAppManifestV1 = { version: 1, handling: "launch", target: { app_scope: "whiteboard", requires_foreground: true, restore_previous_focus: true }, - input_schema: { type: "object", additionalProperties: false }, + input_schema: { type: "object", required: ["board_id", "title"], properties: { board_id: { type: "string", minLength: 1 }, title: { type: "string", minLength: 1 }, initial_state: { type: "object" } }, additionalProperties: false }, output_schema: { type: "object" }, }, { - id: "whiteboard.export", + id: "whiteboard.submit", version: 1, handling: "operation", target: { app_scope: "whiteboard", requires_foreground: true, restore_previous_focus: true }, - input_schema: { type: "object", additionalProperties: false }, - output_schema: { type: "object", required: ["artifact_id"], properties: { artifact_id: { type: "string", minLength: 1 } }, additionalProperties: false }, + input_schema: { type: "object", required: ["board_id"], properties: { board_id: { type: "string", minLength: 1 }, submit_request: { type: "object" } }, additionalProperties: false }, + output_schema: { type: "object", properties: { artifact_id: { type: "string", minLength: 1 }, summary: { type: "object" } }, additionalProperties: false }, }, ], }; diff --git a/tauri/src/runtime/coordination/lineup-runtime.test.ts b/tauri/src/runtime/coordination/lineup-runtime.test.ts index 6e2086c..6de1013 100644 --- a/tauri/src/runtime/coordination/lineup-runtime.test.ts +++ b/tauri/src/runtime/coordination/lineup-runtime.test.ts @@ -271,7 +271,7 @@ describe("LineUpRuntime", () => { const call = JSON.stringify({ v: 1, id: "task-tool-call", type: "lineup.v1.miniapp.tool.call", conversation_id: conversationID, sender: { kind: "agent", id: "agent_1" }, - payload: { call_id: "task-tool-call", tool_id: "task.create", app_scope: "task-dashboard", inventory_revision: "catalog-1", input: { title: "写验收记录" } }, + payload: { call_id: "task-tool-call", tool_id: "task-dashboard.open", app_scope: "task-dashboard", inventory_revision: "catalog-1", input: { title: "写验收记录" } }, }); const transport = new FakeTransport([[{ message_seq: 60, from_uid: "agent_1", payload: call }], []]); const runtime = new LineUpRuntime({ storage: new MemoryStorage(), createTransport: () => transport, now: () => "2026-08-04T00:00:00.000Z", newID: prefix => `${prefix}-id` }); @@ -285,7 +285,7 @@ describe("LineUpRuntime", () => { sdk.tools.subscribe(tool => received.push(tool.call_id)); expect(received).toEqual(["task-tool-call"]); expect(runtime.miniAppToolCalls()).toEqual([expect.objectContaining({ call_id: "task-tool-call", status: "running", instance_id: instance!.instance_id })]); - await sdk.tools.complete("task-tool-call", { task_id: "task-1" }); + await sdk.tools.complete("task-tool-call", { status: "completed", task_id: "task-1" }); await runtime.flushOutbox(); expect(runtime.miniAppToolCalls()).toEqual([expect.objectContaining({ call_id: "task-tool-call", status: "completed" })]); expect(runtime.lifecycle.instances.get(instance!.instance_id)).toMatchObject({ state: "foreground" }); @@ -337,7 +337,7 @@ describe("LineUpRuntime", () => { const launch = JSON.stringify({ v: 1, id: "whiteboard-launch", type: "lineup.v1.miniapp.tool.call", conversation_id: conversationID, sender: { kind: "agent", id: "agent_1" }, - payload: { call_id: "whiteboard-launch", tool_id: "whiteboard.open", app_scope: "whiteboard", inventory_revision: "catalog-1", input: {} }, + payload: { call_id: "whiteboard-launch", tool_id: "whiteboard.open", app_scope: "whiteboard", inventory_revision: "catalog-1", input: { board_id: "board-1", title: "验收画板" } }, }); const transport = new FakeTransport([[{ message_seq: 62, from_uid: "agent_1", payload: launch }], []]); const runtime = new LineUpRuntime({ storage: new MemoryStorage(), createTransport: () => transport }); @@ -359,7 +359,7 @@ describe("LineUpRuntime", () => { it("converges an unclaimed bundled Tool once on App close and keeps no new delivery path", async () => { const conversationID = "user_1:2:channel_1"; - const call = JSON.stringify({ v: 1, id: "task-tool-close", type: "lineup.v1.miniapp.tool.call", conversation_id: conversationID, sender: { kind: "agent", id: "agent_1" }, payload: { call_id: "task-tool-close", tool_id: "task.create", app_scope: "task-dashboard", inventory_revision: "catalog-1", input: { title: "关闭测试" } } }); + const call = JSON.stringify({ v: 1, id: "task-tool-close", type: "lineup.v1.miniapp.tool.call", conversation_id: conversationID, sender: { kind: "agent", id: "agent_1" }, payload: { call_id: "task-tool-close", tool_id: "task-dashboard.open", app_scope: "task-dashboard", inventory_revision: "catalog-1", input: { title: "关闭测试" } } }); const transport = new FakeTransport([[{ message_seq: 61, from_uid: "agent_1", payload: call }], []]); const runtime = new LineUpRuntime({ storage: new MemoryStorage(), createTransport: () => transport, now: () => "2026-08-04T00:00:00.000Z" }); await runtime.login(login); @@ -393,7 +393,7 @@ describe("LineUpRuntime", () => { const call = JSON.stringify({ v: 1, id: "restart-tool-call", type: "lineup.v1.miniapp.tool.call", conversation_id: conversationID, sender: { kind: "agent", id: "agent_1" }, - payload: { call_id: "restart-tool-call", tool_id: "task.create", app_scope: "task-dashboard", inventory_revision: "catalog-1", input: { title: "重启恢复" } }, + payload: { call_id: "restart-tool-call", tool_id: "task-dashboard.open", app_scope: "task-dashboard", inventory_revision: "catalog-1", input: { title: "重启恢复" } }, }); const storage = new MemoryStorage(); const firstTransport = new FakeTransport([[{ message_seq: 70, from_uid: "agent_1", payload: call }], []]); @@ -404,7 +404,7 @@ describe("LineUpRuntime", () => { const instance = first.lifecycle.instances.activeFor("task-dashboard", conversationID)!; const sdk = first.openBundledMiniApp("task-dashboard", instance.instance_id); sdk.tools.subscribe(() => undefined); - await sdk.tools.complete("restart-tool-call", { task_id: "restart-task" }); + await sdk.tools.complete("restart-tool-call", { status: "completed", task_id: "restart-task" }); expect(first.miniAppToolCalls()).toEqual([expect.objectContaining({ call_id: "restart-tool-call", status: "submitted" })]); const secondTransport = new FakeTransport(); diff --git a/tauri/src/runtime/coordination/miniapp-tool-state.test.ts b/tauri/src/runtime/coordination/miniapp-tool-state.test.ts index 62232a8..7f00215 100644 --- a/tauri/src/runtime/coordination/miniapp-tool-state.test.ts +++ b/tauri/src/runtime/coordination/miniapp-tool-state.test.ts @@ -4,7 +4,7 @@ import { MiniAppToolStateMachine } from "@/runtime/coordination/miniapp-tool-sta describe("MiniAppToolStateMachine", () => { it("moves a call through claim and submitted result, while rejecting duplicate terminal writes", () => { const machine = new MiniAppToolStateMachine(); - machine.receive({ call_id: "call-1", tool_id: "task.create", inventory_revision: "catalog-1", app_scope: "task-dashboard", instance_id: "task:1", conversation_id: "c", input: {}, created_at: "2026-08-04T00:00:00.000Z" }); + machine.receive({ call_id: "call-1", tool_id: "task-dashboard.open", inventory_revision: "catalog-1", app_scope: "task-dashboard", instance_id: "task:1", conversation_id: "c", input: {}, created_at: "2026-08-04T00:00:00.000Z" }); expect(machine.route("call-1", "task:1", "2026-08-04T00:00:01.000Z")).toMatchObject({ disposition: "accepted", record: { status: "waiting_for_app" } }); expect(machine.start("call-1", "task:1", "2026-08-04T00:00:02.000Z")).toMatchObject({ disposition: "accepted", record: { status: "running" } }); expect(machine.submit("call-1", "task:1", { task_id: "t1" }, "2026-08-04T00:00:03.000Z")).toMatchObject({ disposition: "accepted", record: { status: "submitted" } }); @@ -14,7 +14,7 @@ describe("MiniAppToolStateMachine", () => { it("cancels only the instance that owns a pending call", () => { const machine = new MiniAppToolStateMachine(); - machine.receive({ call_id: "call-2", tool_id: "task.create", inventory_revision: "catalog-1", app_scope: "task-dashboard", instance_id: "task:1", conversation_id: "c", input: {}, created_at: "2026-08-04T00:00:00.000Z" }); + machine.receive({ call_id: "call-2", tool_id: "task-dashboard.open", inventory_revision: "catalog-1", app_scope: "task-dashboard", instance_id: "task:1", conversation_id: "c", input: {}, created_at: "2026-08-04T00:00:00.000Z" }); machine.route("call-2", "task:1", "2026-08-04T00:00:01.000Z"); expect(machine.cancel("call-2", "task:2", "app_closed", "2026-08-04T00:00:02.000Z")).toEqual(expect.objectContaining({ disposition: "invalid_scope" })); expect(machine.cancel("call-2", "task:1", "app_closed", "2026-08-04T00:00:02.000Z")).toMatchObject({ disposition: "accepted", record: { status: "cancelled", cancel_reason: "app_closed" } }); diff --git a/tauri/src/runtime/coordination/tool-router.test.ts b/tauri/src/runtime/coordination/tool-router.test.ts index 6094053..b9158db 100644 --- a/tauri/src/runtime/coordination/tool-router.test.ts +++ b/tauri/src/runtime/coordination/tool-router.test.ts @@ -26,10 +26,10 @@ describe("ToolRouter", () => { const apps = new CoreAppRegistry(); apps.install({ app_scope: "custom-dashboard", kind: "bundled", enabled: true, default_eligible: false, recovery: false, - manifest: { app_scope: "custom-dashboard", version: "1.0.0", permissions: [], tools: [{ name: "task.create", handling: "operation", parameters: [], input_schema: { type: "object", required: ["title"], properties: { title: { type: "string" } }, additionalProperties: false } }] }, + manifest: { app_scope: "custom-dashboard", version: "1.0.0", permissions: [], tools: [{ name: "task-dashboard.open", handling: "operation", parameters: [], input_schema: { type: "object", required: ["title"], properties: { title: { type: "string" } }, additionalProperties: false } }] }, }); const router = new ToolRouter(apps); - const raw = JSON.stringify({ v: 1, id: "miniapp-call-1", type: "lineup.v1.miniapp.tool.call", conversation_id: "c1", sender: { kind: "agent", id: "agent" }, payload: { call_id: "miniapp-call-1", tool_id: "task.create", app_scope: "custom-dashboard", inventory_revision: "catalog-1", input: { title: "测试任务" } } }); + const raw = JSON.stringify({ v: 1, id: "miniapp-call-1", type: "lineup.v1.miniapp.tool.call", conversation_id: "c1", sender: { kind: "agent", id: "agent" }, payload: { call_id: "miniapp-call-1", tool_id: "task-dashboard.open", app_scope: "custom-dashboard", inventory_revision: "catalog-1", input: { title: "测试任务" } } }); expect(router.route(raw, { app_scope: "chat", conversation_id: "c1", inventory_revision: "catalog-1" })).toEqual(expect.objectContaining({ disposition: "miniapp", handling: "operation", app_scope: "custom-dashboard" })); expect(router.route(raw.replace("catalog-1", "catalog-2"), { app_scope: "chat", conversation_id: "c1", inventory_revision: "catalog-1" })).toEqual({ disposition: "rejected", code: "inventory_revision_mismatch" }); expect(router.route(raw.replace('"title":"测试任务"', '"unknown":true'), { app_scope: "chat", conversation_id: "c1", inventory_revision: "catalog-1" })).toEqual({ disposition: "rejected", code: "invalid_request" }); diff --git a/tauri/src/runtime/persistence/conversation-store.test.ts b/tauri/src/runtime/persistence/conversation-store.test.ts index fefe2bb..15f15f8 100644 --- a/tauri/src/runtime/persistence/conversation-store.test.ts +++ b/tauri/src/runtime/persistence/conversation-store.test.ts @@ -243,7 +243,7 @@ describe("ConversationStore", () => { const storage = new MemoryStorage(); const conversationID = "user_test:2:agent_channel"; const record: MiniAppToolCallRecord = { - call_id: "miniapp_001", tool_id: "task.create", inventory_revision: "catalog-1", app_scope: "task-dashboard", instance_id: "task:instance", conversation_id: conversationID, + call_id: "miniapp_001", tool_id: "task-dashboard.open", inventory_revision: "catalog-1", app_scope: "task-dashboard", instance_id: "task:instance", conversation_id: conversationID, status: "submitted", input: { title: "任务" }, result: { task_id: "task-1" }, created_at: "2026-08-04T00:00:00.000Z", updated_at: "2026-08-04T00:00:01.000Z", submitted_at: "2026-08-04T00:00:01.000Z", }; const first = new ConversationStore(storage); diff --git a/tauri/src/runtime/surfaces/isolated-surface-host.ts b/tauri/src/runtime/surfaces/isolated-surface-host.ts index e175b59..f0f3718 100644 --- a/tauri/src/runtime/surfaces/isolated-surface-host.ts +++ b/tauri/src/runtime/surfaces/isolated-surface-host.ts @@ -188,11 +188,11 @@ function bridgeScript(): string { } function taskDashboardSurfaceScript(): string { - return `const handled=new Set;const tasks=new Map;const editor=document.getElementById("editor");document.getElementById("cancel").onclick=()=>bridge("lifecycle.close",{reason:"用户关闭任务面板"});async function poll(){try{const result=await bridge("tools.list");for(const call of Array.isArray(result.calls)?result.calls:[]){if(handled.has(call.call_id)||!(call.status==="waiting_for_app"||call.status==="running"))continue;handled.add(call.call_id);await handleTask(call)}}catch(_){}}async function handleTask(call){try{await bridge("tools.reportProgress",{call_id:call.call_id,progress:{status:"running",percent:10}});if(call.tool_id==="task.create"){const title=typeof call.input?.title==="string"?call.input.title:"未命名任务";const task_id=typeof call.input?.task_id==="string"?call.input.task_id:"task-"+call.call_id;tasks.set(task_id,{title,status:"open"});await bridge("surface.patch",{data:{state:{title:"任务面板",status:"已创建:"+title,percent:100,steps:[...tasks.values()].map(x=>x.title)}}});await bridge("tools.complete",{call_id:call.call_id,result:{task_id}})}else if(call.tool_id==="task.list"){await bridge("tools.complete",{call_id:call.call_id,result:{tasks:[...tasks.entries()].map(([task_id,task])=>Object.assign({task_id},task))}})}else await bridge("tools.fail",{call_id:call.call_id,code:"tool_not_supported",message:"不支持的任务工具"})}catch(_){await bridge("tools.fail",{call_id:call.call_id,code:"app_bridge_failed",message:"任务面板处理失败"}).catch(()=>{})}}setInterval(poll,500);poll();`; + return `const handled=new Set;const tasks=new Map;const editor=document.getElementById("editor");document.getElementById("cancel").onclick=()=>bridge("lifecycle.close",{reason:"用户关闭任务面板"});async function poll(){try{const result=await bridge("tools.list");for(const call of Array.isArray(result.calls)?result.calls:[]){if(handled.has(call.call_id)||!(call.status==="waiting_for_app"||call.status==="running"))continue;handled.add(call.call_id);await handleTask(call)}}catch(_){}}async function handleTask(call){try{await bridge("tools.reportProgress",{call_id:call.call_id,progress:{status:"running",percent:10}});if(call.tool_id==="task-dashboard.open"){const title=typeof call.input?.title==="string"?call.input.title:"未命名任务";const task_id=typeof call.input?.task_id==="string"?call.input.task_id:"task-"+call.call_id;tasks.set(task_id,{title,status:"open"});await bridge("surface.patch",{data:{state:{title:"任务面板",status:"已创建:"+title,percent:100,steps:[...tasks.values()].map(x=>x.title)}}});await bridge("tools.complete",{call_id:call.call_id,result:{status:"completed",task_id}})}else if(call.tool_id==="task-dashboard.update"){const task_id=String(call.input?.task_id||("task-"+call.call_id));const task={...(tasks.get(task_id)||{}),...(typeof call.input?.status==="string"?{status:call.input.status}:{}),...(typeof call.input?.progress==="number"?{progress:call.input.progress}: {})};tasks.set(task_id,task);await bridge("tools.complete",{call_id:call.call_id,result:{task_id,task}})}else await bridge("tools.fail",{call_id:call.call_id,code:"tool_not_supported",message:"不支持的任务工具"})}catch(_){await bridge("tools.fail",{call_id:call.call_id,code:"app_bridge_failed",message:"任务面板处理失败"}).catch(()=>{})}}setInterval(poll,500);poll();`; } function whiteboardSurfaceScript(): string { - return `const handled=new Set;let board={title:"Whiteboard",status:"可编辑",elements:[]};const editor=document.getElementById("editor");document.getElementById("cancel").onclick=()=>bridge("lifecycle.close",{reason:"用户关闭画板"});editor.addEventListener("input",()=>{board={...board,text:editor.value};bridge("surface.patch",{data:{state:board}}).catch(()=>{})});async function poll(){try{const result=await bridge("tools.list");for(const call of Array.isArray(result.calls)?result.calls:[]){if(handled.has(call.call_id)||!(call.status==="waiting_for_app"||call.status==="running"))continue;handled.add(call.call_id);await handleBoard(call)}}catch(_){}}async function handleBoard(call){try{if(call.tool_id==="whiteboard.open"){await bridge("tools.reportProgress",{call_id:call.call_id,progress:{status:"opening",percent:40}});await bridge("tools.complete",{call_id:call.call_id,result:{status:"completed"}})}else if(call.tool_id==="whiteboard.export"){const artifact_id=typeof call.input?.artifact_id==="string"?call.input.artifact_id:"whiteboard-"+call.call_id;await bridge("tools.reportProgress",{call_id:call.call_id,progress:{status:"exporting",percent:80}});await bridge("surface.patch",{data:{state:{...board,artifact:{artifact_id}}}});await bridge("tools.complete",{call_id:call.call_id,result:{artifact_id}})}else await bridge("tools.fail",{call_id:call.call_id,code:"tool_not_supported",message:"不支持的画板工具"})}catch(_){await bridge("tools.fail",{call_id:call.call_id,code:"app_bridge_failed",message:"画板处理失败"}).catch(()=>{})}}setInterval(poll,500);poll();`; + return `const handled=new Set;let board={title:"Whiteboard",status:"可编辑",elements:[]};const editor=document.getElementById("editor");document.getElementById("cancel").onclick=()=>bridge("lifecycle.close",{reason:"用户关闭画板"});editor.addEventListener("input",()=>{board={...board,text:editor.value};bridge("surface.patch",{data:{state:board}}).catch(()=>{})});async function poll(){try{const result=await bridge("tools.list");for(const call of Array.isArray(result.calls)?result.calls:[]){if(handled.has(call.call_id)||!(call.status==="waiting_for_app"||call.status==="running"))continue;handled.add(call.call_id);await handleBoard(call)}}catch(_){}}async function handleBoard(call){try{if(call.tool_id==="whiteboard.open"){await bridge("tools.reportProgress",{call_id:call.call_id,progress:{status:"opening",percent:40}});await bridge("tools.complete",{call_id:call.call_id,result:{status:"completed"}})}else if(call.tool_id==="whiteboard.submit"){const artifact_id=typeof call.input?.artifact_id==="string"?call.input.artifact_id:"whiteboard-"+call.call_id;await bridge("tools.reportProgress",{call_id:call.call_id,progress:{status:"submitting",percent:80}});await bridge("surface.patch",{data:{state:{...board,artifact:{artifact_id}}}});await bridge("tools.complete",{call_id:call.call_id,result:{artifact_id}})}else await bridge("tools.fail",{call_id:call.call_id,code:"tool_not_supported",message:"不支持的画板工具"})}catch(_){await bridge("tools.fail",{call_id:call.call_id,code:"app_bridge_failed",message:"画板处理失败"}).catch(()=>{})}}setInterval(poll,500);poll();`; } export function productionSurfaceDocument(instanceID: string, verifiedBundle: string): string { diff --git a/迭代/03.sdk_and_coreapp/04.acceptance_review.md b/迭代/03.sdk_and_coreapp/04.acceptance_review.md index 45406b3..5668270 100644 --- a/迭代/03.sdk_and_coreapp/04.acceptance_review.md +++ b/迭代/03.sdk_and_coreapp/04.acceptance_review.md @@ -22,7 +22,7 @@ | ✅ | P1 | A4 | 同一个 App 的多个 instance 之间 Inbox 订阅没有隔离 | `lineup-runtime.ts` | 已按 `app_scope + conversation_id + instance_id` 过滤实时订阅,并补充双实例回归测试。 | | ✅ | P1 | A5 | Interact 使用专用 `ChatRuntimeSDK` 旁路,没有和其他 MiniApp 共用 SDK v1 语义 | `tauri/src/runtime/app-management/app-sdk.ts`、`lineup-runtime.ts`、`main.ts` | 已明确为“通用 Runtime 操作 + 可信 UI 投影”:Interact 保留可信 DOM 和 IM 展示,但行为统一走 `sdk.runtime.*`,展示走 `sdk.ui.*`;旧扁平方法仅作兼容别名。 | | ✅ | P1 | A6 | 标准交互的超时、dismiss、提交和答案 schema 没有由 Runtime 统一裁决 | `lineup-runtime.ts`、`agent-interaction-service.ts`、`standard-interaction-contract.ts` | 已让本地提交、取消、过期和 Agent dismiss 同步推进 Interaction 与 Kernel;notice 也会完成 Kernel;四种答案在 Runtime 按固定 schema 校验,默认有效期和重启过期均只写一条 outbox。 | -| 🔴 | P2 | A7 | 迭代文档中的 Tool 名称和代码 Manifest 不一致 | 主文档 §5.2/§5.3;`reference-miniapps.ts` | 统一契约、Manifest、fixture、Agent Inventory 和验收脚本中的名称。 | +| ✅ | P2 | A7 | 迭代文档中的 Tool 名称和代码 Manifest 不一致 | 主文档 §5.2/§5.3;`reference-miniapps.ts` | 已统一为 `task-dashboard.open`、`task-dashboard.update`、`whiteboard.open`、`whiteboard.submit`;Manifest schema、MiniApp 实现、sandbox iframe、fixture 和测试均已同步。 | | 🔴 | P2 | A8 | 当前 Task Dashboard/Whiteboard Surface 主要是 Host 硬编码的静态页面,无法证明真实业务 UI 在各自受限 Surface 内运行 | `main.ts:180-198`、`isolated-surface-host.ts` | 至少提供能代表业务边界的受限 Surface fixture,并验证 MiniApp 业务逻辑不能取得 Host DOM 或 Runtime 内部。 | | 🔴 | P2 | A9 | MiniApp progress 没有进入可恢复的 Tool 状态记录 | `miniapp-tool-state.ts` | 如果契约要求进度可恢复,需持久化最后进度并覆盖 Runtime 重启;否则修改文档,明确 progress 只是一种可丢失事件。 | | 🔴 | P3 | A10 | App session 记录和 opened/closed 事件没有显式保存 `agent_id` / `conversation_id` 字段 | `app-instance-manager.ts`、`lineup-runtime.ts:203-209,1052-1058` | P3 也必须在本迭代处理:App session opened/closed 事件和持久化记录补齐两个上下文字段。 | @@ -31,7 +31,7 @@ ## 1. 已验证通过的部分 - `npm run build` 通过。 -- `npm test -- --run` 通过:29 个测试文件、129 个测试(含默认有效期、重启过期和答案 schema 回归)。 +- `npm test -- --run` 通过:29 个测试文件、131 个测试(含 Tool 契约、默认有效期、重启过期和答案 schema 回归)。 - `git diff --check` 通过。 - 使用独立 `agent-browser` 会话登录本地测试账号成功。 - 主 IM 页面、同步状态、应用子会话区域和“启用任务面板”入口可见。 @@ -88,17 +88,17 @@ Interact 仍然可以使用可信 DOM,但它的 SDK 已明确拆成两层:`s ## 3. 验收结论 -当前结论为(完成本轮 P1-1~P1-4 后): +当前结论为(完成本轮 P1-1~P1-4,并收敛 A7 后): ```text 功能回归:通过 基础构建与自动化测试:通过 真实登录和消息发送:通过 App 架构边界:部分通过(A1/A2/A5/A6 已实现,A2 待浏览器复验) -MiniApp 隔离与 SDK 规范:A1~A6 已通过,仍需处理后续 P2/P3 +MiniApp 隔离与 SDK 规范:A1~A7 已通过,仍需处理后续 P2/P3 标准交互终态契约:通过 -迭代整体验收:不通过,继续处理 A7~A11;A2 需在最终浏览器复验中确认 +迭代整体验收:不通过,继续处理 A8~A11;A2 需在最终浏览器复验中确认 ``` -本轮独立验收由子 agent 完成。A6 已在本轮修复并通过自动化验证;下一轮继续处理 A7~A11,并重新执行 +本轮独立验收由子 agent 完成。A6 已在本轮修复并通过自动化验证,A7 已完成契约收敛并通过全量测试;下一轮继续处理 A8~A11,并重新执行 独立验收。P2/P3 不能被静默删除,必须在本迭代结束前解决或保留明确的延期记录。