refactor: normalize delivery context

Co-authored-by: adam91holt <adam91holt@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-17 04:24:42 +00:00
parent 8ebfa2950d
commit 4f37f66264
4 changed files with 47 additions and 21 deletions

View File

@@ -18,6 +18,7 @@ import { callGateway } from "../gateway/call.js";
import { defaultRuntime } from "../runtime.js"; import { defaultRuntime } from "../runtime.js";
import { import {
type DeliveryContext, type DeliveryContext,
deliveryContextFromSession,
deliveryContextKey, deliveryContextKey,
mergeDeliveryContext, mergeDeliveryContext,
normalizeDeliveryContext, normalizeDeliveryContext,
@@ -96,6 +97,7 @@ type AnnounceQueueItem = {
enqueuedAt: number; enqueuedAt: number;
sessionKey: string; sessionKey: string;
origin?: DeliveryContext; origin?: DeliveryContext;
originKey?: string;
}; };
type AnnounceQueueState = { 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; return true;
} }
@@ -229,17 +233,14 @@ function hasCrossChannelItems(items: AnnounceQueueItem[]): boolean {
const keys = new Set<string>(); const keys = new Set<string>();
let hasUnkeyed = false; let hasUnkeyed = false;
for (const item of items) { for (const item of items) {
const origin = normalizeDeliveryContext(item.origin); if (!item.origin) {
if (!origin) {
hasUnkeyed = true; hasUnkeyed = true;
continue; continue;
} }
if (!origin.channel || !origin.to) { if (!item.originKey) {
return true; return true;
} }
const key = deliveryContextKey(origin); keys.add(item.originKey);
if (!key) return true;
keys.add(key);
} }
if (keys.size === 0) return false; if (keys.size === 0) return false;
if (hasUnkeyed) return true; if (hasUnkeyed) return true;
@@ -382,14 +383,7 @@ async function maybeQueueSubagentAnnounce(params: {
queueSettings.mode === "steer-backlog" || queueSettings.mode === "steer-backlog" ||
queueSettings.mode === "interrupt"; queueSettings.mode === "interrupt";
if (isActive && (shouldFollowup || queueSettings.mode === "steer")) { if (isActive && (shouldFollowup || queueSettings.mode === "steer")) {
const origin = mergeDeliveryContext( const origin = mergeDeliveryContext(deliveryContextFromSession(entry), params.requesterOrigin);
{
channel: entry?.lastChannel,
to: entry?.lastTo,
accountId: entry?.lastAccountId,
},
params.requesterOrigin,
);
enqueueAnnounce( enqueueAnnounce(
canonicalKey, canonicalKey,
{ {
@@ -633,11 +627,7 @@ export async function runSubagentAnnounceFlow(params: {
let directOrigin = requesterOrigin; let directOrigin = requesterOrigin;
if (!directOrigin) { if (!directOrigin) {
const { entry } = loadRequesterSessionEntry(params.requesterSessionKey); const { entry } = loadRequesterSessionEntry(params.requesterSessionKey);
directOrigin = normalizeDeliveryContext({ directOrigin = deliveryContextFromSession(entry);
channel: entry?.lastChannel ?? entry?.channel,
to: entry?.lastTo,
accountId: entry?.lastAccountId,
});
} }
await callGateway({ await callGateway({
method: "agent", method: "agent",

View File

@@ -20,6 +20,7 @@ import {
resolveGatewayMessageChannel, resolveGatewayMessageChannel,
} from "../../utils/message-channel.js"; } from "../../utils/message-channel.js";
import { normalizeAccountId } from "../../utils/account-id.js"; import { normalizeAccountId } from "../../utils/account-id.js";
import { deliveryContextFromSession } from "../../utils/delivery-context.js";
import type { AgentCommandOpts } from "./types.js"; import type { AgentCommandOpts } from "./types.js";
type RunResult = Awaited< type RunResult = Awaited<
@@ -31,10 +32,11 @@ function resolveDeliveryAccountId(params: {
sessionEntry?: SessionEntry; sessionEntry?: SessionEntry;
targetMode: ChannelOutboundTargetMode; targetMode: ChannelOutboundTargetMode;
}) { }) {
const sessionOrigin = deliveryContextFromSession(params.sessionEntry);
return ( return (
normalizeAccountId(params.opts.accountId) ?? normalizeAccountId(params.opts.accountId) ??
(params.targetMode === "implicit" (params.targetMode === "implicit"
? normalizeAccountId(params.sessionEntry?.lastAccountId) ? normalizeAccountId(sessionOrigin?.accountId)
: undefined) : undefined)
); );
} }

View File

@@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest";
import { import {
deliveryContextKey, deliveryContextKey,
deliveryContextFromSession,
mergeDeliveryContext, mergeDeliveryContext,
normalizeDeliveryContext, normalizeDeliveryContext,
} from "./delivery-context.js"; } from "./delivery-context.js";
@@ -42,4 +43,19 @@ describe("delivery context helpers", () => {
); );
expect(deliveryContextKey({ channel: "whatsapp" })).toBeUndefined(); 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",
});
});
}); });

View File

@@ -6,6 +6,13 @@ export type DeliveryContext = {
accountId?: string; accountId?: string;
}; };
type DeliveryContextSessionSource = {
channel?: string;
lastChannel?: string;
lastTo?: string;
lastAccountId?: string;
};
export function normalizeDeliveryContext(context?: DeliveryContext): DeliveryContext | undefined { export function normalizeDeliveryContext(context?: DeliveryContext): DeliveryContext | undefined {
if (!context) return undefined; if (!context) return undefined;
const channel = typeof context.channel === "string" ? context.channel.trim() : 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( export function mergeDeliveryContext(
primary?: DeliveryContext, primary?: DeliveryContext,
fallback?: DeliveryContext, fallback?: DeliveryContext,