diff --git a/src/infra/outbound/current-conversation-bindings.test.ts b/src/infra/outbound/current-conversation-bindings.test.ts index f4ddfb0f638..7a4f2d5e230 100644 --- a/src/infra/outbound/current-conversation-bindings.test.ts +++ b/src/infra/outbound/current-conversation-bindings.test.ts @@ -18,10 +18,10 @@ function setMinimalCurrentConversationRegistry(): void { setActivePluginRegistry( createTestRegistry([ { - pluginId: "slack", + pluginId: "workspace", source: "test", plugin: { - id: "slack", + id: "workspace", meta: { aliases: [] }, conversationBindings: { supportsCurrentConversationBinding: true, @@ -61,7 +61,7 @@ describe("generic current-conversation bindings", () => { it("advertises support only for channels that opt into current-conversation binds", () => { expect( getGenericCurrentConversationBindingCapabilities({ - channel: "slack", + channel: "workspace", accountId: "default", }), ).toEqual({ @@ -83,7 +83,7 @@ describe("generic current-conversation bindings", () => { expect( getGenericCurrentConversationBindingCapabilities({ - channel: "slack", + channel: "workspace", accountId: "default", }), ).toBeNull(); @@ -91,36 +91,36 @@ describe("generic current-conversation bindings", () => { it("reloads persisted bindings after the in-memory cache is cleared", async () => { const bound = await bindGenericCurrentConversation({ - targetSessionKey: "agent:codex:acp:slack-dm", + targetSessionKey: "agent:codex:acp:workspace-dm", targetKind: "session", conversation: { - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", }, metadata: { - label: "slack-dm", + label: "workspace-dm", }, }); expect(bound).toMatchObject({ - bindingId: "generic:slack\u241fdefault\u241f\u241fuser:U123", - targetSessionKey: "agent:codex:acp:slack-dm", + bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123", + targetSessionKey: "agent:codex:acp:workspace-dm", }); __testing.resetCurrentConversationBindingsForTests(); expect( resolveGenericCurrentConversationBinding({ - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", }), ).toMatchObject({ - bindingId: "generic:slack\u241fdefault\u241f\u241fuser:U123", - targetSessionKey: "agent:codex:acp:slack-dm", + bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123", + targetSessionKey: "agent:codex:acp:workspace-dm", metadata: expect.objectContaining({ - label: "slack-dm", + label: "workspace-dm", }), }); }); @@ -134,18 +134,18 @@ describe("generic current-conversation bindings", () => { version: 1, bindings: [ { - bindingId: "generic:slack\u241fdefault\u241f\u241fuser:U123", - targetSessionKey: " agent:codex:acp:slack-dm ", + bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123", + targetSessionKey: " agent:codex:acp:workspace-dm ", targetKind: "session", conversation: { - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", }, status: "active", boundAt: 1234, metadata: { - label: "slack-dm", + label: "workspace-dm", }, }, ], @@ -153,32 +153,34 @@ describe("generic current-conversation bindings", () => { ); const resolved = resolveGenericCurrentConversationBinding({ - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", }); expect(resolved).toMatchObject({ - bindingId: "generic:slack\u241fdefault\u241f\u241fuser:U123", - targetSessionKey: "agent:codex:acp:slack-dm", + bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123", + targetSessionKey: "agent:codex:acp:workspace-dm", metadata: expect.objectContaining({ - label: "slack-dm", + label: "workspace-dm", }), }); - expect(listGenericCurrentConversationBindingsBySession("agent:codex:acp:slack-dm")).toEqual([ - expect.objectContaining({ - bindingId: "generic:slack\u241fdefault\u241f\u241fuser:U123", - targetSessionKey: "agent:codex:acp:slack-dm", - }), - ]); + expect(listGenericCurrentConversationBindingsBySession("agent:codex:acp:workspace-dm")).toEqual( + [ + expect.objectContaining({ + bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123", + targetSessionKey: "agent:codex:acp:workspace-dm", + }), + ], + ); }); it("drops self-parent conversation refs when storing generic current bindings", async () => { const bound = await bindGenericCurrentConversation({ - targetSessionKey: "agent:codex:acp:telegram-dm", + targetSessionKey: "agent:codex:acp:forum-dm", targetKind: "session", conversation: { - channel: "telegram", + channel: "forum", accountId: "default", conversationId: "6098642967", parentConversationId: "6098642967", @@ -186,9 +188,9 @@ describe("generic current-conversation bindings", () => { }); expect(bound).toMatchObject({ - bindingId: "generic:telegram\u241fdefault\u241f\u241f6098642967", + bindingId: "generic:forum\u241fdefault\u241f\u241f6098642967", conversation: { - channel: "telegram", + channel: "forum", accountId: "default", conversationId: "6098642967", }, @@ -196,13 +198,13 @@ describe("generic current-conversation bindings", () => { expect(bound?.conversation.parentConversationId).toBeUndefined(); expect( resolveGenericCurrentConversationBinding({ - channel: "telegram", + channel: "forum", accountId: "default", conversationId: "6098642967", }), ).toMatchObject({ - bindingId: "generic:telegram\u241fdefault\u241f\u241f6098642967", - targetSessionKey: "agent:codex:acp:telegram-dm", + bindingId: "generic:forum\u241fdefault\u241f\u241f6098642967", + targetSessionKey: "agent:codex:acp:forum-dm", }); }); @@ -215,11 +217,11 @@ describe("generic current-conversation bindings", () => { version: 1, bindings: [ { - bindingId: "generic:telegram\u241fdefault\u241f6098642967\u241f6098642967", - targetSessionKey: "agent:codex:acp:telegram-dm", + bindingId: "generic:forum\u241fdefault\u241f6098642967\u241f6098642967", + targetSessionKey: "agent:codex:acp:forum-dm", targetKind: "session", conversation: { - channel: "telegram", + channel: "forum", accountId: "default", conversationId: "6098642967", parentConversationId: "6098642967", @@ -227,7 +229,7 @@ describe("generic current-conversation bindings", () => { status: "active", boundAt: 1234, metadata: { - label: "telegram-dm", + label: "forum-dm", }, }, ], @@ -235,16 +237,16 @@ describe("generic current-conversation bindings", () => { ); const resolved = resolveGenericCurrentConversationBinding({ - channel: "telegram", + channel: "forum", accountId: "default", conversationId: "6098642967", }); expect(resolved).toMatchObject({ - bindingId: "generic:telegram\u241fdefault\u241f\u241f6098642967", - targetSessionKey: "agent:codex:acp:telegram-dm", + bindingId: "generic:forum\u241fdefault\u241f\u241f6098642967", + targetSessionKey: "agent:codex:acp:forum-dm", conversation: { - channel: "telegram", + channel: "forum", accountId: "default", conversationId: "6098642967", }, @@ -258,14 +260,14 @@ describe("generic current-conversation bindings", () => { }), ).resolves.toEqual([ expect.objectContaining({ - bindingId: "generic:telegram\u241fdefault\u241f\u241f6098642967", + bindingId: "generic:forum\u241fdefault\u241f\u241f6098642967", }), ]); __testing.resetCurrentConversationBindingsForTests(); expect( resolveGenericCurrentConversationBinding({ - channel: "telegram", + channel: "forum", accountId: "default", conversationId: "6098642967", }), @@ -301,22 +303,22 @@ describe("generic current-conversation bindings", () => { it("persists touched activity across reloads", async () => { const bound = await bindGenericCurrentConversation({ - targetSessionKey: "agent:codex:acp:slack-dm", + targetSessionKey: "agent:codex:acp:workspace-dm", targetKind: "session", conversation: { - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", }, metadata: { - label: "slack-dm", + label: "workspace-dm", }, }); expect(bound).not.toBeNull(); touchGenericCurrentConversationBinding( - "generic:slack\u241fdefault\u241f\u241fuser:U123", + "generic:workspace\u241fdefault\u241f\u241fuser:U123", 1_234_567_890, ); @@ -324,13 +326,13 @@ describe("generic current-conversation bindings", () => { expect( resolveGenericCurrentConversationBinding({ - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", })?.metadata, ).toEqual( expect.objectContaining({ - label: "slack-dm", + label: "workspace-dm", lastActivityAt: 1_234_567_890, }), ); diff --git a/src/infra/outbound/delivery-queue.reconnect-drain.test.ts b/src/infra/outbound/delivery-queue.reconnect-drain.test.ts index ce85a94c020..0302341c46a 100644 --- a/src/infra/outbound/delivery-queue.reconnect-drain.test.ts +++ b/src/infra/outbound/delivery-queue.reconnect-drain.test.ts @@ -17,13 +17,13 @@ import { } from "./delivery-queue.test-helpers.js"; const stubCfg = {} as OpenClawConfig; -const NO_LISTENER_ERROR = "No active WhatsApp Web listener"; +const NO_LISTENER_ERROR = "No active DirectChat listener"; function normalizeReconnectAccountIdForTest(accountId?: string | null): string { return (accountId ?? "").trim() || "default"; } -async function drainWhatsAppReconnectPending(opts: { +async function drainDirectChatReconnectPending(opts: { accountId: string; deliver: DeliverFn; log: RecoveryLogger; @@ -31,15 +31,15 @@ async function drainWhatsAppReconnectPending(opts: { }) { const normalizedAccountId = normalizeReconnectAccountIdForTest(opts.accountId); await drainPendingDeliveries({ - drainKey: `whatsapp:${normalizedAccountId}`, - logLabel: "WhatsApp reconnect drain", + drainKey: `directchat:${normalizedAccountId}`, + logLabel: "DirectChat reconnect drain", cfg: stubCfg, log: opts.log, stateDir: opts.stateDir, deliver: opts.deliver, selectEntry: (entry) => ({ match: - entry.channel === "whatsapp" && + entry.channel === "directchat" && normalizeReconnectAccountIdForTest(entry.accountId) === normalizedAccountId, bypassBackoff: typeof entry.lastError === "string" && entry.lastError.includes(NO_LISTENER_ERROR), @@ -53,20 +53,25 @@ function createTransientFailureDeliver(): DeliverFn { }); } -async function enqueueFailedWhatsAppDelivery(params: { +async function enqueueFailedDirectChatDelivery(params: { accountId: string; stateDir: string; error?: string; }): Promise { const id = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: params.accountId }, + { + channel: "directchat", + to: "+1555", + payloads: [{ text: "hi" }], + accountId: params.accountId, + }, params.stateDir, ); await failDelivery(id, params.error ?? NO_LISTENER_ERROR, params.stateDir); return id; } -describe("drainPendingDeliveries for WhatsApp reconnect", () => { +describe("drainPendingDeliveries for reconnect", () => { let tmpDir: string; const fixtures = installDeliveryQueueTmpDirHooks(); @@ -79,12 +84,12 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { const deliver = vi.fn(async () => {}); const id = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); - await failDelivery(id, "No active WhatsApp Web listener", tmpDir); + await failDelivery(id, NO_LISTENER_ERROR, tmpDir); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -93,7 +98,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { expect(deliver).toHaveBeenCalledTimes(1); expect(deliver).toHaveBeenCalledWith( - expect.objectContaining({ channel: "whatsapp", to: "+1555", skipQueue: true }), + expect.objectContaining({ channel: "directchat", to: "+1555", skipQueue: true }), ); }); @@ -102,12 +107,12 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { const deliver = vi.fn(async () => {}); const id = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "other" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "other" }, tmpDir, ); - await failDelivery(id, "No active WhatsApp Web listener", tmpDir); + await failDelivery(id, NO_LISTENER_ERROR, tmpDir); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -122,7 +127,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { const log = createRecoveryLog(); const deliver = createTransientFailureDeliver(); - const id = await enqueueFailedWhatsAppDelivery({ accountId: "acct1", stateDir: tmpDir }); + const id = await enqueueFailedDirectChatDelivery({ accountId: "acct1", stateDir: tmpDir }); const queueDir = path.join(tmpDir, "delivery-queue"); const filePath = path.join(queueDir, `${id}.json`); const before = JSON.parse(fs.readFileSync(filePath, "utf-8")) as { @@ -131,7 +136,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { lastError?: string; }; - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -155,11 +160,11 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { const log = createRecoveryLog(); const deliver = createTransientFailureDeliver(); - await enqueueFailedWhatsAppDelivery({ accountId: "acct1", stateDir: tmpDir }); + await enqueueFailedDirectChatDelivery({ accountId: "acct1", stateDir: tmpDir }); // Should not throw await expect( - drainWhatsAppReconnectPending({ + drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -173,16 +178,16 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { const deliver = vi.fn(async () => {}); const id = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); // Bump retryCount to MAX_RETRIES for (let i = 0; i < MAX_RETRIES; i++) { - await failDelivery(id, "No active WhatsApp Web listener", tmpDir); + await failDelivery(id, NO_LISTENER_ERROR, tmpDir); } - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -207,7 +212,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { }); await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); // Fail it so it matches the "no listener" filter @@ -219,16 +224,16 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { } const entryPath = path.join(tmpDir, "delivery-queue", pending); const entry = JSON.parse(fs.readFileSync(entryPath, "utf-8")); - entry.lastError = "No active WhatsApp Web listener"; + entry.lastError = NO_LISTENER_ERROR; entry.retryCount = 1; fs.writeFileSync(entryPath, JSON.stringify(entry, null, 2)); const opts = { accountId: "acct1", log, stateDir: tmpDir, deliver }; // Start first drain (will block on deliver) - const first = drainWhatsAppReconnectPending(opts); + const first = drainDirectChatReconnectPending(opts); // Start second drain immediately — should be skipped - const second = drainWhatsAppReconnectPending(opts); + const second = drainDirectChatReconnectPending(opts); await second; expect(log.info).toHaveBeenCalledWith(expect.stringContaining("already in progress")); @@ -250,7 +255,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { }); const id = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); const queuePath = path.join(tmpDir, "delivery-queue", `${id}.json`); @@ -264,7 +269,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { retryCount: number; lastError?: string; }; - entry.lastError = "No active WhatsApp Web listener"; + entry.lastError = NO_LISTENER_ERROR; fs.writeFileSync(queuePath, JSON.stringify(entry, null, 2)); const startupRecovery = recoverPendingDeliveries({ @@ -278,7 +283,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { expect(deliver).toHaveBeenCalledTimes(1); }); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -313,23 +318,23 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { { channel: "demo-channel-a", to: "+1000", payloads: [{ text: "blocker" }] }, tmpDir, ); - const whatsappId = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + const directChatId = await enqueueDelivery( + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); const queueDir = path.join(tmpDir, "delivery-queue"); const blockerPath = path.join(queueDir, `${blockerId}.json`); - const whatsappPath = path.join(queueDir, `${whatsappId}.json`); + const directChatPath = path.join(queueDir, `${directChatId}.json`); const blockerEntry = JSON.parse(fs.readFileSync(blockerPath, "utf-8")) as { enqueuedAt: number; }; - const whatsappEntry = JSON.parse(fs.readFileSync(whatsappPath, "utf-8")) as { + const directChatEntry = JSON.parse(fs.readFileSync(directChatPath, "utf-8")) as { enqueuedAt: number; }; blockerEntry.enqueuedAt = 1; - whatsappEntry.enqueuedAt = 2; + directChatEntry.enqueuedAt = 2; fs.writeFileSync(blockerPath, JSON.stringify(blockerEntry, null, 2)); - fs.writeFileSync(whatsappPath, JSON.stringify(whatsappEntry, null, 2)); + fs.writeFileSync(directChatPath, JSON.stringify(directChatEntry, null, 2)); const startupRecovery = recoverPendingDeliveries({ cfg: stubCfg, @@ -344,7 +349,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { ); }); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -360,16 +365,16 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { expect.stringContaining("Recovery skipped for delivery"), ); }); - it("drains fresh pending WhatsApp entries for the reconnecting account", async () => { + it("drains fresh pending entries for the reconnecting account", async () => { const log = createRecoveryLog(); const deliver = vi.fn(async () => {}); await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -382,12 +387,12 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { ).toEqual([]); }); - it("drains backoff-eligible WhatsApp retries on reconnect", async () => { + it("drains backoff-eligible retries on reconnect", async () => { const log = createRecoveryLog(); const deliver = vi.fn(async () => {}); const id = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); await failDelivery(id, "network down", tmpDir); @@ -398,7 +403,7 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { entry.lastAttemptAt = Date.now() - 30_000; fs.writeFileSync(entryPath, JSON.stringify(entry, null, 2)); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -413,12 +418,12 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { const deliver = vi.fn(async () => {}); const id = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); await failDelivery(id, "network down", tmpDir); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -434,12 +439,12 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { const deliver = vi.fn(async () => {}); const id = await enqueueDelivery( - { channel: "whatsapp", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "directchat", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); await failDelivery(id, NO_LISTENER_ERROR, tmpDir); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, @@ -449,16 +454,16 @@ describe("drainPendingDeliveries for WhatsApp reconnect", () => { expect(deliver).toHaveBeenCalledTimes(1); }); - it("ignores non-WhatsApp entries even when reconnect drain runs", async () => { + it("ignores other channels even when reconnect drain runs", async () => { const log = createRecoveryLog(); const deliver = vi.fn(async () => {}); await enqueueDelivery( - { channel: "telegram", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, + { channel: "forum", to: "+1555", payloads: [{ text: "hi" }], accountId: "acct1" }, tmpDir, ); - await drainWhatsAppReconnectPending({ + await drainDirectChatReconnectPending({ accountId: "acct1", deliver, log, diff --git a/src/infra/outbound/delivery-queue.storage.test.ts b/src/infra/outbound/delivery-queue.storage.test.ts index feed95131cd..5cbd2895179 100644 --- a/src/infra/outbound/delivery-queue.storage.test.ts +++ b/src/infra/outbound/delivery-queue.storage.test.ts @@ -21,7 +21,7 @@ describe("delivery-queue storage", () => { it("creates and removes a queue entry", async () => { const id = await enqueueTextDelivery( { - channel: "whatsapp", + channel: "directchat", to: "+1555", payloads: [{ text: "hello" }], bestEffort: true, @@ -48,7 +48,7 @@ describe("delivery-queue storage", () => { const entry = readQueuedEntry(tmpDir(), id); expect(entry).toMatchObject({ id, - channel: "whatsapp", + channel: "directchat", to: "+1555", bestEffort: true, gifPlayback: true, @@ -80,18 +80,18 @@ describe("delivery-queue storage", () => { it.each([ { name: "ack cleans up leftover .delivered marker when .json is already gone", - payload: { channel: "whatsapp", to: "+1", payloads: [{ text: "stale-marker" }] }, + payload: { channel: "directchat", to: "+1", payloads: [{ text: "stale-marker" }] }, prepareDeliveredMarker: true, action: (id: string) => ackDelivery(id, tmpDir()), }, { name: "ack removes .delivered marker so recovery does not replay", - payload: { channel: "whatsapp", to: "+1", payloads: [{ text: "ack-test" }] }, + payload: { channel: "directchat", to: "+1", payloads: [{ text: "ack-test" }] }, action: (id: string) => ackDelivery(id, tmpDir()), }, { name: "loadPendingDeliveries cleans up stale .delivered markers without replaying", - payload: { channel: "telegram", to: "99", payloads: [{ text: "stale" }] }, + payload: { channel: "forum", to: "99", payloads: [{ text: "stale" }] }, prepareDeliveredMarker: true, action: () => loadPendingDeliveries(tmpDir()), expectedEntriesLength: 0, @@ -118,7 +118,7 @@ describe("delivery-queue storage", () => { it("increments retryCount, records attempt time, and sets lastError", async () => { const id = await enqueueTextDelivery( { - channel: "telegram", + channel: "forum", to: "123", payloads: [{ text: "test" }], }, @@ -139,7 +139,7 @@ describe("delivery-queue storage", () => { it("moves entry to failed/ subdirectory", async () => { const id = await enqueueTextDelivery( { - channel: "slack", + channel: "workspace", to: "#general", payloads: [{ text: "hi" }], }, @@ -160,8 +160,8 @@ describe("delivery-queue storage", () => { }); it("loads multiple entries", async () => { - await enqueueTextDelivery({ channel: "whatsapp", to: "+1", payloads: [{ text: "a" }] }); - await enqueueTextDelivery({ channel: "telegram", to: "2", payloads: [{ text: "b" }] }); + await enqueueTextDelivery({ channel: "directchat", to: "+1", payloads: [{ text: "a" }] }); + await enqueueTextDelivery({ channel: "forum", to: "2", payloads: [{ text: "b" }] }); expect(await loadPendingDeliveries(tmpDir())).toHaveLength(2); }); @@ -169,7 +169,7 @@ describe("delivery-queue storage", () => { it("persists gateway caller scopes for replay", async () => { const id = await enqueueTextDelivery( { - channel: "telegram", + channel: "forum", to: "2", payloads: [{ text: "b" }], gatewayClientScopes: ["operator.write"], @@ -184,7 +184,7 @@ describe("delivery-queue storage", () => { it("persists session context for recovery replay", async () => { const id = await enqueueTextDelivery( { - channel: "telegram", + channel: "forum", to: "2", payloads: [{ text: "b" }], session: { @@ -214,7 +214,7 @@ describe("delivery-queue storage", () => { it("backfills lastAttemptAt for legacy retry entries during load", async () => { const id = await enqueueTextDelivery({ - channel: "whatsapp", + channel: "directchat", to: "+1", payloads: [{ text: "legacy" }], }); diff --git a/src/infra/outbound/session-binding-service.test.ts b/src/infra/outbound/session-binding-service.test.ts index bd59d20823c..ed9857165ca 100644 --- a/src/infra/outbound/session-binding-service.test.ts +++ b/src/infra/outbound/session-binding-service.test.ts @@ -26,10 +26,10 @@ function setMinimalCurrentConversationRegistry(): void { setActivePluginRegistry( createTestRegistry([ { - pluginId: "slack", + pluginId: "workspace", source: "test", plugin: { - id: "slack", + id: "workspace", meta: { aliases: [] }, conversationBindings: { supportsCurrentConversationBinding: true, @@ -37,10 +37,10 @@ function setMinimalCurrentConversationRegistry(): void { }, }, { - pluginId: "msteams", + pluginId: "teamchat", source: "test", plugin: { - id: "msteams", + id: "teamchat", meta: { aliases: [] }, conversationBindings: { supportsCurrentConversationBinding: true, @@ -251,12 +251,12 @@ describe("session binding service", () => { }); }); - it("falls back to generic current-conversation bindings for built-in channels", async () => { + it("falls back to generic current-conversation bindings for registered channels", async () => { const service = getSessionBindingService(); expect( service.getCapabilities({ - channel: "Slack", + channel: "Workspace", accountId: " DEFAULT ", }), ).toEqual({ @@ -267,62 +267,62 @@ describe("session binding service", () => { }); const bound = await service.bind({ - targetSessionKey: "agent:codex:acp:slack-dm", + targetSessionKey: "agent:codex:acp:workspace-dm", targetKind: "session", conversation: { - channel: " Slack ", + channel: " Workspace ", accountId: " DEFAULT ", conversationId: " user:U123 ", }, metadata: { - label: "slack-dm", + label: "workspace-dm", }, ttlMs: 60_000, }); expect(bound).toMatchObject({ - bindingId: "generic:slack\u241fdefault\u241f\u241fuser:U123", - targetSessionKey: "agent:codex:acp:slack-dm", + bindingId: "generic:workspace\u241fdefault\u241f\u241fuser:U123", + targetSessionKey: "agent:codex:acp:workspace-dm", targetKind: "session", conversation: { - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", }, status: "active", metadata: expect.objectContaining({ - label: "slack-dm", + label: "workspace-dm", }), }); const resolved = service.resolveByConversation({ - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", }); expect(resolved).toMatchObject({ bindingId: bound.bindingId, - targetSessionKey: "agent:codex:acp:slack-dm", + targetSessionKey: "agent:codex:acp:workspace-dm", }); - expect(service.listBySession("agent:codex:acp:slack-dm")).toEqual([resolved]); + expect(service.listBySession("agent:codex:acp:workspace-dm")).toEqual([resolved]); service.touch(bound.bindingId, 1234); expect( service.resolveByConversation({ - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", })?.metadata, ).toEqual( expect.objectContaining({ - label: "slack-dm", + label: "workspace-dm", lastActivityAt: 1234, }), ); await expect( service.unbind({ - targetSessionKey: "agent:codex:acp:slack-dm", + targetSessionKey: "agent:codex:acp:workspace-dm", reason: "test cleanup", }), ).resolves.toEqual([ @@ -332,7 +332,7 @@ describe("session binding service", () => { ]); expect( service.resolveByConversation({ - channel: "slack", + channel: "workspace", accountId: "default", conversationId: "user:U123", }), @@ -344,7 +344,7 @@ describe("session binding service", () => { expect( service.getCapabilities({ - channel: "msteams", + channel: "teamchat", accountId: "default", }), ).toEqual({ @@ -356,10 +356,10 @@ describe("session binding service", () => { await expect( service.bind({ - targetSessionKey: "agent:codex:acp:msteams-room", + targetSessionKey: "agent:codex:acp:teamchat-room", targetKind: "session", conversation: { - channel: "msteams", + channel: "teamchat", accountId: "default", conversationId: "19:chatid@thread.v2", }, @@ -368,7 +368,7 @@ describe("session binding service", () => { ).rejects.toMatchObject({ code: "BINDING_CAPABILITY_UNSUPPORTED", details: { - channel: "msteams", + channel: "teamchat", accountId: "default", placement: "child", }, @@ -376,17 +376,17 @@ describe("session binding service", () => { await expect( service.bind({ - targetSessionKey: "agent:codex:acp:msteams-room", + targetSessionKey: "agent:codex:acp:teamchat-room", targetKind: "session", conversation: { - channel: "msteams", + channel: "teamchat", accountId: "default", conversationId: "19:chatid@thread.v2", }, }), ).resolves.toMatchObject({ conversation: { - channel: "msteams", + channel: "teamchat", accountId: "default", conversationId: "19:chatid@thread.v2", },