fix: only forward live delivery context when targeting own session

When a gateway tool call (restart, config.apply, config.patch, update.run)
specifies an explicit sessionKey that differs from the current agent's
session, the live delivery context (agentChannel/agentTo/agentAccountId)
belongs to the wrong session and would misroute post-restart replies.

Only set deliveryContext in the sentinel/RPC params when:
- No explicit sessionKey is provided (falls back to own session), or
- The explicit sessionKey matches the current agent's session key

Otherwise omit deliveryContext so the server falls back to
extractDeliveryInfo(sessionKey), which correctly resolves routing for
the target session.
This commit is contained in:
Bryan Marty 2026-03-04 17:59:31 +00:00
parent db21905291
commit 292546cb09
No known key found for this signature in database

View File

@ -89,10 +89,11 @@ export function createGatewayTool(opts?: {
if (!isRestartEnabled(opts?.config)) {
throw new Error("Gateway restart is disabled (commands.restart=false).");
}
const sessionKey =
const explicitSessionKey =
typeof params.sessionKey === "string" && params.sessionKey.trim()
? params.sessionKey.trim()
: opts?.agentSessionKey?.trim() || undefined;
: undefined;
const sessionKey = (explicitSessionKey ?? opts?.agentSessionKey?.trim()) || undefined;
const delayMs =
typeof params.delayMs === "number" && Number.isFinite(params.delayMs)
? Math.floor(params.delayMs)
@ -109,8 +110,17 @@ export function createGatewayTool(opts?: {
// runs to { channel: "webchat", to: "heartbeat" }, causing the
// sentinel to write stale routing data that fails post-restart.
// See #18612.
//
// Only apply the live context when the restart targets this agent's
// own session. When an explicit sessionKey points to a different
// session, the live context belongs to the wrong session and would
// misroute the post-restart reply. Fall back to extractDeliveryInfo()
// so the server uses the correct routing for the target session.
const isTargetingOtherSession =
explicitSessionKey != null &&
explicitSessionKey !== (opts?.agentSessionKey?.trim() || undefined);
const liveContext =
opts?.agentChannel != null && String(opts.agentChannel).trim()
!isTargetingOtherSession && opts?.agentChannel != null && String(opts.agentChannel).trim()
? {
channel: String(opts.agentChannel).trim(),
to: opts?.agentTo ?? undefined,
@ -178,17 +188,26 @@ export function createGatewayTool(opts?: {
restartDelayMs: number | undefined;
deliveryContext: typeof liveDeliveryContextForRpc;
} => {
const sessionKey =
const explicitSessionKey =
typeof params.sessionKey === "string" && params.sessionKey.trim()
? params.sessionKey.trim()
: opts?.agentSessionKey?.trim() || undefined;
: undefined;
const sessionKey = (explicitSessionKey ?? opts?.agentSessionKey?.trim()) || undefined;
const note =
typeof params.note === "string" && params.note.trim() ? params.note.trim() : undefined;
const restartDelayMs =
typeof params.restartDelayMs === "number" && Number.isFinite(params.restartDelayMs)
? Math.floor(params.restartDelayMs)
: undefined;
return { sessionKey, note, restartDelayMs, deliveryContext: liveDeliveryContextForRpc };
// Only forward live context when the target session is this agent's
// own session. When an explicit sessionKey points to a different
// session, omit deliveryContext so the server falls back to
// extractDeliveryInfo(sessionKey) which uses that session's routing.
const isTargetingOtherSession =
explicitSessionKey != null &&
explicitSessionKey !== (opts?.agentSessionKey?.trim() || undefined);
const deliveryContext = isTargetingOtherSession ? undefined : liveDeliveryContextForRpc;
return { sessionKey, note, restartDelayMs, deliveryContext };
};
const resolveConfigWriteParams = async (): Promise<{