diff --git a/src/agents/subagent-announce.ts b/src/agents/subagent-announce.ts index b3f7dbf55..781ed453c 100644 --- a/src/agents/subagent-announce.ts +++ b/src/agents/subagent-announce.ts @@ -18,6 +18,7 @@ import { callGateway } from "../gateway/call.js"; import { defaultRuntime } from "../runtime.js"; import { type DeliveryContext, + deliveryContextFromSession, deliveryContextKey, mergeDeliveryContext, normalizeDeliveryContext, @@ -96,6 +97,7 @@ type AnnounceQueueItem = { enqueuedAt: number; sessionKey: string; origin?: DeliveryContext; + originKey?: string; }; type AnnounceQueueState = { @@ -180,7 +182,9 @@ function enqueueAnnounce( } } - queue.items.push(item); + const origin = normalizeDeliveryContext(item.origin); + const originKey = deliveryContextKey(origin); + queue.items.push({ ...item, origin, originKey }); return true; } @@ -229,17 +233,14 @@ function hasCrossChannelItems(items: AnnounceQueueItem[]): boolean { const keys = new Set(); let hasUnkeyed = false; for (const item of items) { - const origin = normalizeDeliveryContext(item.origin); - if (!origin) { + if (!item.origin) { hasUnkeyed = true; continue; } - if (!origin.channel || !origin.to) { + if (!item.originKey) { return true; } - const key = deliveryContextKey(origin); - if (!key) return true; - keys.add(key); + keys.add(item.originKey); } if (keys.size === 0) return false; if (hasUnkeyed) return true; @@ -382,14 +383,7 @@ async function maybeQueueSubagentAnnounce(params: { queueSettings.mode === "steer-backlog" || queueSettings.mode === "interrupt"; if (isActive && (shouldFollowup || queueSettings.mode === "steer")) { - const origin = mergeDeliveryContext( - { - channel: entry?.lastChannel, - to: entry?.lastTo, - accountId: entry?.lastAccountId, - }, - params.requesterOrigin, - ); + const origin = mergeDeliveryContext(deliveryContextFromSession(entry), params.requesterOrigin); enqueueAnnounce( canonicalKey, { @@ -633,11 +627,7 @@ export async function runSubagentAnnounceFlow(params: { let directOrigin = requesterOrigin; if (!directOrigin) { const { entry } = loadRequesterSessionEntry(params.requesterSessionKey); - directOrigin = normalizeDeliveryContext({ - channel: entry?.lastChannel ?? entry?.channel, - to: entry?.lastTo, - accountId: entry?.lastAccountId, - }); + directOrigin = deliveryContextFromSession(entry); } await callGateway({ method: "agent", diff --git a/src/commands/agent/delivery.ts b/src/commands/agent/delivery.ts index f0bfa3314..672777f93 100644 --- a/src/commands/agent/delivery.ts +++ b/src/commands/agent/delivery.ts @@ -20,6 +20,7 @@ import { resolveGatewayMessageChannel, } from "../../utils/message-channel.js"; import { normalizeAccountId } from "../../utils/account-id.js"; +import { deliveryContextFromSession } from "../../utils/delivery-context.js"; import type { AgentCommandOpts } from "./types.js"; type RunResult = Awaited< @@ -31,10 +32,11 @@ function resolveDeliveryAccountId(params: { sessionEntry?: SessionEntry; targetMode: ChannelOutboundTargetMode; }) { + const sessionOrigin = deliveryContextFromSession(params.sessionEntry); return ( normalizeAccountId(params.opts.accountId) ?? (params.targetMode === "implicit" - ? normalizeAccountId(params.sessionEntry?.lastAccountId) + ? normalizeAccountId(sessionOrigin?.accountId) : undefined) ); } diff --git a/src/utils/delivery-context.test.ts b/src/utils/delivery-context.test.ts index 1b7065b93..fc612ca21 100644 --- a/src/utils/delivery-context.test.ts +++ b/src/utils/delivery-context.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import { deliveryContextKey, + deliveryContextFromSession, mergeDeliveryContext, normalizeDeliveryContext, } from "./delivery-context.js"; @@ -42,4 +43,19 @@ describe("delivery context helpers", () => { ); expect(deliveryContextKey({ channel: "whatsapp" })).toBeUndefined(); }); + + it("derives delivery context from a session entry", () => { + expect( + deliveryContextFromSession({ + channel: "webchat", + lastChannel: " whatsapp ", + lastTo: " +1777 ", + lastAccountId: " acct-9 ", + }), + ).toEqual({ + channel: "whatsapp", + to: "+1777", + accountId: "acct-9", + }); + }); }); diff --git a/src/utils/delivery-context.ts b/src/utils/delivery-context.ts index 0cd1ceae6..e60084ca6 100644 --- a/src/utils/delivery-context.ts +++ b/src/utils/delivery-context.ts @@ -6,6 +6,13 @@ export type DeliveryContext = { accountId?: string; }; +type DeliveryContextSessionSource = { + channel?: string; + lastChannel?: string; + lastTo?: string; + lastAccountId?: string; +}; + export function normalizeDeliveryContext(context?: DeliveryContext): DeliveryContext | undefined { if (!context) return undefined; const channel = typeof context.channel === "string" ? context.channel.trim() : undefined; @@ -19,6 +26,17 @@ export function normalizeDeliveryContext(context?: DeliveryContext): DeliveryCon }; } +export function deliveryContextFromSession( + entry?: DeliveryContextSessionSource, +): DeliveryContext | undefined { + if (!entry) return undefined; + return normalizeDeliveryContext({ + channel: entry.lastChannel ?? entry.channel, + to: entry.lastTo, + accountId: entry.lastAccountId, + }); +} + export function mergeDeliveryContext( primary?: DeliveryContext, fallback?: DeliveryContext,