test(channels): consolidate regression fixtures (#114409)

This commit is contained in:
Peter Steinberger
2026-08-09 08:47:20 -07:00
committed by GitHub
parent 7ea5f8fec6
commit e750dab6d1
5 changed files with 1596 additions and 1996 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -8,6 +8,15 @@ import {
createTestRegistry,
} from "../../test-utils/channel-plugins.js";
import { dispatchChannelMessageAction } from "./message-action-dispatch.js";
function dispatchTestChannelMessageAction(
overrides: Omit<Parameters<typeof dispatchChannelMessageAction>[0], "cfg">,
) {
return dispatchChannelMessageAction({
cfg: {} as OpenClawConfig,
...overrides,
});
}
import type { ChannelMessageActionContext, ChannelPlugin } from "./types.js";
const handleAction = vi.fn(async (_ctx: ChannelMessageActionContext) => jsonResult({ ok: true }));
@@ -46,10 +55,9 @@ describe("dispatchChannelMessageAction trusted sender guard", () => {
it("rejects privileged discord moderation action without trusted sender in tool context", async () => {
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "kick",
cfg: {} as OpenClawConfig,
params: { guildId: "g1", userId: "u1" },
toolContext: { currentChannelProvider: "discord" },
}),
@@ -58,10 +66,9 @@ describe("dispatchChannelMessageAction trusted sender guard", () => {
});
it("allows privileged discord moderation action with trusted sender in tool context", async () => {
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "discord",
action: "kick",
cfg: {} as OpenClawConfig,
params: { guildId: "g1", userId: "u1" },
requesterSenderId: "trusted-user",
toolContext: { currentChannelProvider: "discord" },
@@ -71,10 +78,9 @@ describe("dispatchChannelMessageAction trusted sender guard", () => {
});
it("does not require trusted sender without tool context", async () => {
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "discord",
action: "kick",
cfg: {} as OpenClawConfig,
params: { guildId: "g1", userId: "u1" },
});
@@ -150,10 +156,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
it("allows a non-bundled delegated read of the exact current conversation and account", async () => {
setReadPlugin();
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "channel:current" },
accountId: "Work",
requesterAccountId: "work",
@@ -176,10 +181,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: {
channelId: "other",
conversationReadOrigin: "direct-operator",
@@ -205,10 +209,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
it("rejects a non-core runtime action before any plugin callback", async () => {
setReadPlugin();
const result = await dispatchChannelMessageAction({
const result = await dispatchTestChannelMessageAction({
channel: "discord",
action: "forged-read" as never,
cfg: {} as OpenClawConfig,
params: { channelId: "other" },
conversationReadOrigin: "direct-operator",
});
@@ -222,10 +225,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
it("matches a sanitized channelId to a typed current-channel target", async () => {
setReadPlugin();
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target: "current",
channelId: "current",
@@ -291,10 +293,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: testCase.params,
accountId: testCase.accountId,
requesterAccountId: testCase.requesterAccountId,
@@ -314,10 +315,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
it("allows direct operators through a non-bundled adapter", async () => {
setReadPlugin();
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "other" },
conversationReadOrigin: "direct-operator",
});
@@ -329,10 +329,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "channel:123" },
accountId: "default",
requesterAccountId: "default",
@@ -374,10 +373,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "CHANNEL:CURRENT" },
accountId: "default",
requesterAccountId: "default",
@@ -395,10 +393,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "current" },
requesterAccountId: "work",
conversationReadOrigin: "delegated",
@@ -415,10 +412,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { target: "user:123" },
accountId: "default",
requesterAccountId: "default",
@@ -436,10 +432,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target: "channel:123",
channelId: "123",
@@ -461,10 +456,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target: "123",
},
@@ -485,10 +479,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin();
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: {
channelId: "current",
target: "channel:other",
@@ -508,10 +501,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
it("keeps non-read actions compatible on a non-bundled adapter", async () => {
setReadPlugin();
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "discord",
action: "send",
cfg: {} as OpenClawConfig,
params: { to: "other" },
conversationReadOrigin: "delegated",
});
@@ -522,10 +514,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
it("delegates configured-target policy to a bundled adapter", async () => {
setReadPlugin({ origin: "bundled" });
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "configured" },
conversationReadOrigin: "delegated",
});
@@ -572,10 +563,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin({ channel: "telegram", origin: "bundled" });
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "telegram",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "configured" },
accountId: "default",
requesterAccountId: "default",
@@ -594,10 +584,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
async (action) => {
setReadPlugin({ channel: "telegram", origin: "bundled" });
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "telegram",
action,
cfg: {} as OpenClawConfig,
params: { chatId: "-1001" },
accountId: "default",
requesterAccountId: "default",
@@ -617,10 +606,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin({ channel: "telegram", origin: "workspace" });
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "telegram",
action: "react",
cfg: {} as OpenClawConfig,
params: { chatId: "-1001" },
accountId: "default",
requesterAccountId: "default",
@@ -650,10 +638,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
normalizeTarget,
});
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "nextcloud-talk",
action: "read",
cfg: {} as OpenClawConfig,
params: { to: "nc:room:Current" },
accountId: "default",
requesterAccountId: "default",
@@ -677,10 +664,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "other" },
accountId: "default",
requesterAccountId: "default",
@@ -706,10 +692,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
normalizeTarget,
});
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "nextcloud-talk",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target,
to: "nextcloud-talk:current",
@@ -860,10 +845,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "nextcloud-talk",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target: "user:current",
to: "nextcloud-talk:current",
@@ -888,10 +872,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "nextcloud-talk",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target: "room:current",
},
@@ -915,10 +898,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "nextcloud-talk",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target: "group:current",
to: "nextcloud-talk:current",
@@ -949,10 +931,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "imessage",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target: "malformed-target",
to: "chat_guid:iMessage;+;current",
@@ -988,10 +969,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
},
});
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "imessage",
action: "read",
cfg: {} as OpenClawConfig,
params: { chatGuid: "iMessage;+;current" },
accountId: "default",
requesterAccountId: "default",
@@ -1021,10 +1001,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
},
});
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "whatsapp",
action: "react",
cfg: {} as OpenClawConfig,
params: {
chatJid: "current@g.us",
messageId: "current-message",
@@ -1058,10 +1037,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "whatsapp",
action: "react",
cfg: {} as OpenClawConfig,
params: {
chatJid: "sibling@g.us",
messageId: "sibling-message",
@@ -1103,10 +1081,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "whatsapp",
action: "react",
cfg: {} as OpenClawConfig,
params: { chatJid: "current@g.us", messageId: "current-message" },
accountId: testCase.accountId,
requesterAccountId: testCase.requesterAccountId,
@@ -1142,10 +1119,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
},
});
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "imessage",
action: "react",
cfg: {} as OpenClawConfig,
params: { chatId: 42, messageId: "current-message" },
accountId: "default",
requesterAccountId: "default",
@@ -1176,10 +1152,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
},
});
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "imessage",
action: "react",
cfg: {} as OpenClawConfig,
params: { chatId: 42, messageId: "current-message" },
accountId: "Work",
requesterAccountId: "work",
@@ -1219,10 +1194,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
const normalizedAliasTarget = "chat_id:42";
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "imessage",
action: "react",
cfg: {} as OpenClawConfig,
params: {
target: normalizedAliasTarget,
to: normalizedAliasTarget,
@@ -1259,10 +1233,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "imessage",
action: "react",
cfg: {} as OpenClawConfig,
params: { chatId: 42, messageId: "current-message" },
accountId: "default",
requesterAccountId: "default",
@@ -1294,10 +1267,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "imessage",
action: "react",
cfg: {} as OpenClawConfig,
params: { chatId: 42, messageId: "current-message" },
accountId: "default",
requesterAccountId: "default",
@@ -1329,10 +1301,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "imessage",
action: "react",
cfg: {} as OpenClawConfig,
params: {
target: "other-handle",
chatId: 42,
@@ -1368,10 +1339,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "imessage",
action: "read",
cfg: {} as OpenClawConfig,
params: {
to: "chat_guid:iMessage;+;current",
chatGuid: "iMessage;+;other",
@@ -1407,10 +1377,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "imessage",
action: testCase.action,
cfg: {} as OpenClawConfig,
params: testCase.params,
accountId: "work",
requesterAccountId: "work",
@@ -1437,10 +1406,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "imessage",
action: "read",
cfg: {} as OpenClawConfig,
params: {
target: "chat_guid:iMessage;+;other",
messageId: "current-message",
@@ -1471,10 +1439,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "imessage",
action: "read",
cfg: {} as OpenClawConfig,
params: { messageId: "current-message" },
accountId: "default",
requesterAccountId: "default",
@@ -1492,10 +1459,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
it("allows bundled targetless sticker-cache reads only in matching current context", async () => {
setReadPlugin({ channel: "telegram", origin: "bundled" });
await dispatchChannelMessageAction({
await dispatchTestChannelMessageAction({
channel: "telegram",
action: "sticker-search",
cfg: {} as OpenClawConfig,
params: { query: "party", limit: 5 },
accountId: "work",
requesterAccountId: "work",
@@ -1540,10 +1506,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin({ channel: "telegram", origin: "bundled" });
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "telegram",
action: "sticker-search",
cfg: {} as OpenClawConfig,
params: { query: "party", limit: 5 },
accountId: testCase.accountId,
requesterAccountId: testCase.requesterAccountId,
@@ -1564,10 +1529,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
});
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "configured" },
accountId: "default",
requesterAccountId: "default",
@@ -1587,10 +1551,9 @@ describe("dispatchChannelMessageAction conversation-read provenance", () => {
setReadPlugin(origin ? { origin } : undefined);
await expect(
dispatchChannelMessageAction({
dispatchTestChannelMessageAction({
channel: "discord",
action: "read",
cfg: {} as OpenClawConfig,
params: { channelId: "configured" },
accountId: "default",
requesterAccountId: "default",
+48 -136
View File
@@ -5,6 +5,20 @@ import {
createChannelProgressReceiptTracker,
PROGRESS_STATUS_PREAMBLE_FRESH_MS,
} from "./progress-draft-compositor.js";
function createTestProgressDraftCompositor(
overrides: Omit<
Parameters<typeof createChannelProgressDraftCompositor>[0],
"mode" | "active" | "seed"
>,
) {
return createChannelProgressDraftCompositor({
mode: "progress",
active: true,
seed: "test",
...overrides,
});
}
import { DEFAULT_PROGRESS_DRAFT_INITIAL_DELAY_MS } from "./streaming.js";
describe("createChannelProgressDraftCompositor", () => {
@@ -28,11 +42,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("starts immediately for plans, replaces snapshots, and clears them on reset", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: false } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -78,11 +89,8 @@ describe("createChannelProgressDraftCompositor", () => {
});
it("returns detached structured state for channel-native renderers", async () => {
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: false } } },
mode: "progress",
active: true,
seed: "test",
update: vi.fn(),
});
@@ -135,13 +143,10 @@ describe("createChannelProgressDraftCompositor", () => {
it("keeps the progress label visible when tool lines are hidden", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: {
streaming: { mode: "progress", progress: { label: "Shelling", toolProgress: false } },
},
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -153,13 +158,10 @@ describe("createChannelProgressDraftCompositor", () => {
it("gates window thinking on its own flag, independent of tool progress", async () => {
// thinking: false hides thoughts even though toolProgress stays on…
const hiddenUpdate = vi.fn();
const hidden = createChannelProgressDraftCompositor({
const hidden = createTestProgressDraftCompositor({
entry: {
streaming: { mode: "progress", progress: { label: "Shelling" } },
},
mode: "progress",
active: true,
seed: "test",
reasoningGate: false,
update: hiddenUpdate,
});
@@ -168,16 +170,13 @@ describe("createChannelProgressDraftCompositor", () => {
expect(hiddenUpdate.mock.calls.every(([text]) => !String(text).includes("Reading"))).toBe(true);
const defaultUpdate = vi.fn();
const sharedDefault = createChannelProgressDraftCompositor({
const sharedDefault = createTestProgressDraftCompositor({
entry: {
streaming: {
mode: "progress",
progress: { label: "Shelling", toolProgress: false },
},
},
mode: "progress",
active: true,
seed: "test",
update: defaultUpdate,
});
await sharedDefault.pushToolProgress("🛠️ Exec", { startImmediately: true });
@@ -187,16 +186,13 @@ describe("createChannelProgressDraftCompositor", () => {
);
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: {
streaming: {
mode: "progress",
progress: { label: "Shelling", toolProgress: false },
},
},
mode: "progress",
active: true,
seed: "test",
reasoningLinePrefix: "🧠 ",
reasoningGate: true,
update,
@@ -225,11 +221,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("re-arms the draft for a queued turn after the primary final settled", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -249,11 +242,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("force-rearms an authoritative queued boundary without a prior final", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -269,11 +259,8 @@ describe("createChannelProgressDraftCompositor", () => {
vi.useFakeTimers();
try {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -290,11 +277,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("does not resurrect progress after suppression", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -306,11 +290,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("composes reasoning deltas with tool progress", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
reasoningLinePrefix: "🧠 ",
update,
});
@@ -326,11 +307,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("labels window narration with a 💬 prefix", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling", commentary: true } } },
mode: "progress",
active: true,
seed: "test",
commentaryLinePrefix: "💬 ",
update,
});
@@ -489,13 +467,10 @@ describe("createChannelProgressDraftCompositor", () => {
it("interleaves reasoning bursts with tool calls in arrival order", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: {
streaming: { mode: "progress", progress: { label: "Shelling", maxLines: 8 } },
},
mode: "progress",
active: true,
seed: "test",
reasoningLinePrefix: "🧠 ",
update,
});
@@ -517,11 +492,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("preserves tagged reasoning content without leaking tags", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
reasoningLinePrefix: "🧠 ",
update,
});
@@ -536,11 +508,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("waits for complete reasoning tags before showing tagged progress", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -553,11 +522,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("preserves partial reasoning tag buffers across deltas", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
reasoningLinePrefix: "🧠 ",
update,
});
@@ -573,11 +539,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("keeps literal reasoning tags inside code blocks", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
reasoningLinePrefix: "🧠 ",
update,
});
@@ -595,11 +558,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("replaces repeated formatted reasoning snapshots", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
reasoningLinePrefix: "🧠 ",
update,
});
@@ -652,13 +612,10 @@ describe("createChannelProgressDraftCompositor", () => {
it("hands preambles to the commentary lane when it is enabled", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: {
streaming: { mode: "progress", progress: { label: "Shelling", commentary: true } },
},
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -670,11 +627,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("holds a preamble headline until the gate starts and hides the implicit label", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress" } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -690,11 +644,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("publishes rolling tool-line changes beneath a stable preamble headline", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { maxLines: 8 } } },
mode: "progress",
active: true,
seed: "test",
updateOnLineChange: true,
update,
});
@@ -711,11 +662,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("rejects control-only preambles without clobbering a valid headline", async () => {
let nowMs = 0;
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress" } },
mode: "progress",
active: true,
seed: "test",
now: () => nowMs,
update,
});
@@ -750,11 +698,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("retracts only the matching preamble headline", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -775,11 +720,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("keeps a fresh preamble ahead of later narration", async () => {
let nowMs = 0;
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
now: () => nowMs,
update,
});
@@ -798,11 +740,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("uses newer narration after the preamble becomes stale", async () => {
let nowMs = 0;
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
now: () => nowMs,
update,
});
@@ -821,11 +760,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("uses a plan explanation after the preamble becomes stale", async () => {
let nowMs = 0;
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
now: () => nowMs,
update,
});
@@ -846,11 +782,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("refreshes a new preamble item when its text matches the stale item", async () => {
let nowMs = 0;
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
now: () => nowMs,
update,
});
@@ -871,11 +804,8 @@ describe("createChannelProgressDraftCompositor", () => {
vi.useFakeTimers();
try {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -901,11 +831,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("cancels a pending preamble-expiry refresh when the final starts", async () => {
vi.useFakeTimers();
try {
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress" } },
mode: "progress",
active: true,
seed: "test",
update: vi.fn(),
});
@@ -924,11 +851,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("returns to the retained preamble when narration clears", async () => {
let nowMs = 0;
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
now: () => nowMs,
update,
});
@@ -948,11 +872,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("clears both status sources on reset", async () => {
let nowMs = 0;
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
now: () => nowMs,
update,
});
@@ -971,11 +892,8 @@ describe("createChannelProgressDraftCompositor", () => {
vi.useFakeTimers();
try {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress" } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -1049,11 +967,8 @@ describe("createChannelProgressDraftCompositor", () => {
it("ignores status updates once the final reply started and clears both per turn", async () => {
const update = vi.fn();
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
@@ -1079,11 +994,8 @@ describe("createChannelProgressDraftCompositor", () => {
try {
const error = new Error("send failed");
const update = vi.fn().mockRejectedValue(error);
const progress = createChannelProgressDraftCompositor({
const progress = createTestProgressDraftCompositor({
entry: { streaming: { mode: "progress", progress: { label: "Shelling" } } },
mode: "progress",
active: true,
seed: "test",
update,
});
+38 -96
View File
@@ -141,6 +141,20 @@ function createDispatch(
}) as DispatchReplyWithBufferedBlockDispatcher;
}
function dispatchTestAssembledTurn(
overrides: Omit<
Parameters<typeof dispatchAssembledChannelTurn>[0],
"cfg" | "agentId" | "storePath"
>,
) {
return dispatchAssembledChannelTurn({
cfg,
agentId: "main",
storePath: "/tmp/sessions.json",
...overrides,
});
}
function requireFirstMockCall<T>(mock: { mock: { calls: T[][] } }, label: string): T[] {
const call = mock.mock.calls[0];
if (!call) {
@@ -612,13 +626,10 @@ describe("channel turn kernel", () => {
const recordInboundSession = createRecordInboundSession();
const dispatchReplyWithBufferedBlockDispatcher = createDispatch();
const result = await dispatchAssembledChannelTurn({
cfg,
const result = await dispatchTestAssembledTurn({
channel: "telegram",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:telegram:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({
To: "123",
OriginatingTo: "123",
@@ -677,13 +688,10 @@ describe("channel turn kernel", () => {
},
) as DispatchReplyWithBufferedBlockDispatcher;
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "telegram",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:telegram:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ To: "123", OriginatingTo: "123" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -749,13 +757,10 @@ describe("channel turn kernel", () => {
const onDelivered = vi.fn();
const dispatchReplyWithBufferedBlockDispatcher = createDispatch();
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "tlon",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:tlon:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ To: "chat/~nec/general", OriginatingTo: "chat/~nec/general" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -810,13 +815,10 @@ describe("channel turn kernel", () => {
},
) as DispatchReplyWithBufferedBlockDispatcher;
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "telegram",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:telegram:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ To: "123", OriginatingTo: "123" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -843,13 +845,10 @@ describe("channel turn kernel", () => {
const dispatchReplyWithBufferedBlockDispatcher = createDispatch();
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "telegram",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:telegram:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ To: "123", OriginatingTo: "123" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -879,13 +878,10 @@ describe("channel turn kernel", () => {
const dispatchReplyWithBufferedBlockDispatcher = createDispatch();
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "telegram",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:telegram:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ To: "123", OriginatingTo: "123" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -905,13 +901,10 @@ describe("channel turn kernel", () => {
const dispatchReplyWithBufferedBlockDispatcher = createDispatch();
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "telegram",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:telegram:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ To: "123", OriginatingTo: "123" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -948,12 +941,9 @@ describe("channel turn kernel", () => {
},
) as DispatchReplyWithBufferedBlockDispatcher;
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "test",
agentId: "main",
routeSessionKey: "agent:main:test:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx(),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1002,13 +992,10 @@ describe("channel turn kernel", () => {
return { queuedFinal: true, counts: { tool: 0, block: 0, final: 1 } };
}) as DispatchReplyWithBufferedBlockDispatcher;
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "feishu",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:feishu:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ Surface: "feishu", Provider: "feishu", OriginatingTo: "oc_chat" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1046,12 +1033,9 @@ describe("channel turn kernel", () => {
return { queuedFinal: true, counts: { tool: 0, block: 0, final: 1 } };
}) as DispatchReplyWithBufferedBlockDispatcher;
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "feishu",
agentId: "main",
routeSessionKey: "agent:main:feishu:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ Surface: "feishu", Provider: "feishu" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1075,12 +1059,9 @@ describe("channel turn kernel", () => {
const onError = vi.fn();
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "feishu",
agentId: "main",
routeSessionKey: "agent:main:feishu:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ Surface: "feishu", Provider: "feishu" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher: createDispatch(),
@@ -1137,12 +1118,9 @@ describe("channel turn kernel", () => {
}) as DispatchReplyWithBufferedBlockDispatcher;
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "feishu",
agentId: "main",
routeSessionKey: "agent:main:feishu:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ Surface: "feishu", Provider: "feishu" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1190,12 +1168,9 @@ describe("channel turn kernel", () => {
}) as DispatchReplyWithBufferedBlockDispatcher;
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "feishu",
agentId: "main",
routeSessionKey: "agent:main:feishu:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ Surface: "feishu", Provider: "feishu" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1246,12 +1221,9 @@ describe("channel turn kernel", () => {
}) as DispatchReplyWithBufferedBlockDispatcher;
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "feishu",
agentId: "main",
routeSessionKey: "agent:main:feishu:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ Surface: "feishu", Provider: "feishu" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1284,12 +1256,9 @@ describe("channel turn kernel", () => {
return { queuedFinal: false, counts: { tool: 0, block: 0, final: 0 } };
}) as DispatchReplyWithBufferedBlockDispatcher;
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "feishu",
agentId: "main",
routeSessionKey: "agent:main:feishu:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ Surface: "feishu", Provider: "feishu" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1310,13 +1279,10 @@ describe("channel turn kernel", () => {
const deliver = vi.fn(async () => ({ messageIds: ["local-1"], visibleReplySent: true }));
const dispatchReplyWithBufferedBlockDispatcher = createDispatch();
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "telegram",
accountId: "acct",
agentId: "main",
routeSessionKey: "agent:main:telegram:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({ To: "123", OriginatingTo: "123" }),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1332,12 +1298,9 @@ describe("channel turn kernel", () => {
const deliver = vi.fn(async () => ({ messageIds: ["local-1"], visibleReplySent: true }));
const dispatchReplyWithBufferedBlockDispatcher = createDispatch();
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "test",
agentId: "main",
routeSessionKey: "agent:main:test:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx(),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1377,12 +1340,9 @@ describe("channel turn kernel", () => {
},
) as DispatchReplyWithBufferedBlockDispatcher;
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "test",
agentId: "main",
routeSessionKey: "agent:main:test:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx(),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher,
@@ -1402,12 +1362,9 @@ describe("channel turn kernel", () => {
const recordInboundSession = createRecordInboundSession(events);
const dispatchReplyWithBufferedBlockDispatcher = createDispatch(events);
const result = await dispatchAssembledChannelTurn({
cfg,
const result = await dispatchTestAssembledTurn({
channel: "test",
agentId: "main",
routeSessionKey: "agent:main:test:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx(),
recordInboundSession,
dispatchReplyWithBufferedBlockDispatcher,
@@ -1435,12 +1392,9 @@ describe("channel turn kernel", () => {
const commandSessionKey = "agent:main:command:telegram:42";
const targetSessionKey = "agent:main:telegram:group:42:topic:7";
const result = await dispatchAssembledChannelTurn({
cfg,
const result = await dispatchTestAssembledTurn({
channel: "telegram",
agentId: "main",
routeSessionKey: commandSessionKey,
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({
AgentId: "main",
SessionKey: commandSessionKey,
@@ -1486,12 +1440,9 @@ describe("channel turn kernel", () => {
const dispatchReplyWithBufferedBlockDispatcher = createDispatch();
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "telegram",
agentId: "main",
routeSessionKey: "agent:main:command:telegram:42",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx(),
recordInboundSession,
dispatchReplyWithBufferedBlockDispatcher,
@@ -1505,12 +1456,9 @@ describe("channel turn kernel", () => {
it("rejects surrounding whitespace in an explicit record session", async () => {
await expect(
dispatchAssembledChannelTurn({
cfg,
dispatchTestAssembledTurn({
channel: "telegram",
agentId: "main",
routeSessionKey: "agent:main:command:telegram:42",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx(),
recordInboundSession: createRecordInboundSession(),
dispatchReplyWithBufferedBlockDispatcher: createDispatch(),
@@ -1865,13 +1813,10 @@ describe("channel turn kernel", () => {
});
// Unbinding removes Discord's thread route, not the core-owned outbound identity.
const result = await dispatchAssembledChannelTurn({
cfg,
const result = await dispatchTestAssembledTurn({
channel: "discord",
accountId: "default",
agentId: "main",
routeSessionKey: "agent:main:discord:channel:thread-1",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx({
Provider: "discord",
Surface: "discord",
@@ -2086,12 +2031,9 @@ describe("channel turn kernel", () => {
},
) as DispatchReplyWithBufferedBlockDispatcher;
await dispatchAssembledChannelTurn({
cfg,
await dispatchTestAssembledTurn({
channel: "test",
agentId: "main",
routeSessionKey: "agent:main:test:peer",
storePath: "/tmp/sessions.json",
ctxPayload: createCtx(),
recordInboundSession: createRecordInboundSession(events),
dispatchReplyWithBufferedBlockDispatcher,