

























@@ -4932,4 +4932,101 @@ describe("deliverSubagentAnnouncement requester session backfill (issue #86034)"
49324932await fs.rm(storePath, { force: true });
49334933}
49344934});
4935+4936+// Cross-channel safety regression: a stale lastChannel that differs from
4937+// the completion origin's channel must not leak its lastTo into the
4938+// telegram delivery. mergeDeliveryContext.channelsConflict (delivery-context.shared.ts:233-260)
4939+// is the structural guard; this test locks it.
4940+it("does not import a cross-channel lastTo when the completion origin's channel differs from lastChannel (cross-channel guard regression)", async () => {
4941+const agentId = `xchan-${Date.now()}-${Math.random().toString(16).slice(2)}`;
4942+const sessionKey = `agent:${agentId}:telegram:5866004662`;
4943+const storeTemplate = path.join(
4944+os.tmpdir(),
4945+`openclaw-86034-xchan-session-${agentId}-{agentId}.json`,
4946+);
4947+const storePath = storeTemplate.replaceAll("{agentId}", agentId);
4948+await fs.writeFile(
4949+storePath,
4950+JSON.stringify(
4951+{
4952+[sessionKey]: {
4953+sessionId: "telegram-session-xchan",
4954+updatedAt: Date.now(),
4955+channel: "telegram",
4956+lastChannel: "signal",
4957+lastTo: "signal-stale-target",
4958+lastAccountId: "signal-bot-1",
4959+},
4960+},
4961+null,
4962+2,
4963+),
4964+"utf-8",
4965+);
4966+4967+try {
4968+const dispatchGatewayMethodInProcess = createInProcessGatewayMock({
4969+result: { payloads: [{ text: "requester voice completion" }] },
4970+});
4971+testing.setDepsForTest({
4972+ dispatchGatewayMethodInProcess,
4973+getRequesterSessionActivity: () => ({
4974+sessionId: "telegram-session-xchan",
4975+isActive: false,
4976+}),
4977+getRuntimeConfig: () => ({ session: { store: storeTemplate } }) as never,
4978+});
4979+4980+const result = await deliverSubagentAnnouncement({
4981+requesterSessionKey: sessionKey,
4982+targetRequesterSessionKey: sessionKey,
4983+triggerMessage: "image done",
4984+steerMessage: "image done",
4985+requesterOrigin: { channel: "telegram", accountId: "telegram-bot-1" },
4986+requesterSessionOrigin: { channel: "telegram", accountId: "telegram-bot-1" },
4987+completionDirectOrigin: { channel: "telegram", accountId: "telegram-bot-1" },
4988+directOrigin: { channel: "telegram", accountId: "telegram-bot-1" },
4989+requesterIsSubagent: false,
4990+expectsCompletionMessage: true,
4991+bestEffortDeliver: true,
4992+directIdempotencyKey: "announce-86034-cross-channel",
4993+sourceTool: "image_generate",
4994+});
4995+4996+// Structural assertion: the stale signal `to` must not have leaked into
4997+// any in-process gateway dispatch, regardless of whether the call ended
4998+// up routed (path: "direct") or short-circuited (delivered: false /
4999+// path: "none"). The guard is "no cross-channel `to` ever reaches the
5000+// gateway", not a specific terminal path.
5001+expect(dispatchGatewayMethodInProcess).not.toHaveBeenCalledWith(
5002+"agent",
5003+expect.objectContaining({ to: "signal-stale-target" }),
5004+expect.anything(),
5005+);
5006+expect(dispatchGatewayMethodInProcess).not.toHaveBeenCalledWith(
5007+"agent",
5008+expect.objectContaining({ channel: "signal" }),
5009+expect.anything(),
5010+);
5011+for (const call of asMock(dispatchGatewayMethodInProcess).mock.calls) {
5012+const params = call[1] as Record<string, unknown> | undefined;
5013+if (!params) continue;
5014+expect(params.to).not.toBe("signal-stale-target");
5015+expect(params.channel).not.toBe("signal");
5016+}
5017+5018+// Result-shape assertion: if the dispatcher was invoked at all on the
5019+// direct path, it must not have been with the stale signal target.
5020+if (
5021+(result as { path?: string }).path === "direct" &&
5022+(result as { delivered?: boolean }).delivered === true
5023+) {
5024+const params = mockCallArg(dispatchGatewayMethodInProcess, 0, 1) as Record<string, unknown>;
5025+expect(params.to).not.toBe("signal-stale-target");
5026+expect(params.channel).not.toBe("signal");
5027+}
5028+} finally {
5029+await fs.rm(storePath, { force: true });
5030+}
5031+});
49355032});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。