diff --git a/src/gateway/server-methods/agent.test.ts b/src/gateway/server-methods/agent.test.ts index e749cda03..75efc1c32 100644 --- a/src/gateway/server-methods/agent.test.ts +++ b/src/gateway/server-methods/agent.test.ts @@ -184,6 +184,8 @@ async function invokeAgent( respond?: ReturnType; reqId?: string; context?: GatewayRequestContext; + client?: AgentHandlerArgs["client"]; + isWebchatConnect?: AgentHandlerArgs["isWebchatConnect"]; }, ) { const respond = options?.respond ?? vi.fn(); @@ -192,8 +194,8 @@ async function invokeAgent( respond: respond as never, context: options?.context ?? makeContext(), req: { type: "req", id: options?.reqId ?? "agent-test-req", method: "agent" }, - client: null, - isWebchatConnect: () => false, + client: options?.client ?? null, + isWebchatConnect: options?.isWebchatConnect ?? (() => false), }); return respond; } @@ -346,6 +348,56 @@ describe("gateway agent handler", () => { expect(callArgs.bestEffortDeliver).toBe(false); }); + it("keeps origin messageChannel as webchat while delivery channel uses last session channel", async () => { + mockMainSessionEntry({ + sessionId: "existing-session-id", + lastChannel: "telegram", + lastTo: "12345", + }); + mocks.updateSessionStore.mockImplementation(async (_path, updater) => { + const store: Record = { + "agent:main:main": { + sessionId: "existing-session-id", + updatedAt: Date.now(), + lastChannel: "telegram", + lastTo: "12345", + }, + }; + return await updater(store); + }); + mocks.agentCommand.mockResolvedValue({ + payloads: [{ text: "ok" }], + meta: { durationMs: 100 }, + }); + + await invokeAgent( + { + message: "webchat turn", + sessionKey: "agent:main:main", + idempotencyKey: "test-webchat-origin-channel", + }, + { + reqId: "webchat-origin-1", + client: { + connect: { + client: { id: "webchat-ui", mode: "webchat" }, + }, + } as AgentHandlerArgs["client"], + isWebchatConnect: () => true, + }, + ); + + await vi.waitFor(() => expect(mocks.agentCommand).toHaveBeenCalled()); + const callArgs = mocks.agentCommand.mock.calls.at(-1)?.[0] as { + channel?: string; + messageChannel?: string; + runContext?: { messageChannel?: string }; + }; + expect(callArgs.channel).toBe("telegram"); + expect(callArgs.messageChannel).toBe("webchat"); + expect(callArgs.runContext?.messageChannel).toBe("webchat"); + }); + it("handles missing cliSessionIds gracefully", async () => { mockMainSessionEntry({}); diff --git a/src/gateway/server-methods/agent.ts b/src/gateway/server-methods/agent.ts index a153c556e..5aa518a55 100644 --- a/src/gateway/server-methods/agent.ts +++ b/src/gateway/server-methods/agent.ts @@ -563,6 +563,17 @@ export const agentHandlers: GatewayRequestHandlers = { return; } + const normalizedTurnSource = normalizeMessageChannel(turnSourceChannel); + const turnSourceMessageChannel = + normalizedTurnSource && isGatewayMessageChannel(normalizedTurnSource) + ? normalizedTurnSource + : undefined; + const originMessageChannel = + turnSourceMessageChannel ?? + (client?.connect && isWebchatConnect(client.connect) + ? INTERNAL_MESSAGE_CHANNEL + : resolvedChannel); + const deliver = request.deliver === true && resolvedChannel !== INTERNAL_MESSAGE_CHANNEL; const accepted = { @@ -594,7 +605,7 @@ export const agentHandlers: GatewayRequestHandlers = { accountId: resolvedAccountId, threadId: resolvedThreadId, runContext: { - messageChannel: resolvedChannel, + messageChannel: originMessageChannel, accountId: resolvedAccountId, groupId: resolvedGroupId, groupChannel: resolvedGroupChannel, @@ -607,7 +618,7 @@ export const agentHandlers: GatewayRequestHandlers = { spawnedBy: spawnedByValue, timeout: request.timeout?.toString(), bestEffortDeliver, - messageChannel: resolvedChannel, + messageChannel: originMessageChannel, runId, lane: request.lane, extraSystemPrompt: request.extraSystemPrompt,