test(agents): dedupe subagent announce direct-send variants

This commit is contained in:
Peter Steinberger
2026-02-22 08:49:33 +00:00
parent cfb3cee7aa
commit a1c8525766

View File

@@ -595,78 +595,77 @@ describe("subagent announce formatting", () => {
expect(directTargets).not.toContain("channel:main-parent-channel"); expect(directTargets).not.toContain("channel:main-parent-channel");
}); });
it("uses failure header for completion direct-send when subagent outcome is error", async () => { it.each([
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js"); {
sessionStore = { name: "error",
"agent:main:subagent:test": { childSessionId: "child-session-direct-error",
sessionId: "child-session-direct-error", requesterSessionId: "requester-session-error",
},
"agent:main:main": {
sessionId: "requester-session-error",
},
};
chatHistoryMock.mockResolvedValueOnce({
messages: [{ role: "assistant", content: [{ type: "text", text: "boom details" }] }],
});
readLatestAssistantReplyMock.mockResolvedValue("");
const didAnnounce = await runSubagentAnnounceFlow({
childSessionKey: "agent:main:subagent:test",
childRunId: "run-direct-completion-error", childRunId: "run-direct-completion-error",
requesterSessionKey: "agent:main:main", replyText: "boom details",
requesterDisplayKey: "main", outcome: { status: "error", error: "boom" } as const,
requesterOrigin: { channel: "discord", to: "channel:12345", accountId: "acct-1" }, expectedHeader: "❌ Subagent main failed this task (session remains active)",
...defaultOutcomeAnnounce, excludedHeader: "✅ Subagent main",
outcome: { status: "error", error: "boom" }, spawnMode: "session" as const,
expectsCompletionMessage: true, },
spawnMode: "session", {
}); name: "timeout",
childSessionId: "child-session-direct-timeout",
expect(didAnnounce).toBe(true); requesterSessionId: "requester-session-timeout",
expect(sendSpy).toHaveBeenCalledTimes(1);
const call = sendSpy.mock.calls[0]?.[0] as { params?: Record<string, unknown> };
const rawMessage = call?.params?.message;
const msg = typeof rawMessage === "string" ? rawMessage : "";
expect(msg).toContain("❌ Subagent main failed this task (session remains active)");
expect(msg).toContain("boom details");
expect(msg).not.toContain("✅ Subagent main");
});
it("uses timeout header for completion direct-send when subagent outcome timed out", async () => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
sessionStore = {
"agent:main:subagent:test": {
sessionId: "child-session-direct-timeout",
},
"agent:main:main": {
sessionId: "requester-session-timeout",
},
};
chatHistoryMock.mockResolvedValueOnce({
messages: [{ role: "assistant", content: [{ type: "text", text: "partial output" }] }],
});
readLatestAssistantReplyMock.mockResolvedValue("");
const didAnnounce = await runSubagentAnnounceFlow({
childSessionKey: "agent:main:subagent:test",
childRunId: "run-direct-completion-timeout", childRunId: "run-direct-completion-timeout",
requesterSessionKey: "agent:main:main", replyText: "partial output",
requesterDisplayKey: "main", outcome: { status: "timeout" } as const,
requesterOrigin: { channel: "discord", to: "channel:12345", accountId: "acct-1" }, expectedHeader: "⏱️ Subagent main timed out",
...defaultOutcomeAnnounce, excludedHeader: "✅ Subagent main finished",
outcome: { status: "timeout" }, spawnMode: undefined,
expectsCompletionMessage: true, },
}); ])(
"uses completion direct-send header for $name outcomes",
async ({
childSessionId,
requesterSessionId,
childRunId,
replyText,
outcome,
expectedHeader,
excludedHeader,
spawnMode,
}) => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
sessionStore = {
"agent:main:subagent:test": {
sessionId: childSessionId,
},
"agent:main:main": {
sessionId: requesterSessionId,
},
};
chatHistoryMock.mockResolvedValueOnce({
messages: [{ role: "assistant", content: [{ type: "text", text: replyText }] }],
});
readLatestAssistantReplyMock.mockResolvedValue("");
expect(didAnnounce).toBe(true); const didAnnounce = await runSubagentAnnounceFlow({
expect(sendSpy).toHaveBeenCalledTimes(1); childSessionKey: "agent:main:subagent:test",
const call = sendSpy.mock.calls[0]?.[0] as { params?: Record<string, unknown> }; childRunId,
const rawMessage = call?.params?.message; requesterSessionKey: "agent:main:main",
const msg = typeof rawMessage === "string" ? rawMessage : ""; requesterDisplayKey: "main",
expect(msg).toContain("⏱️ Subagent main timed out"); requesterOrigin: { channel: "discord", to: "channel:12345", accountId: "acct-1" },
expect(msg).toContain("partial output"); ...defaultOutcomeAnnounce,
expect(msg).not.toContain("✅ Subagent main finished"); outcome,
}); expectsCompletionMessage: true,
...(spawnMode ? { spawnMode } : {}),
});
expect(didAnnounce).toBe(true);
expect(sendSpy).toHaveBeenCalledTimes(1);
const call = sendSpy.mock.calls[0]?.[0] as { params?: Record<string, unknown> };
const rawMessage = call?.params?.message;
const msg = typeof rawMessage === "string" ? rawMessage : "";
expect(msg).toContain(expectedHeader);
expect(msg).toContain(replyText);
expect(msg).not.toContain(excludedHeader);
},
);
it("ignores stale session thread hints for manual completion direct-send", async () => { it("ignores stale session thread hints for manual completion direct-send", async () => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js"); const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
@@ -801,125 +800,44 @@ describe("subagent announce formatting", () => {
expect(message).not.toContain("finished"); expect(message).not.toContain("finished");
}); });
it("uses hook-provided thread target when requester origin has no threadId", async () => { it.each([
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js"); {
hasSubagentDeliveryTargetHook = true; name: "requester origin has no threadId",
subagentDeliveryTargetHookMock.mockResolvedValueOnce({
origin: {
channel: "discord",
accountId: "acct-1",
to: "channel:777",
threadId: "777",
},
});
const didAnnounce = await runSubagentAnnounceFlow({
childSessionKey: "agent:main:subagent:test",
childRunId: "run-direct-thread-bound-single", childRunId: "run-direct-thread-bound-single",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
requesterOrigin: { requesterOrigin: {
channel: "discord", channel: "discord",
to: "channel:12345", to: "channel:12345",
accountId: "acct-1", accountId: "acct-1",
}, },
...defaultOutcomeAnnounce, },
expectsCompletionMessage: true, {
spawnMode: "session", name: "requester threadId does not match",
});
expect(didAnnounce).toBe(true);
expect(sendSpy).toHaveBeenCalledTimes(1);
const call = sendSpy.mock.calls[0]?.[0] as { params?: Record<string, unknown> };
expect(call?.params?.channel).toBe("discord");
expect(call?.params?.to).toBe("channel:777");
expect(call?.params?.threadId).toBe("777");
});
it("keeps requester origin when delivery-target hook returns no override", async () => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
hasSubagentDeliveryTargetHook = true;
subagentDeliveryTargetHookMock.mockResolvedValueOnce(undefined);
const didAnnounce = await runSubagentAnnounceFlow({
childSessionKey: "agent:main:subagent:test",
childRunId: "run-direct-thread-persisted",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
requesterOrigin: {
channel: "discord",
to: "channel:12345",
accountId: "acct-1",
},
...defaultOutcomeAnnounce,
expectsCompletionMessage: true,
spawnMode: "session",
});
expect(didAnnounce).toBe(true);
expect(sendSpy).toHaveBeenCalledTimes(1);
const call = sendSpy.mock.calls[0]?.[0] as { params?: Record<string, unknown> };
expect(call?.params?.channel).toBe("discord");
expect(call?.params?.to).toBe("channel:12345");
expect(call?.params?.threadId).toBeUndefined();
});
it("keeps requester origin when delivery-target hook returns non-deliverable channel", async () => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
hasSubagentDeliveryTargetHook = true;
subagentDeliveryTargetHookMock.mockResolvedValueOnce({
origin: {
channel: "webchat",
to: "conversation:123",
},
});
const didAnnounce = await runSubagentAnnounceFlow({
childSessionKey: "agent:main:subagent:test",
childRunId: "run-direct-thread-multi-no-origin",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
requesterOrigin: {
channel: "discord",
to: "channel:12345",
accountId: "acct-1",
},
...defaultOutcomeAnnounce,
expectsCompletionMessage: true,
spawnMode: "session",
});
expect(didAnnounce).toBe(true);
expect(sendSpy).toHaveBeenCalledTimes(1);
const call = sendSpy.mock.calls[0]?.[0] as { params?: Record<string, unknown> };
expect(call?.params?.channel).toBe("discord");
expect(call?.params?.to).toBe("channel:12345");
expect(call?.params?.threadId).toBeUndefined();
});
it("uses hook-provided thread target when requester threadId does not match", async () => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
hasSubagentDeliveryTargetHook = true;
subagentDeliveryTargetHookMock.mockResolvedValueOnce({
origin: {
channel: "discord",
accountId: "acct-1",
to: "channel:777",
threadId: "777",
},
});
const didAnnounce = await runSubagentAnnounceFlow({
childSessionKey: "agent:main:subagent:test",
childRunId: "run-direct-thread-no-match", childRunId: "run-direct-thread-no-match",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
requesterOrigin: { requesterOrigin: {
channel: "discord", channel: "discord",
to: "channel:12345", to: "channel:12345",
accountId: "acct-1", accountId: "acct-1",
threadId: "999", threadId: "999",
}, },
},
])("uses hook-provided thread target when $name", async ({ childRunId, requesterOrigin }) => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
hasSubagentDeliveryTargetHook = true;
subagentDeliveryTargetHookMock.mockResolvedValueOnce({
origin: {
channel: "discord",
accountId: "acct-1",
to: "channel:777",
threadId: "777",
},
});
const didAnnounce = await runSubagentAnnounceFlow({
childSessionKey: "agent:main:subagent:test",
childRunId,
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
requesterOrigin,
...defaultOutcomeAnnounce, ...defaultOutcomeAnnounce,
expectsCompletionMessage: true, expectsCompletionMessage: true,
spawnMode: "session", spawnMode: "session",
@@ -933,6 +851,50 @@ describe("subagent announce formatting", () => {
expect(call?.params?.threadId).toBe("777"); expect(call?.params?.threadId).toBe("777");
}); });
it.each([
{
name: "delivery-target hook returns no override",
childRunId: "run-direct-thread-persisted",
hookResult: undefined,
},
{
name: "delivery-target hook returns non-deliverable channel",
childRunId: "run-direct-thread-multi-no-origin",
hookResult: {
origin: {
channel: "webchat",
to: "conversation:123",
},
},
},
])("keeps requester origin when $name", async ({ childRunId, hookResult }) => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
hasSubagentDeliveryTargetHook = true;
subagentDeliveryTargetHookMock.mockResolvedValueOnce(hookResult);
const didAnnounce = await runSubagentAnnounceFlow({
childSessionKey: "agent:main:subagent:test",
childRunId,
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
requesterOrigin: {
channel: "discord",
to: "channel:12345",
accountId: "acct-1",
},
...defaultOutcomeAnnounce,
expectsCompletionMessage: true,
spawnMode: "session",
});
expect(didAnnounce).toBe(true);
expect(sendSpy).toHaveBeenCalledTimes(1);
const call = sendSpy.mock.calls[0]?.[0] as { params?: Record<string, unknown> };
expect(call?.params?.channel).toBe("discord");
expect(call?.params?.to).toBe("channel:12345");
expect(call?.params?.threadId).toBeUndefined();
});
it("steers announcements into an active run when queue mode is steer", async () => { it("steers announcements into an active run when queue mode is steer", async () => {
const { runSubagentAnnounceFlow } = await import("./subagent-announce.js"); const { runSubagentAnnounceFlow } = await import("./subagent-announce.js");
embeddedRunMock.isEmbeddedPiRunActive.mockReturnValue(true); embeddedRunMock.isEmbeddedPiRunActive.mockReturnValue(true);