






















@@ -10,8 +10,10 @@ import {
1010sendMessage as runtimeSendMessage,
1111} from "./subagent-announce-delivery.runtime.js";
1212import { resolveAnnounceOrigin } from "./subagent-announce-origin.js";
13+import { resetAnnounceQueuesForTests } from "./subagent-announce-queue.js";
13141415afterEach(() => {
16+resetAnnounceQueuesForTests();
1517__testing.setDepsForTest();
1618});
1719@@ -226,6 +228,138 @@ describe("resolveAnnounceOrigin threaded route targets", () => {
226228});
227229});
228230231+describe("deliverSubagentAnnouncement queued delivery", () => {
232+async function deliverQueuedAnnouncement(params: {
233+requesterOrigin?: {
234+channel?: string;
235+to?: string;
236+accountId?: string;
237+threadId?: string | number;
238+};
239+}) {
240+const callGateway = createGatewayMock();
241+let activityChecks = 0;
242+__testing.setDepsForTest({
243+ callGateway,
244+getRequesterSessionActivity: () => ({
245+sessionId: "paperclip-session",
246+isActive: activityChecks++ === 0,
247+}),
248+loadConfig: () =>
249+({
250+messages: {
251+queue: {
252+mode: "followup",
253+debounceMs: 0,
254+},
255+},
256+}) as never,
257+});
258+259+const result = await deliverSubagentAnnouncement({
260+requesterSessionKey: "agent:eng:paperclip:issue:123",
261+targetRequesterSessionKey: "agent:eng:paperclip:issue:123",
262+triggerMessage: "child done",
263+steerMessage: "child done",
264+requesterOrigin: params.requesterOrigin,
265+requesterIsSubagent: false,
266+expectsCompletionMessage: false,
267+directIdempotencyKey: "announce-no-external-route",
268+});
269+270+expect(result).toEqual(
271+expect.objectContaining({
272+delivered: true,
273+path: "queued",
274+}),
275+);
276+await vi.waitFor(() => expect(callGateway).toHaveBeenCalledTimes(1));
277+return callGateway;
278+}
279+280+it("keeps queued announces with no external route session-only", async () => {
281+const callGateway = await deliverQueuedAnnouncement({});
282+283+expect(callGateway).toHaveBeenCalledWith(
284+expect.objectContaining({
285+method: "agent",
286+params: expect.objectContaining({
287+sessionKey: "agent:eng:paperclip:issue:123",
288+deliver: false,
289+channel: undefined,
290+accountId: undefined,
291+to: undefined,
292+threadId: undefined,
293+}),
294+}),
295+);
296+});
297+298+it("keeps queued announces with channel-only origins session-only", async () => {
299+const callGateway = await deliverQueuedAnnouncement({
300+requesterOrigin: {
301+channel: "slack",
302+},
303+});
304+305+expect(callGateway).toHaveBeenCalledWith(
306+expect.objectContaining({
307+params: expect.objectContaining({
308+deliver: false,
309+channel: undefined,
310+to: undefined,
311+}),
312+}),
313+);
314+});
315+316+it("keeps queued announces with internal origins session-only", async () => {
317+const callGateway = await deliverQueuedAnnouncement({
318+requesterOrigin: {
319+channel: "webchat",
320+to: "internal:room",
321+accountId: "acct-1",
322+threadId: "thread-1",
323+},
324+});
325+326+expect(callGateway).toHaveBeenCalledWith(
327+expect.objectContaining({
328+params: expect.objectContaining({
329+deliver: false,
330+channel: undefined,
331+accountId: undefined,
332+to: undefined,
333+threadId: undefined,
334+}),
335+}),
336+);
337+});
338+339+it("preserves queued external route fields when channel and target are present", async () => {
340+const callGateway = await deliverQueuedAnnouncement({
341+requesterOrigin: {
342+channel: "slack",
343+to: "channel:C123",
344+accountId: "acct-1",
345+threadId: "171.222",
346+},
347+});
348+349+expect(callGateway).toHaveBeenCalledWith(
350+expect.objectContaining({
351+params: expect.objectContaining({
352+deliver: true,
353+channel: "slack",
354+accountId: "acct-1",
355+to: "channel:C123",
356+threadId: "171.222",
357+}),
358+}),
359+);
360+});
361+});
362+229363describe("deliverSubagentAnnouncement completion delivery", () => {
230364it("keeps completion announces session-internal while preserving route context for active requesters", async () => {
231365const callGateway = createGatewayMock();
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。