fix: normalize delivery routing context
Co-authored-by: adam91holt <adam91holt@users.noreply.github.com>
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { DEFAULT_CHAT_CHANNEL } from "../../channels/registry.js";
|
||||
import { agentCommand } from "../../commands/agent.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import {
|
||||
@@ -9,10 +8,10 @@ import {
|
||||
updateSessionStore,
|
||||
} from "../../config/sessions.js";
|
||||
import { registerAgentRunContext } from "../../infra/agent-events.js";
|
||||
import { resolveOutboundTarget, resolveSessionDeliveryTarget } from "../../infra/outbound/targets.js";
|
||||
import { resolveAgentDeliveryPlan } from "../../infra/outbound/agent-delivery.js";
|
||||
import { resolveOutboundTarget } from "../../infra/outbound/targets.js";
|
||||
import { defaultRuntime } from "../../runtime.js";
|
||||
import { resolveSendPolicy } from "../../sessions/send-policy.js";
|
||||
import { normalizeAccountId } from "../../utils/account-id.js";
|
||||
import { normalizeSessionDeliveryFields } from "../../utils/delivery-context.js";
|
||||
import {
|
||||
INTERNAL_MESSAGE_CHANNEL,
|
||||
@@ -202,53 +201,21 @@ export const agentHandlers: GatewayRequestHandlers = {
|
||||
const runId = idem;
|
||||
|
||||
const wantsDelivery = request.deliver === true;
|
||||
const requestedChannel = normalizeMessageChannel(request.channel) ?? "last";
|
||||
const explicitTo =
|
||||
typeof request.to === "string" && request.to.trim() ? request.to.trim() : undefined;
|
||||
|
||||
const baseDelivery = resolveSessionDeliveryTarget({
|
||||
entry: sessionEntry,
|
||||
requestedChannel: requestedChannel === INTERNAL_MESSAGE_CHANNEL ? "last" : requestedChannel,
|
||||
const deliveryPlan = resolveAgentDeliveryPlan({
|
||||
sessionEntry,
|
||||
requestedChannel: request.channel,
|
||||
explicitTo,
|
||||
accountId: request.accountId,
|
||||
wantsDelivery,
|
||||
});
|
||||
|
||||
const resolvedChannel = (() => {
|
||||
if (requestedChannel === INTERNAL_MESSAGE_CHANNEL) return INTERNAL_MESSAGE_CHANNEL;
|
||||
if (requestedChannel === "last") {
|
||||
// WebChat is not a deliverable surface. Treat it as "unset" for routing,
|
||||
// so VoiceWake and CLI callers don't get stuck with deliver=false.
|
||||
if (baseDelivery.channel && baseDelivery.channel !== INTERNAL_MESSAGE_CHANNEL) {
|
||||
return baseDelivery.channel;
|
||||
}
|
||||
return wantsDelivery ? DEFAULT_CHAT_CHANNEL : INTERNAL_MESSAGE_CHANNEL;
|
||||
}
|
||||
const resolvedChannel = deliveryPlan.resolvedChannel;
|
||||
const deliveryTargetMode = deliveryPlan.deliveryTargetMode;
|
||||
const resolvedAccountId = deliveryPlan.resolvedAccountId;
|
||||
let resolvedTo = deliveryPlan.resolvedTo;
|
||||
|
||||
if (isGatewayMessageChannel(requestedChannel)) return requestedChannel;
|
||||
|
||||
if (baseDelivery.channel && baseDelivery.channel !== INTERNAL_MESSAGE_CHANNEL) {
|
||||
return baseDelivery.channel;
|
||||
}
|
||||
return wantsDelivery ? DEFAULT_CHAT_CHANNEL : INTERNAL_MESSAGE_CHANNEL;
|
||||
})();
|
||||
|
||||
const deliveryTargetMode = explicitTo
|
||||
? "explicit"
|
||||
: isDeliverableMessageChannel(resolvedChannel)
|
||||
? "implicit"
|
||||
: undefined;
|
||||
const resolvedAccountId =
|
||||
normalizeAccountId(request.accountId) ??
|
||||
(deliveryTargetMode === "implicit" && resolvedChannel === baseDelivery.lastChannel
|
||||
? baseDelivery.lastAccountId
|
||||
: undefined);
|
||||
let resolvedTo = explicitTo;
|
||||
if (
|
||||
!resolvedTo &&
|
||||
isDeliverableMessageChannel(resolvedChannel) &&
|
||||
resolvedChannel === baseDelivery.lastChannel
|
||||
) {
|
||||
resolvedTo = baseDelivery.lastTo;
|
||||
}
|
||||
if (!resolvedTo && isDeliverableMessageChannel(resolvedChannel)) {
|
||||
const cfg = cfgForAgent ?? loadConfig();
|
||||
const fallback = resolveOutboundTarget({
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
} from "../infra/restart-sentinel.js";
|
||||
import { enqueueSystemEvent } from "../infra/system-events.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { deliveryContextFromSession, mergeDeliveryContext } from "../utils/delivery-context.js";
|
||||
import { loadSessionEntry } from "./session-utils.js";
|
||||
|
||||
export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) {
|
||||
@@ -28,12 +29,11 @@ export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) {
|
||||
}
|
||||
|
||||
const { cfg, entry } = loadSessionEntry(sessionKey);
|
||||
const lastChannel = entry?.lastChannel;
|
||||
const lastTo = entry?.lastTo?.trim();
|
||||
const parsedTarget = resolveAnnounceTargetFromKey(sessionKey);
|
||||
const channelRaw = lastChannel ?? parsedTarget?.channel;
|
||||
const origin = mergeDeliveryContext(deliveryContextFromSession(entry), parsedTarget ?? undefined);
|
||||
const channelRaw = origin?.channel;
|
||||
const channel = channelRaw ? normalizeChannelId(channelRaw) : null;
|
||||
const to = lastTo || parsedTarget?.to;
|
||||
const to = origin?.to;
|
||||
if (!channel || !to) {
|
||||
enqueueSystemEvent(message, { sessionKey });
|
||||
return;
|
||||
@@ -43,7 +43,7 @@ export async function scheduleRestartSentinelWake(params: { deps: CliDeps }) {
|
||||
channel,
|
||||
to,
|
||||
cfg,
|
||||
accountId: parsedTarget?.accountId ?? entry?.lastAccountId,
|
||||
accountId: origin?.accountId,
|
||||
mode: "implicit",
|
||||
});
|
||||
if (!resolved.ok) {
|
||||
|
||||
@@ -128,6 +128,7 @@ describe("gateway server sessions", () => {
|
||||
thinkingLevel?: string;
|
||||
verboseLevel?: string;
|
||||
lastAccountId?: string;
|
||||
deliveryContext?: { channel?: string; to?: string; accountId?: string };
|
||||
}>;
|
||||
}>(ws, "sessions.list", { includeGlobal: false, includeUnknown: false });
|
||||
|
||||
@@ -140,6 +141,11 @@ describe("gateway server sessions", () => {
|
||||
expect(main?.thinkingLevel).toBe("low");
|
||||
expect(main?.verboseLevel).toBe("on");
|
||||
expect(main?.lastAccountId).toBe("work");
|
||||
expect(main?.deliveryContext).toEqual({
|
||||
channel: "whatsapp",
|
||||
to: "+1555",
|
||||
accountId: "work",
|
||||
});
|
||||
|
||||
const active = await rpcReq<{
|
||||
sessions: Array<{ key: string }>;
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
normalizeMainKey,
|
||||
parseAgentSessionKey,
|
||||
} from "../routing/session-key.js";
|
||||
import { normalizeSessionDeliveryFields } from "../utils/delivery-context.js";
|
||||
import type {
|
||||
GatewayAgentRow,
|
||||
GatewaySessionRow,
|
||||
@@ -401,6 +402,7 @@ export function listSessionsFromStore(params: {
|
||||
key,
|
||||
})
|
||||
: undefined);
|
||||
const deliveryFields = normalizeSessionDeliveryFields(entry);
|
||||
return {
|
||||
key,
|
||||
kind: classifySessionKey(key, entry),
|
||||
@@ -427,9 +429,10 @@ export function listSessionsFromStore(params: {
|
||||
modelProvider: entry?.modelProvider,
|
||||
model: entry?.model,
|
||||
contextTokens: entry?.contextTokens,
|
||||
lastChannel: entry?.lastChannel,
|
||||
lastTo: entry?.lastTo,
|
||||
lastAccountId: entry?.lastAccountId,
|
||||
deliveryContext: deliveryFields.deliveryContext,
|
||||
lastChannel: deliveryFields.lastChannel ?? entry?.lastChannel,
|
||||
lastTo: deliveryFields.lastTo ?? entry?.lastTo,
|
||||
lastAccountId: deliveryFields.lastAccountId ?? entry?.lastAccountId,
|
||||
} satisfies GatewaySessionRow;
|
||||
})
|
||||
.sort((a, b) => (b.updatedAt ?? 0) - (a.updatedAt ?? 0));
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type { SessionEntry } from "../config/sessions.js";
|
||||
import type { DeliveryContext } from "../utils/delivery-context.js";
|
||||
|
||||
export type GatewaySessionsDefaults = {
|
||||
modelProvider: string | null;
|
||||
@@ -32,6 +33,7 @@ export type GatewaySessionRow = {
|
||||
modelProvider?: string;
|
||||
model?: string;
|
||||
contextTokens?: number;
|
||||
deliveryContext?: DeliveryContext;
|
||||
lastChannel?: SessionEntry["lastChannel"];
|
||||
lastTo?: string;
|
||||
lastAccountId?: string;
|
||||
|
||||
Reference in New Issue
Block a user