diff --git a/tauri/src/runtime/coordination/lineup-runtime.test.ts b/tauri/src/runtime/coordination/lineup-runtime.test.ts index 58fab59..51275da 100644 --- a/tauri/src/runtime/coordination/lineup-runtime.test.ts +++ b/tauri/src/runtime/coordination/lineup-runtime.test.ts @@ -379,6 +379,27 @@ describe("LineUpRuntime", () => { ]); }); + it("ignores channel messages addressed to another user before scope routing", async () => { + const foreign = agentEnvelope("foreign-user-message", "lineup:2:channel_1:user_2", { + target: { kind: "human", id: "user_2" }, + }); + const own = agentEnvelope("current-user-message", "lineup:2:channel_1:user_1", { + target: { kind: "human", id: "user_1" }, + }); + const transport = new FakeTransport([[ + { message_seq: 40, from_uid: "agent_1", payload: foreign }, + { message_seq: 41, from_uid: "agent_1", payload: own }, + ], []]); + const runtime = new LineUpRuntime({ storage: new MemoryStorage(), createTransport: () => transport }); + const chat = runtime.openChatApp(); + await runtime.login(login); + await runtime.syncOnce(); + + expect(chat.listAgentMessages()).toHaveLength(1); + expect(chat.listAgentMessages()[0]?.message_id).toBe("current-user-message"); + expect(runtime.snapshot()?.cursor).toBe(41); + }); + it("rejects malformed scope and never delivers the same message id twice", async () => { const conversationID = "user_1:2:channel_1"; const duplicate = agentEnvelope("dedupe_me", conversationID); diff --git a/tauri/src/runtime/coordination/lineup-runtime.ts b/tauri/src/runtime/coordination/lineup-runtime.ts index 2dc2eea..293f5f4 100644 --- a/tauri/src/runtime/coordination/lineup-runtime.ts +++ b/tauri/src/runtime/coordination/lineup-runtime.ts @@ -754,6 +754,16 @@ export class LineUpRuntime { runtimeLog("message.ignored", { stage: "sender" }); return; } + // The AppServer channel is shared by several users. Agent envelopes carry + // the intended human target; discard another user's message before scope + // validation so it is not reported as a malformed message for this user. + const parsedEnvelope = parseEnvelopeJSON(payload); + if (parsedEnvelope.ok + && parsedEnvelope.envelope.target?.kind === "human" + && parsedEnvelope.envelope.target.id !== this.session.uid) { + runtimeLog("message.ignored", { stage: "target" }); + return; + } if (message.from_uid === this.session.uid) { const own = parseEnvelopeJSON(payload); if (own.ok && CONTROL_ECHO_TYPES.has(own.envelope.type)) return; diff --git a/迭代/03.sdk_and_coreapp/03.sdk_and_coreapp.md b/迭代/03.sdk_and_coreapp/03.sdk_and_coreapp.md index 7fae66c..053e020 100644 --- a/迭代/03.sdk_and_coreapp/03.sdk_and_coreapp.md +++ b/迭代/03.sdk_and_coreapp/03.sdk_and_coreapp.md @@ -1,7 +1,7 @@ # LineUp App 迭代定义:MiniApp SDK v1 与内置参考 MiniApp **迭代编号:** 03.sdk_and_coreapp -**状态:** 实现完成,最终验收待完成(核心 Runtime / SDK 闭环和自动化验证已完成;真实 AppServer 旧 Web Host 进程需重启后才能加载最新构建) +**状态:** 已完成 **日期:** 2026-08-05 **前置基线:** [00.base.md](../00.base/00.base.md)、[01.kernel.md](../01.kernel/01.kernel.md) **权威架构:** [APP架构设计.md](../../APP架构设计.md) @@ -913,7 +913,7 @@ Whiteboard instance 启动时创建自己的 App 子会话;Agent 与用户围 当前实现已落地以下边界:标准交互记录已接入 ConversationStore(当前版本 14),Runtime 登录时恢复并清理未提交 input 草稿;`interaction.dismiss` 已由 Tool Router 接入 Runtime;默认注册表已使用 `system / bundled`,并内置 Task Dashboard、Whiteboard 的 Manifest、受限 SDK 入口和参考实现。bundled MiniApp 的 Surface 请求现在由 Runtime 在校验 instance / scope 后发出本地事件,再由 Host 挂载、更新或卸载隔离 Surface,不再伪装成发给 Agent 的 UI 协议消息。Task Dashboard、Whiteboard 已通过同一套 SDK 自动装配,覆盖 Tool、progress/result、Surface 和生命周期;App Inbox 已按 instance 过滤并在 ACK 时再次校验;普通 Tool 的 `submitted` outbox 重启恢复、App 子会话只读历史和“继续处理”(新 instance / 新子会话)已有实现与自动化测试。 -验收记录:`npm run build`、`npm test -- --run`(29 个测试文件、124 个测试)和 `git diff --check` 全部通过;已使用 `agent-browser` 登录本地测试账号,确认页面可进入、同步状态正常、应用子会话区域和“启用任务面板”入口可见。期间发现 AppServer 频道同步会返回其他用户的消息,Runtime 已增加发送者过滤;同时兼容 AppServer 使用的 `lineup:::` 会话键,并将出站协议统一改为该格式。当前 1420 端口的旧 Web Host 进程未重启,仍可能展示修复前的旧控制台日志;重启 Web Host 后即可加载最新构建。 +验收记录:`npm run build`、`npm test -- --run`(29 个测试文件、125 个测试)和 `git diff --check` 全部通过;已使用全新 `agent-browser` 会话登录本地测试账号,确认页面进入、同步状态、主 IM 消息、应用子会话区域和“启用任务面板”入口均正常;发送“第三次迭代验收测试”后页面显示“已发送”。AppServer 频道同步会返回其他用户的消息,Runtime 已按发送者和 Agent 目标用户过滤;同时兼容 AppServer 使用的 `lineup:::` 会话键,并将出站协议统一改为该格式。最终浏览器控制台未再出现 `scope_mismatch`,只保留跨用户消息的安全忽略日志。 本迭代预计在现有目录中演进,不重建并行 Runtime: