Files
openclaw/src/agents/subagent-announce-delivery.test.ts
2026-05-24 15:37:59 +02:00

2808 lines
87 KiB
TypeScript

import { afterEach, describe, expect, it, vi } from "vitest";
import {
testing as sessionBindingServiceTesting,
registerSessionBindingAdapter,
} from "../infra/outbound/session-binding-service.js";
import type { AgentInternalEvent } from "./internal-events.js";
import type {
EmbeddedPiQueueFailureReason,
EmbeddedPiQueueMessageOptions,
EmbeddedPiQueueMessageOutcome,
} from "./pi-embedded-runner/runs.js";
import {
testing,
deliverSubagentAnnouncement,
resolveSubagentCompletionOrigin,
} from "./subagent-announce-delivery.js";
import {
callGateway as runtimeCallGateway,
dispatchGatewayMethodInProcess as runtimeDispatchGatewayMethodInProcess,
sendMessage as runtimeSendMessage,
} from "./subagent-announce-delivery.runtime.js";
import { resolveAnnounceOrigin } from "./subagent-announce-origin.js";
afterEach(() => {
sessionBindingServiceTesting.resetSessionBindingAdaptersForTests();
testing.setDepsForTest();
});
const slackThreadOrigin = {
channel: "slack",
to: "channel:C123",
accountId: "acct-1",
threadId: "171.222",
} as const;
function createGatewayMock(response: Record<string, unknown> = {}) {
return vi.fn(async () => response) as unknown as typeof runtimeCallGateway;
}
function createGatewaySequenceMock(
responses: Record<string, unknown>[],
): ReturnType<typeof vi.fn> & typeof runtimeCallGateway {
let index = 0;
return vi.fn(async () => {
const response = responses[Math.min(index, responses.length - 1)] ?? {};
index += 1;
return response;
}) as unknown as ReturnType<typeof vi.fn> & typeof runtimeCallGateway;
}
function createInProcessGatewayMock(response: Record<string, unknown> = {}) {
return vi.fn(async () => response) as unknown as typeof runtimeDispatchGatewayMethodInProcess;
}
function createSendMessageMock() {
return vi.fn(async () => ({
channel: "slack",
to: "channel:C123",
via: "direct" as const,
mediaUrl: null,
result: { messageId: "msg-1" },
})) as unknown as typeof runtimeSendMessage;
}
type QueueEmbeddedPiMessageWithOutcome = (
sessionId: string,
message: string,
options?: EmbeddedPiQueueMessageOptions,
) => EmbeddedPiQueueMessageOutcome;
function createQueueOutcomeMock(
queued: boolean,
): ReturnType<typeof vi.fn<QueueEmbeddedPiMessageWithOutcome>> {
return vi.fn((sessionId: string) =>
queued
? {
queued: true,
sessionId,
target: "embedded_run",
gatewayHealth: "live",
enqueuedAtMs: 4_100,
deliveredAtMs: 4_200,
}
: {
queued: false,
sessionId,
reason: "not_streaming",
gatewayHealth: "live",
},
);
}
function createQueueOutcomeSequenceMock(
queuedOutcomes: (boolean | EmbeddedPiQueueFailureReason)[],
): ReturnType<typeof vi.fn<QueueEmbeddedPiMessageWithOutcome>> {
let index = 0;
return vi.fn((sessionId: string) => {
const outcome = queuedOutcomes[Math.min(index, queuedOutcomes.length - 1)] ?? false;
index += 1;
return outcome === true
? {
queued: true,
sessionId,
target: "embedded_run",
gatewayHealth: "live",
}
: {
queued: false,
sessionId,
reason: typeof outcome === "string" ? outcome : "not_streaming",
gatewayHealth: "live",
};
});
}
const longChildCompletionOutput = [
"34/34 tests pass, clean build. Now docker repro:",
"Root cause: the requester's announce delivery accepted a prefix-only assistant payload as delivered.",
"PR: https://github.com/openclaw/openclaw/pull/12345",
"Verification: pnpm test src/agents/subagent-announce-delivery.test.ts passed with the regression enabled.",
].join("\n");
function expectRecordFields(record: unknown, expected: Record<string, unknown>) {
if (!record || typeof record !== "object") {
throw new Error("Expected record");
}
const actual = record as Record<string, unknown>;
for (const [key, value] of Object.entries(expected)) {
expect(actual[key]).toEqual(value);
}
return actual;
}
function asMock(fn: unknown) {
return fn as ReturnType<typeof vi.fn>;
}
function mockCallArg(fn: unknown, callIndex = 0, argIndex = 0) {
const call = asMock(fn).mock.calls[callIndex];
if (!call) {
throw new Error(`Expected mock call ${callIndex}`);
}
return call[argIndex];
}
function expectGatewayAgentParams(
callGateway: typeof runtimeCallGateway,
expected: Record<string, unknown>,
) {
const request = expectRecordFields(mockCallArg(callGateway), { method: "agent" });
return expectRecordFields(request.params, expected);
}
function expectInProcessAgentParams(
dispatchGatewayMethodInProcess: typeof runtimeDispatchGatewayMethodInProcess,
expected: Record<string, unknown>,
) {
const method = mockCallArg(dispatchGatewayMethodInProcess, 0, 0);
expect(method).toBe("agent");
const params = mockCallArg(dispatchGatewayMethodInProcess, 0, 1);
return expectRecordFields(params, expected);
}
async function deliverSlackThreadAnnouncement(params: {
callGateway: typeof runtimeCallGateway;
isActive: boolean;
sessionId: string;
expectsCompletionMessage: boolean;
directIdempotencyKey: string;
queueEmbeddedPiMessageWithOutcome?: QueueEmbeddedPiMessageWithOutcome;
sendMessage?: typeof runtimeSendMessage;
internalEvents?: AgentInternalEvent[];
sourceTool?: string;
}) {
testing.setDepsForTest({
callGateway: params.callGateway,
getRequesterSessionActivity: () => ({
sessionId: params.sessionId,
isActive: params.isActive,
}),
getRuntimeConfig: () => ({}) as never,
sendMessage: params.sendMessage ?? runtimeSendMessage,
...(params.queueEmbeddedPiMessageWithOutcome
? { queueEmbeddedPiMessageWithOutcome: params.queueEmbeddedPiMessageWithOutcome }
: {}),
});
return deliverSubagentAnnouncement({
requesterSessionKey: "agent:main:slack:channel:C123:thread:171.222",
targetRequesterSessionKey: "agent:main:slack:channel:C123:thread:171.222",
triggerMessage: "child done",
steerMessage: "child done",
requesterOrigin: slackThreadOrigin,
requesterSessionOrigin: slackThreadOrigin,
completionDirectOrigin: slackThreadOrigin,
directOrigin: slackThreadOrigin,
requesterIsSubagent: false,
expectsCompletionMessage: params.expectsCompletionMessage,
bestEffortDeliver: true,
directIdempotencyKey: params.directIdempotencyKey,
internalEvents: params.internalEvents,
sourceTool: params.sourceTool,
});
}
async function deliverDiscordDirectMessageCompletion(params: {
callGateway: typeof runtimeCallGateway;
sendMessage?: typeof runtimeSendMessage;
internalEvents?: AgentInternalEvent[];
sourceTool?: string;
}) {
const origin = {
channel: "discord",
to: "dm:U123",
accountId: "acct-1",
};
testing.setDepsForTest({
callGateway: params.callGateway,
getRequesterSessionActivity: () => ({
sessionId: "requester-session-dm",
isActive: false,
}),
getRuntimeConfig: () => ({}) as never,
sendMessage: params.sendMessage ?? runtimeSendMessage,
});
return deliverSubagentAnnouncement({
requesterSessionKey: "agent:main:discord:dm:U123",
targetRequesterSessionKey: "agent:main:discord:dm:U123",
triggerMessage: "child done",
steerMessage: "child done",
requesterOrigin: origin,
requesterSessionOrigin: origin,
completionDirectOrigin: origin,
directOrigin: origin,
requesterIsSubagent: false,
expectsCompletionMessage: true,
bestEffortDeliver: true,
directIdempotencyKey: "announce-dm-fallback-empty",
internalEvents: params.internalEvents,
sourceTool: params.sourceTool,
});
}
async function deliverTelegramDirectMessageCompletion(params: {
callGateway: typeof runtimeCallGateway;
sendMessage?: typeof runtimeSendMessage;
internalEvents?: AgentInternalEvent[];
isActive?: boolean;
requesterSessionId?: string | null;
queueEmbeddedPiMessageWithOutcome?: QueueEmbeddedPiMessageWithOutcome;
requesterSessionKey?: string;
sourceTool?: string;
runtimeConfig?: Record<string, unknown>;
origin?: {
channel: "telegram";
to: string;
accountId?: string;
threadId?: string | number;
};
}) {
const origin = params.origin ?? {
channel: "telegram",
to: "123456789",
accountId: "bot-1",
};
const requesterSessionKey = params.requesterSessionKey ?? "agent:main:telegram:123456789";
testing.setDepsForTest({
callGateway: params.callGateway,
getRequesterSessionActivity: () => ({
sessionId:
params.requesterSessionId === null
? undefined
: (params.requesterSessionId ?? "requester-session-telegram"),
isActive: params.isActive === true,
}),
getRuntimeConfig: () => (params.runtimeConfig ?? {}) as never,
sendMessage: params.sendMessage ?? runtimeSendMessage,
...(params.queueEmbeddedPiMessageWithOutcome
? { queueEmbeddedPiMessageWithOutcome: params.queueEmbeddedPiMessageWithOutcome }
: {}),
});
return deliverSubagentAnnouncement({
requesterSessionKey,
targetRequesterSessionKey: requesterSessionKey,
triggerMessage: "child done",
steerMessage: "child done",
requesterOrigin: origin,
requesterSessionOrigin: origin,
completionDirectOrigin: origin,
directOrigin: origin,
requesterIsSubagent: false,
expectsCompletionMessage: true,
bestEffortDeliver: true,
directIdempotencyKey: "announce-telegram-dm-fallback",
internalEvents: params.internalEvents,
sourceTool: params.sourceTool,
});
}
async function deliverSlackChannelAnnouncement(params: {
callGateway: typeof runtimeCallGateway;
isActive: boolean;
sessionId: string;
expectsCompletionMessage: boolean;
directIdempotencyKey: string;
requesterSessionKey?: string;
requesterOrigin?: {
channel?: string;
to?: string;
accountId?: string;
threadId?: string | number;
};
completionDirectOrigin?: {
channel?: string;
to?: string;
accountId?: string;
threadId?: string | number;
};
queueEmbeddedPiMessageWithOutcome?: QueueEmbeddedPiMessageWithOutcome;
sendMessage?: typeof runtimeSendMessage;
internalEvents?: AgentInternalEvent[];
sourceTool?: string;
runtimeConfig?: Record<string, unknown>;
}) {
const origin = {
channel: "slack",
to: "channel:C123",
accountId: "acct-1",
} as const;
testing.setDepsForTest({
callGateway: params.callGateway,
getRequesterSessionActivity: () => ({
sessionId: params.sessionId,
isActive: params.isActive,
}),
getRuntimeConfig: () => (params.runtimeConfig ?? {}) as never,
sendMessage: params.sendMessage ?? runtimeSendMessage,
...(params.queueEmbeddedPiMessageWithOutcome
? { queueEmbeddedPiMessageWithOutcome: params.queueEmbeddedPiMessageWithOutcome }
: {}),
});
return deliverSubagentAnnouncement({
requesterSessionKey: params.requesterSessionKey ?? "agent:main:slack:channel:C123",
targetRequesterSessionKey: params.requesterSessionKey ?? "agent:main:slack:channel:C123",
triggerMessage: "child done",
steerMessage: "child done",
requesterOrigin: params.requesterOrigin ?? origin,
requesterSessionOrigin: params.requesterOrigin ?? origin,
completionDirectOrigin: params.completionDirectOrigin ?? params.requesterOrigin ?? origin,
directOrigin: params.requesterOrigin ?? origin,
requesterIsSubagent: false,
expectsCompletionMessage: params.expectsCompletionMessage,
bestEffortDeliver: true,
directIdempotencyKey: params.directIdempotencyKey,
internalEvents: params.internalEvents,
sourceTool: params.sourceTool,
});
}
describe("resolveAnnounceOrigin threaded route targets", () => {
it("preserves stored thread ids when requester origin omits one for the same chat", () => {
expect(
resolveAnnounceOrigin(
{
lastChannel: "topicchat",
lastTo: "topicchat:room-a:topic:99",
lastThreadId: 99,
},
{
channel: "topicchat",
to: "topicchat:room-a",
},
),
).toEqual({
channel: "topicchat",
to: "topicchat:room-a",
threadId: 99,
});
});
it("preserves stored thread ids for group-prefixed requester targets", () => {
expect(
resolveAnnounceOrigin(
{
lastChannel: "topicchat",
lastTo: "topicchat:room-a:topic:99",
lastThreadId: 99,
},
{
channel: "topicchat",
to: "group:room-a",
},
),
).toEqual({
channel: "topicchat",
to: "group:room-a",
threadId: 99,
});
});
it("still strips stale thread ids when the stored route points at a different chat", () => {
expect(
resolveAnnounceOrigin(
{
lastChannel: "topicchat",
lastTo: "topicchat:room-b:topic:99",
lastThreadId: 99,
},
{
channel: "topicchat",
to: "topicchat:room-a",
},
),
).toEqual({
channel: "topicchat",
to: "topicchat:room-a",
});
});
});
describe("resolveSubagentCompletionOrigin", () => {
it("resolves bound completion delivery from the requester session, not the child session", async () => {
registerSessionBindingAdapter({
channel: "discord",
accountId: "bot-alpha",
listBySession: (targetSessionKey: string) => {
if (targetSessionKey === "agent:worker:subagent:child") {
return [
{
bindingId: "discord:bot-alpha:child-window",
targetSessionKey,
targetKind: "subagent",
conversation: {
channel: "discord",
accountId: "bot-alpha",
conversationId: "child-window",
},
status: "active",
boundAt: 1,
},
];
}
return [];
},
resolveByConversation: () => null,
});
registerSessionBindingAdapter({
channel: "discord",
accountId: "acct-1",
listBySession: (targetSessionKey: string) => {
if (targetSessionKey === "agent:main:main") {
return [
{
bindingId: "discord:acct-1:parent-main",
targetSessionKey,
targetKind: "session",
conversation: {
channel: "discord",
accountId: "acct-1",
conversationId: "parent-main",
},
status: "active",
boundAt: 1,
},
];
}
return [];
},
resolveByConversation: () => null,
});
const origin = await resolveSubagentCompletionOrigin({
childSessionKey: "agent:worker:subagent:child",
requesterSessionKey: "agent:main:main",
requesterOrigin: {
channel: "discord",
accountId: "acct-1",
to: "channel:parent-main",
},
spawnMode: "session",
expectsCompletionMessage: true,
});
expect(origin).toEqual({
channel: "discord",
accountId: "acct-1",
to: "channel:parent-main",
});
});
it("prefers requester binding when child and requester share the same channel and accountId", async () => {
registerSessionBindingAdapter({
channel: "telegram",
accountId: "bot-1",
listBySession: (targetSessionKey: string) => {
if (targetSessionKey === "agent:main:telegram:default:direct:123") {
return [
{
bindingId: "telegram:bot-1:child-dm",
targetSessionKey,
targetKind: "subagent",
conversation: {
channel: "telegram",
accountId: "bot-1",
conversationId: "direct:123",
},
status: "active",
boundAt: 1,
},
];
}
if (targetSessionKey === "agent:main:main") {
return [
{
bindingId: "telegram:bot-1:parent-main",
targetSessionKey,
targetKind: "session",
conversation: {
channel: "telegram",
accountId: "bot-1",
conversationId: "direct:789",
},
status: "active",
boundAt: 1,
},
];
}
return [];
},
resolveByConversation: () => null,
});
const origin = await resolveSubagentCompletionOrigin({
childSessionKey: "agent:main:telegram:default:direct:123",
requesterSessionKey: "agent:main:main",
requesterOrigin: {
channel: "telegram",
accountId: "bot-1",
to: "telegram:direct:789",
},
spawnMode: "run",
expectsCompletionMessage: true,
});
expect(origin).toEqual({
channel: "telegram",
accountId: "bot-1",
to: "telegram:direct:789",
});
});
it("falls back to child binding when requester has no binding", async () => {
registerSessionBindingAdapter({
channel: "telegram",
accountId: "bot-1",
listBySession: (targetSessionKey: string) => {
if (targetSessionKey === "agent:main:telegram:default:direct:123") {
return [
{
bindingId: "telegram:bot-1:child-dm",
targetSessionKey,
targetKind: "subagent",
conversation: {
channel: "telegram",
accountId: "bot-1",
conversationId: "direct:123",
},
status: "active",
boundAt: 1,
},
];
}
return [];
},
resolveByConversation: () => null,
});
const origin = await resolveSubagentCompletionOrigin({
childSessionKey: "agent:main:telegram:default:direct:123",
requesterSessionKey: "agent:main:main",
requesterOrigin: {
channel: "telegram",
accountId: "bot-1",
to: "telegram:direct:123",
},
spawnMode: "run",
expectsCompletionMessage: true,
});
expect(origin).toEqual({
channel: "telegram",
accountId: "bot-1",
to: "telegram:direct:123",
});
});
});
describe("deliverSubagentAnnouncement active requester steering", () => {
async function deliverSteeredAnnouncement(params: {
mode?: "followup" | "collect" | "interrupt";
queueEmbeddedPiMessageWithOutcome?: QueueEmbeddedPiMessageWithOutcome;
requesterOrigin?: {
channel?: string;
to?: string;
accountId?: string;
threadId?: string | number;
};
}) {
const callGateway = createGatewayMock();
let activityChecks = 0;
testing.setDepsForTest({
callGateway,
getRequesterSessionActivity: () => ({
sessionId: "paperclip-session",
isActive: activityChecks++ === 0,
}),
queueEmbeddedPiMessageWithOutcome:
params.queueEmbeddedPiMessageWithOutcome ?? createQueueOutcomeMock(true),
getRuntimeConfig: () =>
({
messages: {
queue: {
mode: params.mode ?? "followup",
debounceMs: 0,
},
},
}) as never,
});
const result = await deliverSubagentAnnouncement({
requesterSessionKey: "agent:eng:paperclip:issue:123",
targetRequesterSessionKey: "agent:eng:paperclip:issue:123",
triggerMessage: "child done",
steerMessage: "child done",
requesterOrigin: params.requesterOrigin,
requesterIsSubagent: false,
expectsCompletionMessage: false,
directIdempotencyKey: "announce-no-external-route",
});
expectRecordFields(result, {
delivered: true,
path: "steered",
});
return callGateway;
}
it("steers active announces with no external route", async () => {
const callGateway = await deliverSteeredAnnouncement({});
expect(callGateway).not.toHaveBeenCalled();
});
it("steers active announces with channel-only origins", async () => {
const callGateway = await deliverSteeredAnnouncement({
requesterOrigin: {
channel: "slack",
},
});
expect(callGateway).not.toHaveBeenCalled();
});
it("steers active announces with internal origins", async () => {
const callGateway = await deliverSteeredAnnouncement({
requesterOrigin: {
channel: "webchat",
to: "internal:room",
accountId: "acct-1",
threadId: "thread-1",
},
});
expect(callGateway).not.toHaveBeenCalled();
});
it("steers active announces with external route fields", async () => {
const callGateway = await deliverSteeredAnnouncement({
requesterOrigin: {
channel: "slack",
to: "channel:C123",
accountId: "acct-1",
threadId: "171.222",
},
});
expect(callGateway).not.toHaveBeenCalled();
});
it.each(["followup", "collect", "interrupt"] as const)(
"steers active requester announces even in %s mode",
async (mode) => {
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeMock(true);
await deliverSteeredAnnouncement({
mode,
queueEmbeddedPiMessageWithOutcome,
requesterOrigin: {
channel: "slack",
to: "channel:C123",
accountId: "acct-1",
},
});
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledOnce();
},
);
it("preserves best-effort steering for active runtimes without transcript wait support", async () => {
const queueEmbeddedPiMessageWithOutcome = vi
.fn<QueueEmbeddedPiMessageWithOutcome>()
.mockImplementationOnce((sessionId: string) => ({
queued: false,
sessionId,
reason: "transcript_commit_wait_unsupported",
gatewayHealth: "live",
}))
.mockImplementationOnce((sessionId: string) => ({
queued: true,
sessionId,
target: "embedded_run",
gatewayHealth: "live",
enqueuedAtMs: 4_100,
}));
const callGateway = await deliverSteeredAnnouncement({
queueEmbeddedPiMessageWithOutcome,
requesterOrigin: {
channel: "slack",
to: "channel:C123",
accountId: "acct-1",
},
});
expect(callGateway).not.toHaveBeenCalled();
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledTimes(2);
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenNthCalledWith(
1,
"paperclip-session",
"child done",
{
steeringMode: "all",
debounceMs: 0,
waitForTranscriptCommit: true,
deliveryTimeoutMs: 120_000,
},
);
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenNthCalledWith(
2,
"paperclip-session",
"child done",
{
steeringMode: "all",
debounceMs: 0,
deliveryTimeoutMs: 120_000,
},
);
});
it("does not report delivery when active requester steering is rejected", async () => {
const queueEmbeddedPiMessageWithOutcome = vi.fn(async (sessionId: string) => ({
queued: false as const,
sessionId,
reason: "runtime_rejected" as const,
gatewayHealth: "live" as const,
errorMessage: "cannot steer a compact turn",
}));
const callGateway = createGatewayMock();
testing.setDepsForTest({
callGateway,
getRequesterSessionActivity: () => ({
sessionId: "paperclip-session",
isActive: true,
}),
queueEmbeddedPiMessageWithOutcome,
getRuntimeConfig: () =>
({
messages: {
queue: {
mode: "steer",
debounceMs: 0,
},
},
}) as never,
});
const result = await deliverSubagentAnnouncement({
requesterSessionKey: "agent:eng:paperclip:issue:123",
targetRequesterSessionKey: "agent:eng:paperclip:issue:123",
triggerMessage: "child done",
steerMessage: "child done",
requesterIsSubagent: false,
expectsCompletionMessage: false,
directIdempotencyKey: "announce-rejected-steer",
});
expectRecordFields(result, {
delivered: false,
path: "none",
phases: [{ phase: "steer-primary", delivered: false, path: "none", error: undefined }],
});
expect(callGateway).not.toHaveBeenCalled();
});
it("falls through to direct delivery when requester ends during awaited steering failure", async () => {
const queueEmbeddedPiMessageWithOutcome = vi.fn(async (sessionId: string) => ({
queued: false as const,
sessionId,
reason: "runtime_rejected" as const,
gatewayHealth: "live" as const,
errorMessage: "active session ended before queued steering message was committed",
}));
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "child completion output" }],
},
});
let activityChecks = 0;
testing.setDepsForTest({
callGateway,
getRequesterSessionActivity: () => ({
sessionId: "paperclip-session",
isActive: activityChecks++ === 0,
}),
queueEmbeddedPiMessageWithOutcome,
getRuntimeConfig: () =>
({
messages: {
queue: {
mode: "steer",
debounceMs: 0,
},
},
}) as never,
});
const result = await deliverSubagentAnnouncement({
requesterSessionKey: "agent:eng:paperclip:issue:123",
targetRequesterSessionKey: "agent:eng:paperclip:issue:123",
triggerMessage: "child done",
steerMessage: "child done",
requesterOrigin: slackThreadOrigin,
requesterIsSubagent: false,
expectsCompletionMessage: false,
directIdempotencyKey: "announce-recheck-after-steer-failure",
});
expectRecordFields(result, {
delivered: true,
path: "direct",
phases: [
{ phase: "steer-primary", delivered: false, path: "none", error: undefined },
{ phase: "direct-primary", delivered: true, path: "direct", error: undefined },
],
});
expect(callGateway).toHaveBeenCalledTimes(1);
});
});
describe("deliverSubagentAnnouncement completion delivery", () => {
it("uses an active requester queue as the completion handoff when message-tool delivery is not required", async () => {
const callGateway = createGatewayMock();
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeMock(true);
const result = await deliverSlackThreadAnnouncement({
callGateway,
sessionId: "requester-session-1",
isActive: true,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-1",
queueEmbeddedPiMessageWithOutcome,
});
expectRecordFields(result, {
delivered: true,
path: "steered",
enqueuedAt: 4_100,
deliveredAt: 4_200,
});
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledWith(
"requester-session-1",
"child done",
{
steeringMode: "all",
debounceMs: 500,
waitForTranscriptCommit: true,
deliveryTimeoutMs: 120_000,
},
);
expect(callGateway).not.toHaveBeenCalled();
});
it("does not also direct-run a queued active completion", async () => {
const callGateway = createGatewayMock();
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeMock(true);
const result = await deliverSlackThreadAnnouncement({
callGateway,
sessionId: "requester-session-1",
isActive: true,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-harness-task",
queueEmbeddedPiMessageWithOutcome,
sourceTool: "agent_harness_task",
});
expectRecordFields(result, {
delivered: true,
path: "steered",
enqueuedAt: 4_100,
deliveredAt: 4_200,
});
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledTimes(1);
expect(callGateway).not.toHaveBeenCalled();
});
it("keeps direct external delivery for dormant completion requesters", async () => {
const callGateway = createGatewayMock();
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeMock(false);
await deliverSlackThreadAnnouncement({
callGateway,
sessionId: "requester-session-2",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-1b",
queueEmbeddedPiMessageWithOutcome,
});
expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
threadId: "171.222",
bestEffortDeliver: true,
});
expect(queueEmbeddedPiMessageWithOutcome).not.toHaveBeenCalled();
});
it("uses in-process agent dispatch for dormant completion requesters", async () => {
const callGateway = createGatewayMock();
const dispatchGatewayMethodInProcess = createInProcessGatewayMock({
result: {
payloads: [{ text: "requester voice completion" }],
},
});
testing.setDepsForTest({
callGateway,
dispatchGatewayMethodInProcess,
getRequesterSessionActivity: () => ({
sessionId: "requester-session-local",
isActive: false,
}),
getRuntimeConfig: () => ({}) as never,
});
const result = await deliverSubagentAnnouncement({
requesterSessionKey: "agent:main:slack:channel:C123:thread:171.222",
targetRequesterSessionKey: "agent:main:slack:channel:C123:thread:171.222",
triggerMessage: "child done",
steerMessage: "child done",
requesterOrigin: slackThreadOrigin,
requesterSessionOrigin: slackThreadOrigin,
completionDirectOrigin: slackThreadOrigin,
directOrigin: slackThreadOrigin,
requesterIsSubagent: false,
expectsCompletionMessage: true,
bestEffortDeliver: true,
directIdempotencyKey: "announce-local-dispatch",
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(callGateway).not.toHaveBeenCalled();
expectInProcessAgentParams(dispatchGatewayMethodInProcess, {
deliver: true,
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
threadId: "171.222",
bestEffortDeliver: true,
});
expect(mockCallArg(dispatchGatewayMethodInProcess, 0, 2)).toMatchObject({
expectFinal: true,
forceSyntheticClient: true,
timeoutMs: 120_000,
});
});
it("keeps announce-agent delivery primary for dormant completion events with child output", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "requester voice completion" }],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-fallback-1",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
const params = expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
threadId: "171.222",
bestEffortDeliver: true,
});
expect(Array.isArray(params.internalEvents)).toBe(true);
expect(sendMessage).not.toHaveBeenCalled();
});
it("keeps requester-agent output primary even when it is a child-result prefix", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "34/34 tests pass, clean build. Now docker repro:" }],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-fallback-prefix",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: longChildCompletionOutput,
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("keeps word-boundary requester-agent prefixes on the mediated path", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "34/34 tests pass, clean build. Now docker repro" }],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-fallback-word-prefix",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: longChildCompletionOutput,
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("keeps mid-word requester-agent prefixes on the mediated path", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "34/34 tests pass, clean build. Now dock" }],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-fallback-midword-prefix",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: longChildCompletionOutput,
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("reports requester-agent delivery failure even when output stayed visible", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "Tests passed and the PR is ready for review." }],
deliveryStatus: {
status: "failed",
errorMessage: "Slack send failed: channel not found",
},
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-delivery-status-failed",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: false,
path: "direct",
error: "Slack send failed: channel not found",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("does not raw-send grouped child results when requester-agent output is empty", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-fallback-grouped-results",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:first",
childSessionId: "child-session-1",
announceType: "subagent task",
taskLabel: "first task",
status: "ok",
statusLabel: "completed successfully",
result: "first child result",
replyInstruction: "Summarize the result.",
},
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:second",
childSessionId: "child-session-2",
announceType: "subagent task",
taskLabel: "second task",
status: "ok",
statusLabel: "completed successfully",
result: "second child result",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("treats stale thread subagent completions as delivered after parent handoff", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
},
});
const sendMessage = createSendMessageMock();
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeSequenceMock([
"transcript_commit_wait_unsupported",
"no_active_run",
]);
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
queueEmbeddedPiMessageWithOutcome,
sessionId: "requester-session-4",
isActive: true,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-fallback-empty",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(callGateway).toHaveBeenCalledTimes(1);
expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
threadId: "171.222",
});
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledTimes(2);
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenNthCalledWith(
1,
"requester-session-4",
"child done",
{
debounceMs: 500,
deliveryTimeoutMs: 120_000,
steeringMode: "all",
waitForTranscriptCommit: true,
},
);
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenNthCalledWith(
2,
"requester-session-4",
"child done",
{
debounceMs: 500,
deliveryTimeoutMs: 120_000,
steeringMode: "all",
},
);
expect(sendMessage).not.toHaveBeenCalled();
});
it("keeps concise requester rewrites primary even when child output is long", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "Tests passed and the PR is ready for review." }],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-rewrite-primary",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: longChildCompletionOutput,
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("keeps copied complete-sentence requester summaries primary", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "34/34 tests pass, clean build." }],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-copied-summary-primary",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: longChildCompletionOutput,
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("reports failure instead of raw-sending child output when announce-agent delivery fails", async () => {
const callGateway = vi.fn(async () => {
throw new Error("UNAVAILABLE: gateway lost final output");
}) as unknown as typeof runtimeCallGateway;
const sendMessage = createSendMessageMock();
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-4",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-fallback-1",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: false,
path: "direct",
error: "UNAVAILABLE: gateway lost final output",
});
expect(callGateway).toHaveBeenCalledTimes(4);
expect(sendMessage).not.toHaveBeenCalled();
});
it("reports failure for Telegram DMs when announce-agent delivery fails", async () => {
const callGateway = createGatewayMock({
result: {
deliveryStatus: {
status: "failed",
errorMessage: "requester wake failed",
},
},
});
const sendMessage = createSendMessageMock();
const result = await deliverTelegramDirectMessageCompletion({
callGateway,
sendMessage,
queueEmbeddedPiMessageWithOutcome: createQueueOutcomeMock(false),
requesterSessionId: null,
requesterSessionKey: "agent:main:telegram:direct:123456789",
origin: {
channel: "telegram",
to: "direct:123456789",
accountId: "bot-1",
},
runtimeConfig: {
agents: {
defaults: {
subagents: {
announceTimeoutMs: 10,
},
},
},
},
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "telegram completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: false,
path: "direct",
error: "requester wake failed",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("falls back to requester-agent handoff when an active Telegram requester cannot be woken", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "child completion output" }],
},
});
const sendMessage = createSendMessageMock();
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeMock(false);
const result = await deliverTelegramDirectMessageCompletion({
callGateway,
sendMessage,
isActive: true,
queueEmbeddedPiMessageWithOutcome,
runtimeConfig: {
agents: {
defaults: {
subagents: {
announceTimeoutMs: 10,
},
},
},
},
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "telegram wake smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
phases: [
{
phase: "direct-primary",
delivered: true,
path: "direct",
error: undefined,
},
],
});
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledTimes(1);
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledWith(
"requester-session-telegram",
"child done",
{
steeringMode: "all",
debounceMs: 500,
waitForTranscriptCommit: true,
deliveryTimeoutMs: 10,
},
);
expect(callGateway).toHaveBeenCalledTimes(1);
expect(sendMessage).not.toHaveBeenCalled();
});
it("uses steer fallback when a completion handoff has no visible output", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
},
});
const queueEmbeddedPiMessageWithOutcome = vi
.fn<QueueEmbeddedPiMessageWithOutcome>()
.mockImplementationOnce((sessionId: string) => ({
queued: false,
sessionId,
reason: "not_streaming",
gatewayHealth: "live",
}))
.mockImplementationOnce((sessionId: string) => ({
queued: true,
sessionId,
target: "embedded_run",
gatewayHealth: "live",
}));
const result = await deliverSlackChannelAnnouncement({
callGateway,
sessionId: "requester-session-channel",
isActive: true,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-empty-direct-steer-fallback",
queueEmbeddedPiMessageWithOutcome,
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "channel completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
phases: [
{
phase: "direct-primary",
delivered: true,
path: "direct",
error: undefined,
},
],
});
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledTimes(1);
expect(callGateway).toHaveBeenCalledTimes(1);
});
it("does not fail stale thread subagent completions only because the parent stayed private", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
},
});
const sendMessage = createSendMessageMock();
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeSequenceMock([
"transcript_commit_wait_unsupported",
"no_active_run",
]);
const result = await deliverSlackThreadAnnouncement({
callGateway,
sendMessage,
queueEmbeddedPiMessageWithOutcome,
sessionId: "requester-session-4",
isActive: true,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-thread-fallback-empty",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "thread completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(callGateway).toHaveBeenCalledTimes(1);
expect(sendMessage).not.toHaveBeenCalled();
});
it("directly delivers generated media DMs when announce-agent returns no visible output", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sendMessage,
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.\nMEDIA:/tmp/generated-night-drive.mp3",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction: "Deliver the generated music.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: false,
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
threadId: undefined,
sourceReplyDeliveryMode: "message_tool_only",
});
expect(sendMessage).toHaveBeenCalledWith(
expect.objectContaining({
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
content: "The generated music is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
idempotencyKey: "announce-dm-fallback-empty:generated-media-direct",
}),
);
});
it("does not fallback when announce-agent delivered media through the message tool", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
didSendViaMessagingTool: false,
messagingToolSentMediaUrls: ["/tmp/generated-night-drive.mp3"],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sendMessage,
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.\nMEDIA:/tmp/generated-night-drive.mp3",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction: "Deliver the generated music through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(callGateway).toHaveBeenCalledTimes(1);
expectGatewayAgentParams(callGateway, {
deliver: false,
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
threadId: undefined,
sourceReplyDeliveryMode: "message_tool_only",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("does not fallback when message-tool evidence already contains generated media", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [
{
text: "The track is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
},
],
messagingToolSentMediaUrls: ["/tmp/generated-night-drive.mp3"],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sendMessage,
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.\nMEDIA:/tmp/generated-night-drive.mp3",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction:
"Tell the user the music is ready and send it through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("requires generated media completion DMs to use the message tool", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
messagingToolSentTargets: [
{
tool: "message",
provider: "discord",
accountId: "acct-1",
to: "dm:U123",
text: "The track is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sendMessage,
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.\nMEDIA:/tmp/generated-night-drive.mp3",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction:
"Tell the user the music is ready. If visible source delivery requires the message tool, send it there with the generated media attached.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: false,
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
threadId: undefined,
sourceReplyDeliveryMode: "message_tool_only",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("stringifies Telegram topic ids for generated video completion handoff", async () => {
const callGateway = createGatewayMock({
payloads: [],
didSendViaMessagingTool: true,
messagingToolSentMediaUrls: ["/tmp/generated-corgi.mp4"],
});
const sendMessage = createSendMessageMock();
const result = await deliverTelegramDirectMessageCompletion({
callGateway,
sendMessage,
requesterSessionKey: "agent:main:telegram:group:-1003970070733:topic:1",
origin: {
channel: "telegram",
to: "telegram:-1003970070733",
accountId: "bot-1",
threadId: 1,
},
sourceTool: "video_generate",
internalEvents: [
{
type: "task_completion",
source: "video_generation",
childSessionKey: "video_generate:task-123",
childSessionId: "task-123",
announceType: "video generation task",
taskLabel: "anime corgi skateboard",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 video.\nMEDIA:/tmp/generated-corgi.mp4",
mediaUrls: ["/tmp/generated-corgi.mp4"],
replyInstruction: "Deliver the generated video through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: false,
channel: "telegram",
accountId: "bot-1",
to: "telegram:-1003970070733",
threadId: "1",
sourceReplyDeliveryMode: "message_tool_only",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("requires generated image completion DMs to use the message tool", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
messagingToolSentTargets: [
{
tool: "message",
provider: "discord",
accountId: "acct-1",
to: "dm:U123",
text: "The image is ready.",
mediaUrls: ["/tmp/generated-robot.png"],
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sendMessage,
sourceTool: "image_generate",
internalEvents: [
{
type: "task_completion",
source: "image_generation",
childSessionKey: "image_generate:task-123",
childSessionId: "task-123",
announceType: "image generation task",
taskLabel: "small watercolor robot",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 image.\nMEDIA:/tmp/generated-robot.png",
mediaUrls: ["/tmp/generated-robot.png"],
replyInstruction:
"Tell the user the image is ready and send it through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: false,
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
threadId: undefined,
sourceReplyDeliveryMode: "message_tool_only",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("accepts failed generated media completion notices without requiring message-tool delivery", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
messagingToolSentTargets: [
{
tool: "message",
provider: "discord",
accountId: "acct-1",
to: "dm:U123",
text: "Music generation failed: provider failed.",
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sendMessage,
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "error",
statusLabel: "failed",
result: "provider failed",
replyInstruction: "Deliver the failure through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
threadId: undefined,
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("directly delivers generated media when the announce agent replies text-only", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [
{
text: "The track is ready.",
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sendMessage,
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.",
attachments: [
{
type: "audio",
path: "/tmp/generated-night-drive.mp3",
mimeType: "audio/mpeg",
name: "generated-night-drive.mp3",
},
],
replyInstruction: "Deliver the generated music.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).toHaveBeenCalledWith(
expect.objectContaining({
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
content: "The generated music is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
idempotencyKey: "announce-dm-fallback-empty:generated-media-direct",
}),
);
});
it("allows visible direct delivery for media generation failure summaries without generated media", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "Music generation failed. Provider timed out." }],
},
});
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "error",
statusLabel: "failed",
result: "All music generation models failed.",
replyInstruction: "Tell the user music generation failed.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
threadId: undefined,
});
});
it("directly delivers generated media group completions that miss required message-tool delivery", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [
{
text: "The track is ready.",
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-media-message-tool",
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.\nMEDIA:/tmp/generated-night-drive.mp3",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction:
"Tell the user the music is ready. If visible source delivery requires the message tool, send it there with the generated media attached.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: false,
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
threadId: undefined,
sourceReplyDeliveryMode: "message_tool_only",
});
expect(sendMessage).toHaveBeenCalledWith(
expect.objectContaining({
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
content: "The generated music is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
idempotencyKey: "announce-channel-media-message-tool:generated-media-direct",
}),
);
});
it("directly delivers payload-only generated media when message tool sent text only", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [
{
text: "The track is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
},
],
messagingToolSentTargets: [
{
tool: "message",
provider: "slack",
accountId: "acct-1",
to: "channel:C123",
text: "The track is ready.",
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-media-text-only-message-tool",
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.\nMEDIA:/tmp/generated-night-drive.mp3",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction:
"Tell the user the music is ready and send it through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).toHaveBeenCalledWith(
expect.objectContaining({
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
content: "The generated music is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
idempotencyKey: "announce-channel-media-text-only-message-tool:generated-media-direct",
}),
);
});
it("directly delivers only missing generated media after partial message-tool delivery", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
messagingToolSentTargets: [
{
tool: "message",
provider: "slack",
accountId: "acct-1",
to: "channel:C123",
text: "The first image is ready.",
mediaUrls: ["/tmp/generated-robot-1.png"],
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-media-partial-message-tool",
sourceTool: "image_generate",
internalEvents: [
{
type: "task_completion",
source: "image_generation",
childSessionKey: "image_generate:task-123",
childSessionId: "task-123",
announceType: "image generation task",
taskLabel: "two proof images",
status: "ok",
statusLabel: "completed successfully",
result:
"Generated 2 images.\nMEDIA:/tmp/generated-robot-1.png\nMEDIA:/tmp/generated-robot-2.png",
mediaUrls: ["/tmp/generated-robot-1.png", "/tmp/generated-robot-2.png"],
replyInstruction:
"Tell the user the images are ready and send them through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).toHaveBeenCalledWith(
expect.objectContaining({
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
content: "The generated image is ready.",
mediaUrls: ["/tmp/generated-robot-2.png"],
idempotencyKey: "announce-channel-media-partial-message-tool:generated-media-direct",
}),
);
});
it("keeps generated media completions on the active requester session path", async () => {
const callGateway = createGatewayMock();
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeMock(true);
const sendMessage = createSendMessageMock();
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-channel",
isActive: true,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-media-active-direct",
sourceTool: "video_generate",
queueEmbeddedPiMessageWithOutcome,
internalEvents: [
{
type: "task_completion",
source: "video_generation",
childSessionKey: "video_generate:task-123",
childSessionId: "task-123",
announceType: "video generation task",
taskLabel: "corgi proof video",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 video.\nMEDIA:/tmp/generated-corgi.mp4",
mediaUrls: ["/tmp/generated-corgi.mp4"],
replyInstruction:
"Tell the user the video is ready. If visible source delivery requires the message tool, send it there with the generated media attached.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "steered",
enqueuedAt: 4_100,
deliveredAt: 4_200,
});
expect(queueEmbeddedPiMessageWithOutcome).toHaveBeenCalledWith(
"requester-session-channel",
"child done",
{
steeringMode: "all",
sourceReplyDeliveryMode: "message_tool_only",
debounceMs: 500,
waitForTranscriptCommit: true,
deliveryTimeoutMs: 120_000,
},
);
expect(callGateway).not.toHaveBeenCalled();
expect(sendMessage).not.toHaveBeenCalled();
});
it.each([
{
name: "legacy Discord channel",
requesterSessionKey: "agent:main:discord:guild-123:channel-456",
origin: { channel: "discord", to: "channel:456", accountId: "acct-1" },
},
{
name: "legacy WhatsApp group",
requesterSessionKey: "agent:main:whatsapp:123@g.us",
origin: { channel: "whatsapp", to: "123@g.us", accountId: "acct-1" },
},
])(
"requires message-tool delivery for generated media completions in $name sessions",
async ({ requesterSessionKey, origin }) => {
const callGateway = createGatewayMock({
result: {
payloads: [
{
text: "The track is ready.",
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-legacy-group",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: `announce-legacy-media-message-tool-${origin.channel}`,
requesterSessionKey,
requesterOrigin: origin,
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.\nMEDIA:/tmp/generated-night-drive.mp3",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction:
"Tell the user the music is ready. If visible source delivery requires the message tool, send it there with the generated media attached.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: false,
channel: origin.channel,
accountId: "acct-1",
to: origin.to,
threadId: undefined,
sourceReplyDeliveryMode: "message_tool_only",
});
expect(sendMessage).toHaveBeenCalledWith(
expect.objectContaining({
channel: origin.channel,
accountId: "acct-1",
to: origin.to,
content: "The generated music is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
idempotencyKey: `announce-legacy-media-message-tool-${origin.channel}:generated-media-direct`,
}),
);
},
);
it("does not fallback for generated media group completions when message tool evidence exists", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
didSendViaMessagingTool: false,
messagingToolSentTargets: [
{
tool: "message",
provider: "slack",
accountId: "acct-1",
to: "channel:C123",
text: "The track is ready.",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
},
],
},
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-media-message-tool-evidence",
sourceTool: "music_generate",
internalEvents: [
{
type: "task_completion",
source: "music_generation",
childSessionKey: "music_generate:task-123",
childSessionId: "task-123",
announceType: "music generation task",
taskLabel: "night-drive synthwave",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 track.\nMEDIA:/tmp/generated-night-drive.mp3",
mediaUrls: ["/tmp/generated-night-drive.mp3"],
replyInstruction: "Deliver the generated music through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(sendMessage).not.toHaveBeenCalled();
});
it("preserves pending announce delivery without direct generated media fallback", async () => {
const callGateway = createGatewayMock({
runId: "video_generate:task-123:ok",
status: "accepted",
acceptedAt: Date.now(),
});
const sendMessage = createSendMessageMock();
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-media-pending",
sourceTool: "video_generate",
internalEvents: [
{
type: "task_completion",
source: "video_generation",
childSessionKey: "video_generate:task-123",
childSessionId: "task-123",
announceType: "video generation task",
taskLabel: "lobster trailer",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 video.\nMEDIA:/tmp/lobster-trailer.mp4",
mediaUrls: ["/tmp/lobster-trailer.mp4"],
replyInstruction: "Deliver the generated video through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(callGateway).toHaveBeenCalledTimes(1);
expect(sendMessage).not.toHaveBeenCalled();
});
it("does not race pending announce delivery with direct generated media fallback", async () => {
const callGateway = createGatewayMock({
runId: "video_generate:task-123:ok",
status: "accepted",
acceptedAt: Date.now(),
});
const sendMessage = vi.fn(async () => {
throw new Error("temporary channel upload failure");
}) as unknown as typeof runtimeSendMessage;
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-media-pending-fallback-fails",
sourceTool: "video_generate",
internalEvents: [
{
type: "task_completion",
source: "video_generation",
childSessionKey: "video_generate:task-123",
childSessionId: "task-123",
announceType: "video generation task",
taskLabel: "lobster trailer",
status: "ok",
statusLabel: "completed successfully",
result: "Generated 1 video.\nMEDIA:/tmp/lobster-trailer.mp4",
mediaUrls: ["/tmp/lobster-trailer.mp4"],
replyInstruction: "Deliver the generated video through the message tool.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(callGateway).toHaveBeenCalledTimes(1);
expect(sendMessage).not.toHaveBeenCalled();
});
it("does not fail stale channel subagent completions only because the parent stayed private", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [],
},
});
const sendMessage = createSendMessageMock();
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeSequenceMock([
"transcript_commit_wait_unsupported",
"no_active_run",
]);
const result = await deliverSlackChannelAnnouncement({
callGateway,
sendMessage,
queueEmbeddedPiMessageWithOutcome,
sessionId: "requester-session-channel",
isActive: true,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-fallback-empty",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "channel completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(callGateway).toHaveBeenCalledTimes(1);
expect(sendMessage).not.toHaveBeenCalled();
});
it("keeps configured channel subagent completions on parent message-tool handoff", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "The subagent is done." }],
didSendViaMessagingTool: true,
messagingToolSentTexts: ["The subagent is done."],
},
});
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeMock(false);
const result = await deliverSlackChannelAnnouncement({
callGateway,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-subagent-message-tool",
sourceTool: "subagent_announce",
runtimeConfig: { messages: { groupChat: { visibleReplies: "message_tool" } } },
queueEmbeddedPiMessageWithOutcome,
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "channel completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: false,
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
threadId: undefined,
sourceReplyDeliveryMode: "message_tool_only",
});
});
it("fails configured channel subagent completions when parent skips required message tool", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "The subagent is done." }],
},
});
const queueEmbeddedPiMessageWithOutcome = createQueueOutcomeMock(false);
const result = await deliverSlackChannelAnnouncement({
callGateway,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-subagent-message-tool-missing",
sourceTool: "subagent_announce",
runtimeConfig: { messages: { groupChat: { visibleReplies: "message_tool" } } },
queueEmbeddedPiMessageWithOutcome,
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "channel completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: false,
path: "direct",
error: "completion agent did not use the message tool for message-tool-only delivery",
});
});
it("delivers Telegram forum-topic subagent completions through the normal parent handoff", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "The delegated task is complete." }],
},
});
const result = await deliverTelegramDirectMessageCompletion({
callGateway,
requesterSessionKey: "agent:main:telegram:group:-1003871627242:topic:6823",
origin: {
channel: "telegram",
to: "telegram:-1003871627242",
accountId: "bot-1",
threadId: 6823,
},
sourceTool: "subagent_announce",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:codex:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "telegram forum completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "delegated task output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expect(callGateway).toHaveBeenCalledTimes(1);
expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "telegram",
accountId: "bot-1",
to: "telegram:-1003871627242",
threadId: "6823",
});
});
it("keeps automatic final delivery for direct subagent completions", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "The subagent is done." }],
},
});
const result = await deliverDiscordDirectMessageCompletion({
callGateway,
sourceTool: "subagent_announce",
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "direct completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "discord",
accountId: "acct-1",
to: "dm:U123",
threadId: undefined,
});
});
it("falls back to the external requester route when completion origin is internal", async () => {
const callGateway = createGatewayMock({
result: {
payloads: [{ text: "child completion output" }],
},
});
const result = await deliverSlackChannelAnnouncement({
callGateway,
sessionId: "requester-session-channel",
isActive: false,
expectsCompletionMessage: true,
directIdempotencyKey: "announce-channel-internal-origin",
completionDirectOrigin: {
channel: "webchat",
},
internalEvents: [
{
type: "task_completion",
source: "subagent",
childSessionKey: "agent:worker:subagent:child",
childSessionId: "child-session-id",
announceType: "subagent task",
taskLabel: "channel completion smoke",
status: "ok",
statusLabel: "completed successfully",
result: "child completion output",
replyInstruction: "Summarize the result.",
},
],
});
expectRecordFields(result, {
delivered: true,
path: "direct",
});
expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
});
});
it("keeps direct external delivery for non-completion announces", async () => {
const callGateway = createGatewayMock();
await deliverSlackThreadAnnouncement({
callGateway,
sessionId: "requester-session-3",
isActive: false,
expectsCompletionMessage: false,
directIdempotencyKey: "announce-2",
});
expectGatewayAgentParams(callGateway, {
deliver: true,
channel: "slack",
accountId: "acct-1",
to: "channel:C123",
threadId: "171.222",
bestEffortDeliver: true,
});
});
});