openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
2,069 lines • 88.6 kB
JavaScript
import { c as normalizeOptionalString, s as normalizeOptionalLowercaseString } from "./string-coerce-mnp54Vah.js";
import { a as formatUncaughtError, c as readErrorName } from "./errors-BXgSefBE.js";
import { n as isAbortError } from "./unhandled-rejections-CifjyMHp.js";
import { _ as uniqueStrings, l as normalizeStringEntries } from "./string-normalization-WNUDCpXX.js";
import { n as defaultRuntime } from "./runtime-B4lgFmsS.js";
import "./agent-scope-MrLta7Pq.js";
import { c as parseAgentSessionKey, l as parseRawSessionConversationRef, n as isAcpSessionKey, u as parseThreadSessionSuffix } from "./session-key-utils-Bx3apsJ3.js";
import { f as resolveAgentIdFromSessionKey, s as classifySessionKeyShape, u as normalizeAgentId } from "./session-key-B_NoIfpX.js";
import { c as resolveDefaultAgentId, n as listAgentIds } from "./agent-scope-config-CgCYpZfK.js";
import { r as resolveProviderIdForAuth } from "./provider-auth-aliases-BNZrcvHv.js";
import { a as hasGatewayClientCap, r as GATEWAY_CLIENT_MODES, t as GATEWAY_CLIENT_CAPS } from "./client-info-CcqJJIan.js";
import { t as setSafeTimeout } from "./timer-delay-T0mHtzex.js";
import { t as ADMIN_SCOPE } from "./operator-scopes-CS3xdS-V.js";
import "./method-scopes-BtdN3y7s.js";
import { i as resolveAssistantAvatarUrl } from "./control-ui-shared-Clry3kRy.js";
import { l as onAgentEvent, u as registerAgentRunContext } from "./agent-events-C1B8VhOg.js";
import { n as resolveAgentMainSessionKey, r as resolveExplicitAgentSessionKey } from "./main-session-Eahn-btj.js";
import { i as resolveSessionStoreKey, n as canonicalizeSpawnedByForAgent } from "./session-store-key-BsydjA1h.js";
import { i as mergeDeliveryContext, o as normalizeDeliveryContext, s as normalizeSessionDeliveryFields } from "./delivery-context.shared-CpAdEETO.js";
import { n as isInternalNonDeliveryChannel } from "./message-channel-constants-CgI32lqD.js";
import { a as resolveSessionFilePathOptions, i as resolveSessionFilePath } from "./paths-NEwU8m3X.js";
import { D as resolveMaintenanceConfigFromInput } from "./store-load-C4uq6Lrq.js";
import { d as updateSessionStore } from "./store-Qsgtu-0y.js";
import { i as normalizeMessageChannel, n as isGatewayMessageChannel, t as isDeliverableMessageChannel } from "./message-channel-normalize-BLLf0ubu.js";
import "./message-channel-BiOeMu0l.js";
import { r as mergeSessionEntry } from "./types-D8S_uNvu.js";
import { c as resolveSessionLifecycleTimestamps, l as resolveTerminalMainSessionTranscriptRegistryCheck, o as hasTerminalMainSessionTranscriptNewerThanRegistrySync } from "./sessions-D6zZvoxX.js";
import { c as resolveSessionResetPolicy, n as resolveChannelResetConfig, o as evaluateSessionFreshness, r as resolveSessionResetType } from "./reset-D9vnBMp0.js";
import { t as asBoolean } from "./boolean-By0bZpFH.js";
import { a as normalizeAgentRunTimeoutPhase, i as mergeAgentRunTerminalOutcome, t as buildAgentRunTerminalOutcome } from "./agent-run-terminal-outcome-wSgVLvyR.js";
import { i as createRunningTaskRun, o as finalizeTaskRunByRunId } from "./detached-task-runtime-DbytjQtn.js";
import { t as resolveAgentTimeoutMs } from "./timeout-Drw0_zOv.js";
import { r as readAcpSessionMeta } from "./session-meta-BuCj-TpW.js";
import { c as shouldPreserveUserFacingSessionStateForInputProvenance, n as annotateInterSessionPromptText, s as normalizeInputProvenance } from "./input-provenance-CYQvfQZP.js";
import { b as resolveSessionModelRef, d as migrateAndPruneGatewaySessionStoreKey, g as resolveGatewaySessionStoreTarget, h as resolveGatewayModelSupportsImages, l as loadGatewaySessionRow, u as loadSessionEntry } from "./session-utils-CHNCuY8a.js";
import { n as resolveSendPolicy } from "./send-policy-CfC5ATxp.js";
import { c as isTimeoutError } from "./failover-error-DAwMDmz6.js";
import { s as resolveTrustedGroupId } from "./agent-tools.policy-dtD_g0G1.js";
import { c as resolveIngressWorkspaceOverrideForSpawnedRun, s as normalizeSpawnedRunMetadata } from "./subagent-spawn-plan-DY8rBX1m.js";
import "./subagent-announce-delivery-B2BgoPnM.js";
import { n as shouldDowngradeDeliveryToSessionOnly } from "./best-effort-delivery-BoBL3YtD.js";
import { r as resolveMessageChannelSelection } from "./channel-selection-MKj8pvsX.js";
import { t as clearAllCliSessions } from "./cli-session-DY8dQzGa.js";
import { in as errorShape, rn as ErrorCodes } from "./schema-BwaBORnA.js";
import { i as validateAgentWaitParams, n as validateAgentIdentityParams, r as validateAgentParams, t as formatValidationErrors } from "./src-oj0IwW6K.js";
import { l as updateChatRunProvider, o as registerChatAbortController, s as resolveAgentRunExpiresAtMs } from "./chat-abort-Cu_ehtms.js";
import { a as emitGatewaySessionEndPluginHook, c as performGatewaySessionReset, o as emitGatewaySessionStartPluginHook } from "./session-reset-service-DG5dw0Ix.js";
import { n as resolvePublicAgentAvatarSource, t as resolveAgentAvatar } from "./identity-avatar-DDczSDZA.js";
import { n as agentCommandFromIngress } from "./agent-command-DimMXeog.js";
import { n as resolveAgentDeliveryPlanWithSessionRoute, r as resolveAgentOutboundTarget } from "./agent-delivery-DgS5lONr.js";
import { t as formatForLog } from "./ws-log-DlzuStZb.js";
import "./agent-COt_BYfw.js";
import { i as parseExecApprovalFollowupApprovalId, n as consumeExecApprovalFollowupRuntimeHandoff, r as isExecApprovalFollowupSessionRebound } from "./bash-tools.exec-approval-followup-state-BQdEUwIw.js";
import { a as resolveChatAttachmentMaxBytes, i as parseMessageWithAttachments, n as MediaOffloadError, t as normalizeRpcAttachmentsToChatAttachments } from "./attachment-normalize-CATn3nI8.js";
import { n as setGatewayDedupeEntry, r as waitForTerminalGatewayDedupe, t as readTerminalSnapshotFromGatewayDedupe } from "./agent-wait-dedupe-Cb0P9yCb.js";
import { n as resolveAssistantIdentity } from "./assistant-identity-BYpxelqc.js";
import { r as resolveVoiceWakeRouteByTrigger, t as loadVoiceWakeRoutingConfig } from "./voicewake-routing-BF5EBU0h.js";
import { t as reactivateCompletedSubagentSession } from "./session-subagent-reactivation-CoFo29Kt.js";
import { existsSync } from "node:fs";
import { randomUUID } from "node:crypto";
//#region src/gateway/server-methods/agent-job.ts
const AGENT_RUN_CACHE_TTL_MS = 10 * 6e4;
/**
* Embedded runs can emit transient lifecycle `error` events while auth/model
* failover is still in progress. Give errors a short grace window so a
* subsequent `start` event can cancel premature terminal snapshots.
*/
const AGENT_RUN_ERROR_RETRY_GRACE_MS = 15e3;
/**
* Some embedded runtimes emit an intermediate lifecycle `end` with
* `aborted=true` immediately before retrying the same run. Hold timeout
* snapshots briefly so `agent.wait` does not resolve to a stale timeout when a
* final success is about to arrive.
*/
const AGENT_RUN_TIMEOUT_RETRY_GRACE_MS = 15e3;
const agentRunCache = /* @__PURE__ */ new Map();
const agentRunStarts = /* @__PURE__ */ new Map();
const pendingAgentRunErrors = /* @__PURE__ */ new Map();
const pendingAgentRunTimeouts = /* @__PURE__ */ new Map();
const agentRunWaiterCounts = /* @__PURE__ */ new Map();
let agentRunListenerStarted = false;
function pruneAgentRunCache(now = Date.now()) {
for (const [runId, entry] of agentRunCache) if (now - entry.ts > AGENT_RUN_CACHE_TTL_MS) agentRunCache.delete(runId);
}
function recordAgentRunSnapshot(entry) {
pruneAgentRunCache(entry.ts);
const existing = agentRunCache.get(entry.runId);
if (existing && shouldPreserveTerminalSnapshot(existing, entry)) {
agentRunCache.set(entry.runId, {
...existing,
ts: entry.ts
});
return;
}
agentRunCache.set(entry.runId, entry);
}
function shouldPreserveTerminalSnapshot(existing, incoming) {
const existingOutcome = terminalOutcomeFromSnapshot(existing);
const incomingOutcome = terminalOutcomeFromSnapshot(incoming);
if (!existingOutcome) return false;
if (!incomingOutcome) return false;
return mergeAgentRunTerminalOutcome(existingOutcome, incomingOutcome) === existingOutcome;
}
function terminalOutcomeFromSnapshot(snapshot) {
if (snapshot.pendingError) return;
return buildAgentRunTerminalOutcome(snapshot);
}
function clearPendingAgentRunError(runId) {
const pending = pendingAgentRunErrors.get(runId);
if (!pending) return;
clearTimeout(pending.timer);
pendingAgentRunErrors.delete(runId);
}
function clearPendingAgentRunTimeout(runId) {
const pending = pendingAgentRunTimeouts.get(runId);
if (!pending) return;
clearTimeout(pending.timer);
pendingAgentRunTimeouts.delete(runId);
}
function schedulePendingAgentRunError(snapshot) {
const pendingTimeout = pendingAgentRunTimeouts.get(snapshot.runId);
if (pendingTimeout && shouldPreserveTerminalSnapshot(pendingTimeout.snapshot, snapshot)) return;
clearPendingAgentRunTimeout(snapshot.runId);
clearPendingAgentRunError(snapshot.runId);
const dueAt = Date.now() + AGENT_RUN_ERROR_RETRY_GRACE_MS;
const timer = setTimeout(() => {
const pending = pendingAgentRunErrors.get(snapshot.runId);
if (!pending) return;
pendingAgentRunErrors.delete(snapshot.runId);
recordAgentRunSnapshot(pending.snapshot);
}, AGENT_RUN_ERROR_RETRY_GRACE_MS);
timer.unref?.();
pendingAgentRunErrors.set(snapshot.runId, {
snapshot,
dueAt,
timer
});
}
function schedulePendingAgentRunTimeout(snapshot) {
const pendingTimeout = pendingAgentRunTimeouts.get(snapshot.runId);
if (pendingTimeout && shouldPreserveTerminalSnapshot(pendingTimeout.snapshot, snapshot)) return;
clearPendingAgentRunError(snapshot.runId);
clearPendingAgentRunTimeout(snapshot.runId);
const dueAt = Date.now() + AGENT_RUN_TIMEOUT_RETRY_GRACE_MS;
const timer = setTimeout(() => {
const pending = pendingAgentRunTimeouts.get(snapshot.runId);
if (!pending) return;
pendingAgentRunTimeouts.delete(snapshot.runId);
recordAgentRunSnapshot(pending.snapshot);
}, AGENT_RUN_TIMEOUT_RETRY_GRACE_MS);
timer.unref?.();
pendingAgentRunTimeouts.set(snapshot.runId, {
snapshot,
dueAt,
timer
});
}
function getPendingAgentRunError(runId) {
const pending = pendingAgentRunErrors.get(runId);
if (!pending) return;
return {
snapshot: pending.snapshot,
dueAt: pending.dueAt
};
}
function getPendingAgentRunTimeout(runId) {
const pending = pendingAgentRunTimeouts.get(runId);
if (!pending) return;
return {
snapshot: pending.snapshot,
dueAt: pending.dueAt
};
}
function createPendingErrorTimeoutSnapshot(snapshot) {
return {
runId: snapshot.runId,
status: "timeout",
startedAt: snapshot.startedAt,
error: snapshot.error,
pendingError: true,
...snapshot.providerStarted !== void 0 ? { providerStarted: snapshot.providerStarted } : {},
ts: Date.now()
};
}
function createSnapshotFromLifecycleEvent(params) {
const { runId, phase, data } = params;
const startedAt = typeof data?.startedAt === "number" ? data.startedAt : agentRunStarts.get(runId);
const endedAt = typeof data?.endedAt === "number" ? data.endedAt : void 0;
const error = typeof data?.error === "string" ? data.error : void 0;
const stopReason = typeof data?.stopReason === "string" ? data.stopReason : void 0;
const livenessState = typeof data?.livenessState === "string" ? data.livenessState : void 0;
const terminalOutcome = buildAgentRunTerminalOutcome({
status: phase === "error" ? "error" : data?.aborted ? "timeout" : "ok",
error,
stopReason,
livenessState,
timeoutPhase: data?.timeoutPhase,
providerStarted: data?.providerStarted,
startedAt,
endedAt
});
return {
runId,
status: terminalOutcome.status,
startedAt,
endedAt,
error: terminalOutcome.error,
stopReason,
livenessState,
...data?.yielded === true ? { yielded: true } : {},
...terminalOutcome.timeoutPhase ? { timeoutPhase: terminalOutcome.timeoutPhase } : {},
...terminalOutcome.providerStarted !== void 0 ? { providerStarted: terminalOutcome.providerStarted } : {},
ts: Date.now()
};
}
function ensureAgentRunListener() {
if (agentRunListenerStarted) return;
agentRunListenerStarted = true;
onAgentEvent((evt) => {
if (!evt) return;
if (evt.stream !== "lifecycle") return;
const phase = evt.data?.phase;
if (phase === "start") {
const startedAt = typeof evt.data?.startedAt === "number" ? evt.data.startedAt : void 0;
agentRunStarts.set(evt.runId, startedAt ?? Date.now());
clearPendingAgentRunError(evt.runId);
clearPendingAgentRunTimeout(evt.runId);
agentRunCache.delete(evt.runId);
return;
}
if (phase !== "end" && phase !== "error") return;
const snapshot = createSnapshotFromLifecycleEvent({
runId: evt.runId,
phase,
data: evt.data
});
agentRunStarts.delete(evt.runId);
if (phase === "error") {
schedulePendingAgentRunError(snapshot);
return;
}
if (snapshot.status === "timeout") {
schedulePendingAgentRunTimeout(snapshot);
return;
}
const pendingTimeout = pendingAgentRunTimeouts.get(evt.runId);
if (pendingTimeout && shouldPreserveTerminalSnapshot(pendingTimeout.snapshot, snapshot)) return;
clearPendingAgentRunError(evt.runId);
clearPendingAgentRunTimeout(evt.runId);
recordAgentRunSnapshot(snapshot);
});
}
function getCachedAgentRun(runId) {
pruneAgentRunCache();
return agentRunCache.get(runId);
}
function addAgentRunWaiter(runId) {
agentRunWaiterCounts.set(runId, (agentRunWaiterCounts.get(runId) ?? 0) + 1);
let removed = false;
return () => {
if (removed) return;
removed = true;
const nextCount = (agentRunWaiterCounts.get(runId) ?? 1) - 1;
if (nextCount <= 0) {
agentRunWaiterCounts.delete(runId);
return;
}
agentRunWaiterCounts.set(runId, nextCount);
};
}
async function waitForAgentJob(params) {
const { runId, timeoutMs, signal, ignoreCachedSnapshot = false } = params;
ensureAgentRunListener();
const cached = ignoreCachedSnapshot ? void 0 : getCachedAgentRun(runId);
if (cached) return cached;
if (timeoutMs <= 0 || signal?.aborted) return null;
return await new Promise((resolve) => {
let settled = false;
let pendingErrorTimer;
let pendingTimeoutTimer;
let pendingTimeoutSnapshot;
let removeWaiter = () => {};
const clearPendingErrorTimer = () => {
if (!pendingErrorTimer) return;
clearTimeout(pendingErrorTimer);
pendingErrorTimer = void 0;
};
const clearPendingTimeoutTimer = () => {
if (!pendingTimeoutTimer) return;
clearTimeout(pendingTimeoutTimer);
pendingTimeoutTimer = void 0;
pendingTimeoutSnapshot = void 0;
};
const finish = (entry) => {
if (settled) return;
settled = true;
clearTimeout(timer);
clearPendingErrorTimer();
clearPendingTimeoutTimer();
unsubscribe();
removeWaiter();
if (onAbort) signal?.removeEventListener("abort", onAbort);
resolve(entry);
};
const scheduleTerminalFinish = (kind, snapshot, delayMs) => {
if (pendingTimeoutSnapshot && shouldPreserveTerminalSnapshot(pendingTimeoutSnapshot, snapshot)) return;
clearPendingErrorTimer();
clearPendingTimeoutTimer();
const timerRef = setSafeTimeout(() => {
const latest = ignoreCachedSnapshot ? void 0 : getCachedAgentRun(runId);
if (latest) {
finish(latest);
return;
}
recordAgentRunSnapshot(snapshot);
finish(snapshot);
}, delayMs);
timerRef.unref?.();
if (kind === "error") pendingErrorTimer = timerRef;
else {
pendingTimeoutTimer = timerRef;
pendingTimeoutSnapshot = snapshot;
}
};
const scheduleErrorFinish = (snapshot, delayMs = AGENT_RUN_ERROR_RETRY_GRACE_MS) => {
scheduleTerminalFinish("error", snapshot, delayMs);
};
const scheduleTimeoutFinish = (snapshot, delayMs = AGENT_RUN_TIMEOUT_RETRY_GRACE_MS) => {
scheduleTerminalFinish("timeout", snapshot, delayMs);
};
if (!ignoreCachedSnapshot) {
const pendingError = getPendingAgentRunError(runId);
if (pendingError) scheduleErrorFinish(pendingError.snapshot, pendingError.dueAt - Date.now());
const pendingTimeout = getPendingAgentRunTimeout(runId);
if (pendingTimeout) scheduleTimeoutFinish(pendingTimeout.snapshot, pendingTimeout.dueAt - Date.now());
}
const unsubscribe = onAgentEvent((evt) => {
if (!evt || evt.stream !== "lifecycle") return;
if (evt.runId !== runId) return;
const phase = evt.data?.phase;
if (phase === "start") {
clearPendingErrorTimer();
clearPendingTimeoutTimer();
return;
}
if (phase !== "end" && phase !== "error") return;
const latest = ignoreCachedSnapshot ? void 0 : getCachedAgentRun(runId);
if (latest) {
if (pendingTimeoutSnapshot && shouldPreserveTerminalSnapshot(pendingTimeoutSnapshot, latest)) return;
finish(latest);
return;
}
const snapshot = createSnapshotFromLifecycleEvent({
runId: evt.runId,
phase,
data: evt.data
});
if (phase === "error") {
scheduleErrorFinish(snapshot);
return;
}
if (snapshot.status === "timeout") {
scheduleTimeoutFinish(snapshot);
return;
}
if (pendingTimeoutSnapshot && shouldPreserveTerminalSnapshot(pendingTimeoutSnapshot, snapshot)) return;
recordAgentRunSnapshot(snapshot);
finish(snapshot);
});
removeWaiter = addAgentRunWaiter(runId);
const timer = setSafeTimeout(() => {
const pendingError = getPendingAgentRunError(runId);
finish(pendingError ? createPendingErrorTimeoutSnapshot(pendingError.snapshot) : null);
}, timeoutMs);
const onAbort = () => finish(null);
signal?.addEventListener("abort", onAbort, { once: true });
});
}
ensureAgentRunListener();
//#endregion
//#region src/gateway/server-methods/agent.ts
const RESET_COMMAND_RE = /^\/(new|reset)(?:\s+([\s\S]*))?$/i;
function formatAttachmentFailureForLog(err) {
const primary = formatUncaughtError(err);
const cause = err instanceof Error ? err.cause : void 0;
if (cause === void 0) return primary;
const causeText = formatUncaughtError(cause);
if (!causeText || causeText === primary) return primary;
return `${primary}\nCaused by: ${causeText}`;
}
function logAttachmentFailure(logGateway, label, err) {
logGateway.error(label, {
error: formatAttachmentFailureForLog(err),
consoleMessage: `${label}: ${formatForLog(err)}`
});
}
function clientHasAdminScope(client) {
return (Array.isArray(client?.connect?.scopes) ? client.connect.scopes : []).includes(ADMIN_SCOPE);
}
function resolveAllowModelOverrideFromClient(client) {
return clientHasAdminScope(client) || client?.internal?.allowModelOverride === true;
}
function resolveCanUseInternalRuntimeHandoff(client) {
return client?.connect?.client?.mode === GATEWAY_CLIENT_MODES.BACKEND;
}
function emitAgentSendSessionLifecycleTransition(transition) {
if (!transition) return;
if (transition.previousSessionId) emitGatewaySessionEndPluginHook({
cfg: transition.cfg,
sessionKey: transition.sessionKey,
sessionId: transition.previousSessionId,
storePath: transition.storePath,
sessionFile: transition.previousSessionFile,
agentId: transition.agentId,
reason: transition.previousEndReason ?? "unknown",
nextSessionId: transition.sessionId,
nextSessionKey: transition.sessionKey
});
emitGatewaySessionStartPluginHook({
cfg: transition.cfg,
sessionKey: transition.sessionKey,
sessionId: transition.sessionId,
resumedFrom: transition.previousSessionId,
storePath: transition.storePath,
sessionFile: transition.sessionFile,
agentId: transition.agentId
});
}
async function runSessionResetFromAgent(params) {
const result = await performGatewaySessionReset({
key: params.key,
...params.agentId ? { agentId: params.agentId } : {},
reason: params.reason,
commandSource: "gateway:agent"
});
if (!result.ok) return result;
return {
ok: true,
key: result.key,
sessionId: result.entry.sessionId
};
}
function sessionResetAckText(reason) {
return reason === "new" ? "✅ New session started." : "✅ Session reset.";
}
function buildBareSessionResetResult(params) {
return {
payloads: [{ text: sessionResetAckText(params.reason) }],
meta: {
durationMs: 0,
...params.sessionId ? { agentMeta: { sessionId: params.sessionId } } : {}
}
};
}
function buildBareSessionResetResponse(params) {
return {
runId: params.runId,
status: "ok",
summary: "completed",
result: params.result
};
}
async function deliverBareSessionResetResult(params) {
const { deliverAgentCommandResult } = await import("./delivery.runtime.js");
const result = buildBareSessionResetResult({
reason: params.reason,
sessionId: params.sessionId
});
return await deliverAgentCommandResult({
cfg: params.cfg,
deps: params.context.deps,
runtime: defaultRuntime,
opts: {
message: sessionResetAckText(params.reason),
...params.agentId ? { agentId: params.agentId } : {},
...params.sessionId ? { sessionId: params.sessionId } : {},
sessionKey: params.sessionKey,
deliver: true,
replyTo: params.request.replyTo,
to: params.request.to,
replyChannel: params.request.replyChannel,
channel: params.request.channel,
replyAccountId: params.request.replyAccountId,
accountId: params.request.accountId,
threadId: params.request.threadId,
deliveryTargetMode: params.deliveryTargetMode,
bestEffortDeliver: params.bestEffortDeliver,
runId: params.runId,
messageChannel: params.originMessageChannel,
runContext: {
messageChannel: params.originMessageChannel,
accountId: params.request.replyAccountId ?? params.request.accountId,
currentThreadTs: params.request.threadId != null ? String(params.request.threadId) : void 0
},
allowModelOverride: false
},
outboundSession: void 0,
sessionEntry: params.sessionEntry,
result,
payloads: result.payloads
});
}
async function resolveBareSessionResetResult(params) {
if (params.request.deliver !== true) return buildBareSessionResetResult({
reason: params.reason,
sessionId: params.sessionId
});
if (resolveSendPolicy({
cfg: params.cfg,
entry: params.sessionEntry,
sessionKey: params.sessionKey,
channel: params.sessionEntry?.channel,
chatType: params.sessionEntry?.chatType
}) === "deny") throw new Error("send blocked by session policy");
const deliveryPlan = await resolveAgentDeliveryPlanWithSessionRoute({
cfg: params.cfg,
agentId: params.agentId ?? resolveAgentIdFromSessionKey(params.sessionKey),
currentSessionKey: params.sessionKey,
sessionEntry: params.sessionEntry,
requestedChannel: normalizeOptionalString(params.request.replyChannel) ?? normalizeOptionalString(params.request.channel),
explicitTo: normalizeOptionalString(params.request.replyTo) ?? normalizeOptionalString(params.request.to),
explicitThreadId: normalizeOptionalString(params.request.threadId),
accountId: normalizeOptionalString(params.request.replyAccountId) ?? normalizeOptionalString(params.request.accountId),
wantsDelivery: true,
turnSourceChannel: normalizeOptionalString(params.request.channel),
turnSourceTo: normalizeOptionalString(params.request.to),
turnSourceAccountId: normalizeOptionalString(params.request.accountId),
turnSourceThreadId: normalizeOptionalString(params.request.threadId)
});
const mainSessionKey = resolveAgentMainSessionKey({
cfg: params.cfg,
agentId: params.agentId ?? resolveAgentIdFromSessionKey(params.sessionKey)
});
const bestEffortDeliver = typeof params.request.bestEffortDeliver === "boolean" ? params.request.bestEffortDeliver : params.sessionKey === mainSessionKey || params.sessionKey === "global" ? true : void 0;
return await deliverBareSessionResetResult({
cfg: params.cfg,
context: params.context,
reason: params.reason,
sessionId: params.sessionId,
sessionKey: params.sessionKey,
agentId: params.agentId,
sessionEntry: params.sessionEntry,
request: params.request,
bestEffortDeliver,
deliveryTargetMode: deliveryPlan.deliveryTargetMode,
originMessageChannel: params.originMessageChannel ?? deliveryPlan.resolvedChannel,
runId: params.runId
});
}
function loadBareSessionResetDeliverySession(params) {
const selectedGlobalAgentId = params.sessionKey === "global" && params.agentId ? params.agentId : void 0;
const loaded = loadSessionEntry(params.sessionKey, {
clone: false,
...selectedGlobalAgentId ? { agentId: selectedGlobalAgentId } : {}
});
const loadedCfg = loaded?.cfg ?? params.cfg;
return {
cfg: loadedCfg,
entry: loaded?.entry,
agentId: selectedGlobalAgentId ?? resolveAgentIdFromSessionKey(params.sessionKey) ?? resolveDefaultAgentId(loadedCfg)
};
}
function resolveSessionRuntimeCwd(params) {
return normalizeOptionalString(params.sessionEntry?.spawnedCwd);
}
function normalizeTrustedGroupMetadata(value) {
return {
groupId: normalizeOptionalString(value?.groupId),
groupChannel: normalizeOptionalString(value?.groupChannel),
groupSpace: normalizeOptionalString(value?.groupSpace ?? value?.space)
};
}
function resolveSessionKeyGroupId(sessionKey) {
const { baseSessionKey } = parseThreadSessionSuffix(sessionKey);
const conversation = parseRawSessionConversationRef(baseSessionKey ?? sessionKey);
if (!conversation || conversation.kind !== "group" && conversation.kind !== "channel") return;
return conversation.rawId;
}
function resolveTrustedGroupMetadata(params) {
return {
groupId: params.stored.groupId ?? params.inherited?.groupId ?? resolveSessionKeyGroupId(params.sessionKey) ?? (params.spawnedBy ? resolveSessionKeyGroupId(params.spawnedBy) : void 0),
groupChannel: params.stored.groupChannel ?? params.inherited?.groupChannel,
groupSpace: params.stored.groupSpace ?? params.inherited?.groupSpace
};
}
function requestGroupMatchesTrusted(params) {
const requestGroupId = params.requestGroupId?.trim();
if (!requestGroupId) return true;
return Boolean(params.trustedGroupId && requestGroupId === params.trustedGroupId);
}
function emitSessionsChanged(context, payload) {
const connIds = context.getSessionEventSubscriberConnIds();
if (connIds.size === 0) return;
const sessionRow = payload.sessionKey ? loadGatewaySessionRow(payload.sessionKey, payload.sessionKey === "global" && payload.agentId ? { agentId: payload.agentId } : void 0) : null;
const omitUnscopedGlobalGoal = payload.sessionKey === "global" && !payload.agentId;
context.broadcastToConnIds("sessions.changed", {
...payload,
ts: Date.now(),
...sessionRow ? {
updatedAt: sessionRow.updatedAt ?? void 0,
sessionId: sessionRow.sessionId,
kind: sessionRow.kind,
channel: sessionRow.channel,
subject: sessionRow.subject,
groupChannel: sessionRow.groupChannel,
space: sessionRow.space,
chatType: sessionRow.chatType,
origin: sessionRow.origin,
spawnedBy: sessionRow.spawnedBy,
spawnedWorkspaceDir: sessionRow.spawnedWorkspaceDir,
spawnedCwd: sessionRow.spawnedCwd,
forkedFromParent: sessionRow.forkedFromParent,
spawnDepth: sessionRow.spawnDepth,
subagentRole: sessionRow.subagentRole,
subagentControlScope: sessionRow.subagentControlScope,
label: sessionRow.label,
displayName: sessionRow.displayName,
deliveryContext: sessionRow.deliveryContext,
parentSessionKey: sessionRow.parentSessionKey,
childSessions: sessionRow.childSessions,
thinkingLevel: sessionRow.thinkingLevel,
fastMode: sessionRow.fastMode,
verboseLevel: sessionRow.verboseLevel,
traceLevel: sessionRow.traceLevel,
reasoningLevel: sessionRow.reasoningLevel,
elevatedLevel: sessionRow.elevatedLevel,
sendPolicy: sessionRow.sendPolicy,
systemSent: sessionRow.systemSent,
abortedLastRun: sessionRow.abortedLastRun,
inputTokens: sessionRow.inputTokens,
outputTokens: sessionRow.outputTokens,
lastChannel: sessionRow.lastChannel,
lastTo: sessionRow.lastTo,
lastAccountId: sessionRow.lastAccountId,
lastThreadId: sessionRow.lastThreadId,
totalTokens: sessionRow.totalTokens,
totalTokensFresh: sessionRow.totalTokensFresh,
...omitUnscopedGlobalGoal ? {} : { goal: sessionRow.goal ?? null },
contextTokens: sessionRow.contextTokens,
estimatedCostUsd: sessionRow.estimatedCostUsd,
responseUsage: sessionRow.responseUsage,
modelProvider: sessionRow.modelProvider,
model: sessionRow.model,
status: sessionRow.status,
startedAt: sessionRow.startedAt,
endedAt: sessionRow.endedAt,
runtimeMs: sessionRow.runtimeMs,
compactionCheckpointCount: sessionRow.compactionCheckpointCount,
latestCompactionCheckpoint: sessionRow.latestCompactionCheckpoint
} : {}
}, connIds, { dropIfSlow: true });
}
function resolveGatewayAgentTaskTrackingMode(params) {
if (!params.sessionKey?.trim() || params.inputProvenance?.kind === "inter_session") return "none";
return params.client?.internal?.agentRunTracking === "plugin_subagent" ? "plugin_subagent" : "cli";
}
async function registerPluginSubagentRunFromGateway(params) {
const childSessionKey = params.childSessionKey.trim();
if (!childSessionKey) return;
const ownerSessionKey = resolveAgentMainSessionKey({
cfg: params.cfg,
agentId: resolveAgentIdFromSessionKey(childSessionKey)
});
const { registerSubagentRun } = await import("./subagent-registry-C9AeC_7M.js");
registerSubagentRun({
runId: params.runId,
childSessionKey,
controllerSessionKey: ownerSessionKey,
requesterSessionKey: ownerSessionKey,
requesterOrigin: params.requesterOrigin,
requesterDisplayKey: "main",
task: params.task,
cleanup: "keep",
...params.pluginId ? { label: `plugin:${params.pluginId}` } : {},
expectsCompletionMessage: false,
spawnMode: "run"
});
}
function resolveFailedTrackedAgentTaskStatus(error) {
return isAbortError(error) || isTimeoutError(error) ? "timed_out" : "failed";
}
function tryFinalizeTrackedAgentTask(params) {
try {
finalizeTaskRunByRunId({
runId: params.runId,
runtime: "cli",
status: params.status,
endedAt: Date.now(),
...params.error !== void 0 ? { error: params.error } : {},
...params.terminalSummary !== void 0 ? { terminalSummary: params.terminalSummary } : {}
});
} catch (err) {
params.log.warn(`failed to finalize tracked agent task ${params.runId}: ${formatForLog(err)}`);
}
}
function resolveAgentDedupeKeys(params) {
const keys = [`agent:${params.idempotencyKey}`];
const approvalId = params.execApprovalFollowupApprovalId?.trim();
if (approvalId) keys.push(`agent:exec-approval-followup:${approvalId}`);
return uniqueStrings(keys);
}
function readGatewayDedupeEntry(params) {
for (const key of params.keys) {
const entry = params.dedupe.get(key);
if (entry) return entry;
}
}
function isAcceptedAgentDedupePayload(payload) {
return typeof payload === "object" && payload !== null && payload.status === "accepted";
}
function isPreRegistrationAbortedAgentDedupePayload(payload) {
const stopReason = payload?.stopReason;
return typeof payload === "object" && payload !== null && payload.status === "timeout" && (stopReason === "rpc" || stopReason === "stop");
}
function isPreRegistrationAbortedAgentDedupeEntryForSession(params) {
if (!params.entry?.ok || !isPreRegistrationAbortedAgentDedupePayload(params.entry.payload)) return false;
const payload = params.entry.payload;
const payloadRunId = typeof payload.runId === "string" ? payload.runId.trim() : "";
if (payloadRunId && payloadRunId !== params.runId) return false;
const payloadSessionKey = typeof payload.sessionKey === "string" && payload.sessionKey.trim() ? payload.sessionKey.trim() : void 0;
const expectedSessionKeys = new Set([params.sessionKey, ...params.alternateSessionKeys ?? []].filter((value) => Boolean(value?.trim())));
return !payloadSessionKey || expectedSessionKeys.size === 0 || expectedSessionKeys.has(payloadSessionKey);
}
function setGatewayDedupeEntries(params) {
for (const key of params.keys) setGatewayDedupeEntry({
dedupe: params.dedupe,
key,
entry: params.entry
});
}
function setAbortedAgentDedupeEntries(params) {
setGatewayDedupeEntries({
dedupe: params.dedupe,
keys: params.keys,
entry: {
ts: Date.now(),
ok: true,
payload: {
runId: params.runId,
...params.agentId ? { agentId: params.agentId } : {},
status: "timeout",
summary: "aborted",
stopReason: params.stopReason,
timeoutPhase: "queue",
providerStarted: false
}
}
});
}
function readAgentRunTimeoutAttribution(meta) {
const record = meta && typeof meta === "object" && !Array.isArray(meta) ? meta : void 0;
return {
timeoutPhase: normalizeAgentRunTimeoutPhase(record?.timeoutPhase),
providerStarted: asBoolean(record?.providerStarted)
};
}
function isGatewayAbortSignalReason(reason) {
return reason === void 0 || isAbortError(reason) || readErrorName(reason) === "TimeoutError";
}
function isGatewayAgentAbortRejection(error, signal) {
if (!signal.aborted) return false;
if (readErrorName(signal.reason) === "TimeoutError") return true;
if (!isGatewayAbortSignalReason(signal.reason)) return false;
return isAbortError(error) || readErrorName(error) === "TimeoutError";
}
function resolveGatewayAgentAbortStopReason(signal) {
return readErrorName(signal.reason) === "TimeoutError" ? "timeout" : "rpc";
}
function resolveAbortedAgentStopReason(entry) {
return entry?.abortStopReason?.trim() || "rpc";
}
function deleteGatewayDedupeEntries(params) {
for (const key of params.keys) params.dedupe.delete(key);
}
function dispatchAgentRunFromGateway(params) {
const shouldTrackTask = params.taskTrackingMode === "cli";
let taskTracked = false;
if (shouldTrackTask) try {
taskTracked = Boolean(createRunningTaskRun({
runtime: "cli",
sourceId: params.runId,
ownerKey: params.ingressOpts.sessionKey,
scopeKind: "session",
requesterOrigin: normalizeDeliveryContext({
channel: params.ingressOpts.channel,
to: params.ingressOpts.to,
accountId: params.ingressOpts.accountId,
threadId: params.ingressOpts.threadId
}),
childSessionKey: params.ingressOpts.sessionKey,
runId: params.runId,
task: params.ingressOpts.message,
deliveryStatus: "not_applicable",
startedAt: Date.now()
}));
} catch (err) {
params.context.logGateway.warn(`failed to start tracked agent task ${params.runId}: ${formatForLog(err)}`);
}
agentCommandFromIngress(params.ingressOpts, defaultRuntime, params.context.deps).then((result) => {
const aborted = result?.meta?.aborted === true;
const timeoutAttribution = readAgentRunTimeoutAttribution(result?.meta);
if (taskTracked) tryFinalizeTrackedAgentTask({
runId: params.runId,
status: aborted ? "timed_out" : "succeeded",
terminalSummary: aborted ? "aborted" : "completed",
log: params.context.logGateway
});
const payload = {
runId: params.runId,
status: aborted ? "timeout" : "ok",
summary: aborted ? "aborted" : "completed",
...aborted ? { stopReason: result?.meta?.stopReason ?? "rpc" } : {},
...aborted && timeoutAttribution.timeoutPhase ? { timeoutPhase: timeoutAttribution.timeoutPhase } : {},
...aborted && timeoutAttribution.providerStarted !== void 0 ? { providerStarted: timeoutAttribution.providerStarted } : {},
result
};
setGatewayDedupeEntries({
dedupe: params.context.dedupe,
keys: params.dedupeKeys,
entry: {
ts: Date.now(),
ok: true,
payload
}
});
params.respond(true, payload, void 0, { runId: params.runId });
}).catch((err) => {
const aborted = isGatewayAgentAbortRejection(err, params.abortController.signal);
const renderedErr = formatForLog(err);
if (taskTracked) tryFinalizeTrackedAgentTask({
runId: params.runId,
status: aborted ? "timed_out" : resolveFailedTrackedAgentTaskStatus(err),
error: renderedErr,
terminalSummary: renderedErr,
log: params.context.logGateway
});
const error = errorShape(ErrorCodes.UNAVAILABLE, renderedErr);
const stopReason = resolveGatewayAgentAbortStopReason(params.abortController.signal);
const payload = {
runId: params.runId,
status: aborted ? "timeout" : "error",
summary: aborted ? "aborted" : renderedErr,
...aborted ? {
stopReason,
timeoutPhase: "gateway_draining"
} : {}
};
setGatewayDedupeEntries({
dedupe: params.context.dedupe,
keys: params.dedupeKeys,
entry: {
ts: Date.now(),
ok: aborted,
payload,
...aborted ? {} : { error }
}
});
params.respond(aborted, payload, aborted ? void 0 : error, {
runId: params.runId,
...aborted ? {} : { error: formatForLog(err) }
});
}).finally(() => {
if (params.context.chatAbortControllers.get(params.runId)?.controller === params.abortController) params.context.chatAbortControllers.delete(params.runId);
});
}
function shouldSuppressAgentPromptPersistence(params) {
if (params.inputProvenance?.kind !== "inter_session" || params.inputProvenance.sourceTool !== "subagent_announce") return false;
return params.internalEvents?.some((event) => event.type === "task_completion" && event.source === "subagent") === true;
}
function yieldAfterAgentAcceptedAck() {
return new Promise((resolve) => {
setTimeout(resolve, 10);
});
}
const agentHandlers = {
agent: async ({ params, respond, context, client, isWebchatConnect }) => {
const p = params;
if (!validateAgentParams(p)) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: ${formatValidationErrors(validateAgentParams.errors)}`));
return;
}
const request = p;
const allowModelOverride = resolveAllowModelOverrideFromClient(client);
const canUseInternalRuntimeHandoff = resolveCanUseInternalRuntimeHandoff(client);
const requestedModelOverride = Boolean(request.provider || request.model);
const requestedInternalSessionEffects = request.sessionEffects === "internal";
const requestedPromptPersistenceSuppression = request.suppressPromptPersistence === true;
const isRawModelRun = request.modelRun === true || request.promptMode === "none";
if (requestedModelOverride && !allowModelOverride) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "provider/model overrides are not authorized for this caller."));
return;
}
if ((requestedInternalSessionEffects || requestedPromptPersistenceSuppression) && !canUseInternalRuntimeHandoff) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "internal session-effect controls are reserved for backend callers."));
return;
}
const providerOverride = allowModelOverride ? request.provider : void 0;
const modelOverride = allowModelOverride ? request.model : void 0;
const cfg = context.getRuntimeConfig();
const idem = request.idempotencyKey;
const runId = idem;
const execApprovalFollowupApprovalId = parseExecApprovalFollowupApprovalId(idem);
if (execApprovalFollowupApprovalId && !canUseInternalRuntimeHandoff) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "exec approval followup idempotency keys are reserved for backend callers."));
return;
}
const normalizedSpawned = normalizeSpawnedRunMetadata({
groupId: request.groupId,
groupChannel: request.groupChannel,
groupSpace: request.groupSpace
});
let resolvedGroupId = normalizedSpawned.groupId;
let resolvedGroupChannel = normalizedSpawned.groupChannel;
let resolvedGroupSpace = normalizedSpawned.groupSpace;
let spawnedByValue;
const inputProvenance = normalizeInputProvenance(request.inputProvenance);
const preserveUserFacingSessionModelState = canUseInternalRuntimeHandoff && shouldPreserveUserFacingSessionStateForInputProvenance(inputProvenance);
const sessionEffects = requestedInternalSessionEffects ? "internal" : request.sessionEffects;
const suppressVisibleSessionEffects = sessionEffects === "internal";
const agentDedupeKeys = resolveAgentDedupeKeys({
idempotencyKey: idem,
execApprovalFollowupApprovalId
});
const cached = readGatewayDedupeEntry({
dedupe: context.dedupe,
keys: agentDedupeKeys
});
if (cached) {
if (cached.ok && isAcceptedAgentDedupePayload(cached.payload)) {
const cachedRunId = typeof cached.payload.runId === "string" && cached.payload.runId.trim() ? cached.payload.runId.trim() : runId;
const cachedSessionKey = typeof cached.payload.sessionKey === "string" && cached.payload.sessionKey.trim() ? cached.payload.sessionKey.trim() : void 0;
const cachedAgentId = cachedSessionKey === "global" && typeof cached.payload.agentId === "string" && cached.payload.agentId.trim() ? cached.payload.agentId.trim() : void 0;
respond(true, {
runId: cachedRunId,
status: "in_flight",
...cachedSessionKey ? { sessionKey: cachedSessionKey } : {},
...cachedAgentId ? { agentId: cachedAgentId } : {}
}, void 0, {
cached: true,
runId: cachedRunId
});
return;
}
respond(cached.ok, cached.payload, cached.error, { cached: true });
return;
}
let agentDedupeReserved = false;
let agentRunAccepted = false;
const ownerConnId = typeof client?.connId === "string" ? client.connId : void 0;
const ownerDeviceId = typeof client?.connect?.device?.id === "string" ? client.connect.device.id : void 0;
const reservePreAcceptedAgentDedupe = (sessionKey, dedupeAgentId) => {
if (agentDedupeReserved || !sessionKey) return;
const dedupeSessionResolvesGlobal = resolveSessionStoreKey({
cfg,
sessionKey
}) === "global";
const acceptedAt = Date.now();
const pendingTimeoutMs = resolveAgentTimeoutMs({
cfg,
overrideSeconds: typeof request.timeout === "number" ? request.timeout : void 0
});
setGatewayDedupeEntries({
dedupe: context.dedupe,
keys: agentDedupeKeys,
entry: {
ts: acceptedAt,
ok: true,
payload: {
runId,
status: "accepted",
sessionKey,
...dedupeSessionResolvesGlobal && dedupeAgentId ? { agentId: dedupeAgentId } : {},
controlUiVisible: !suppressVisibleSessionEffects,
acceptedAt,
dedupeKeys: agentDedupeKeys,
expiresAtMs: resolveAgentRunExpiresAtMs({
now: acceptedAt,
timeoutMs: pendingTimeoutMs
}),
ownerConnId,
ownerDeviceId
}
}
});
agentDedupeReserved = true;
};
const clearUnacceptedExecApprovalFollowupDedupe = () => {
if (!agentDedupeReserved || agentRunAccepted) return;
if (isPreRegistrationAbortedAgentDedupeEntryForSession({
entry: readGatewayDedupeEntry({
dedupe: context.dedupe,
keys: agentDedupeKeys
}),
runId
})) return;
deleteGatewayDedupeEntries({
dedupe: context.dedupe,
keys: agentDedupeKeys
});
agentDedupeReserved = false;
};
const normalizedAttachments = normalizeRpcAttachmentsToChatAttachments(request.attachments);
const requestedBestEffortDeliver = typeof request.bestEffortDeliver === "boolean" ? request.bestEffortDeliver : void 0;
const knownAgents = listAgentIds(cfg);
const agentIdRaw = normalizeOptionalString(request.agentId) ?? "";
let agentId = agentIdRaw ? normalizeAgentId(agentIdRaw) : void 0;
if (agentId && !knownAgents.includes(agentId)) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: unknown agent id "${request.agentId}"`));
return;
}
const requestedSessionKeyRaw = normalizeOptionalString(request.sessionKey);
if (requestedSessionKeyRaw && classifySessionKeyShape(requestedSessionKeyRaw) === "malformed_agent") {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: malformed session key "${requestedSessionKeyRaw}"`));
return;
}
if (!agentId && requestedSessionKeyRaw) {
const parsed = parseAgentSessionKey(requestedSessionKeyRaw);
const inferredAgentId = parsed && resolveSessionStoreKey({
cfg,
sessionKey: requestedSessionKeyRaw
}) === "global" ? normalizeAgentId(parsed.agentId) : void 0;
if (inferredAgentId) {
if (!knownAgents.includes(inferredAgentId)) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: unknown agent id "${parsed?.agentId}"`));
return;
}
agentId = inferredAgentId;
}
}
if (execApprovalFollowupApprovalId && requestedSessionKeyRaw) {
const expectedSessionId = normalizeOptionalString(request.execApprovalFollowupExpectedSessionId);
let currentSessionId;
try {
currentSessionId = normalizeOptionalString(loadSessionEntry(requestedSessionKeyRaw).entry?.sessionId);
} catch {
currentSessionId = void 0;
}
if (isExecApprovalFollowupSessionRebound({
expectedSessionId,
resolvedSessionId: currentSessionId
})) {
context.logGateway.info(`Dropping stale exec approval followup ${execApprovalFollowupApprovalId}: session ${requestedSessionKeyRaw} rebound (expected ${expectedSessionId}, current ${currentSessionId}) before the approval resolved`);
const droppedPayload = {
runId,
status: "ok",
summary: "exec approval followup dropped: session was reset before the approval resolved"
};
setGatewayDedupeEntries({
dedupe: context.dedupe,
keys: agentDedupeKeys,
entry: {
ts: Date.now(),
ok: true,
payload: droppedPayload
}
});
respond(true, droppedPayload, void 0, { runId });
return;
}
}
const requestedSessionId = normalizeOptionalString(request.sessionId);
let requestedSessionKey = requestedSessionKeyRaw ?? (!requestedSessionId ? resolveExplicitAgentSessionKey({
cfg,
agentId
}) : void 0);
if (agentId && requestedSessionKeyRaw) {
const parsedRequestedSessionKey = parseAgentSessionKey(requestedSessionKeyRaw);
const requestedCanonicalKey = resolveSessionStoreKey({
cfg,
sessionKey: requestedSessionKeyRaw
});
const sessionAgentId = parsedRequestedSessionKey?.agentId ? normalizeAgentId(parsedRequestedSessionKey.agentId) : requestedCanonicalKey === "global" ? agentId : resolveAgentIdFromSessionKey(requestedSessionKeyRaw);
if (sessionAgentId !== agentId) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: agent "${request.agentId}" does not match session key agent "${sessionAgentId}"`));
return;
}
}
const preAcceptedReservedSessionKey = requestedSessionKey && resolveSessionStoreKey({
cfg,
sessionKey: requestedSessionKey
}) === "global" ? "global" : requestedSessionKey;
reservePreAcceptedAgentDedupe(preAcceptedReservedSessionKey, agentId);
try {
let message = (request.message ?? "").trim();
if (!isRawModelRun) message = annotateInterSessionPromptText(message, inputProvenance);
let images = [];
let imageOrder = [];
if (normalizedAttachments.length > 0) {
let baseProvider;
let baseModel;
let requestedAcpMeta;
if (requestedSessionKeyRaw) {
const { cfg: sessCfg, entry: sessEntry, canonicalKey: sessCanonicalKey } = loadSessionEntry(requestedSessionKeyRaw, {
...agentId ? { agentId } : {},
clone: false
});
const modelRef = resolveSessionModelRef(sessCfg, sessEntry, sessCanonicalKey === "global" && agentId ? agentId : resolveAgentIdFromSessionKey(sessCanonicalKey));
baseProvider = modelRef.provider;
baseModel = modelRef.model;
requestedAcpMeta = readAcpSessionMeta({ sessionKey: sessCanonicalKey });
}
const effectiveProvider = providerOverride || baseProvider;
const effectiveModel = modelOverride || baseModel;
const supportsInlineImages = request.acpTurnSource === "manual_spawn" && isAcpSessionKey(requestedSessionKeyRaw) && requestedAcpMeta != null ? true : await resolveGatewayModelSupportsImages({
loadGatewayModelCatalog: context.loadGatewayModelCatalog,
provider: effectiveProvider,
model: effectiveModel
});
try {
const parsed = await parseMessageWithAttachments(message, normalizedAttachments, {
maxBytes: resolveChatAttachmentMaxBytes(cfg),
log: context.logGateway,
supportsInlineImages,
acceptNonImage: false
});
message = parsed.message.trim();
images = parsed.images;
imageOrder = parsed.imageOrder;
} catch (err) {
logAttachmentFailure(context.logGateway, "agent attachment parse failed", err);
respond(false, void 0, errorShape(err instanceof MediaOffloadError ? ErrorCodes.UNAVAILABLE : ErrorCodes.INVALID_REQUEST, String(err)));
return;
}
}
const isKnownGatewayChannel = (value) => isGatewayMessageChannel(value) || isInternalNonDeliveryChannel(value);
const channelHints = normalizeStringEntries([request.channel, request.replyChannel].filter((value) => typeof value === "string"));
for (const rawChannel of channelHints) {
const normalized = normalizeMessageChannel(rawChannel);
if (normalized && normalized !== "last" && !isKnownGatewayChannel(normalized)) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent params: unknown channel: ${normalized}`));
return;
}
}
const voiceWakeTrigger = normalizeOptionalString(request.voiceWakeTrigger) ?? "";
const replyTo = normalizeOptionalString(request.replyTo) ?? "";
const to = normalizeOptionalString(request.to) ?? "";
const explicitVoiceWakeSessionTarget = !agentId && requestedSessionKeyRaw ? (() => {
const { cfg: sessionCfg, canonicalKey } = loadSessionEntry(requestedSessionKeyRaw, { clone: false });
const routedAgentId = resolveAgentIdFromSessionKey(canonicalKey);
if (routedAgentId !== normalizeAgentId(resolveDefaultAgentId(sessionCfg))) return true;
return canonicalKey !== resolveAgentMainSessionKey({
cfg: sessionCfg,
agentId: routedAgentId
});
})() : false;
const canAutoRouteVoiceWake = !agentId && !explicitVoiceWakeSessionTarget && !requestedSessionId && !replyTo && !to;
if (Object.hasOwn(request, "voiceWakeTrigger") && canAutoRouteVoiceWake) try {
const routingConfig = await loadVoiceWakeRoutingConfig();
const route = resolveVoiceWakeRouteByTrigger({
trigger: voiceWakeTrigger || void 0,
config: routingConfig
});
if ("agentId" in route) if (knownAgents.includes(route.agentId)) {
agentId = route.agentId;
requestedSessionKey = resolveExplicitAgentSessionKey({
cfg,
agentId
});
} else context.logGateway.warn(`voicewake routing ignored unknown agentId="${route.agentId}" trigger="${voiceWakeTrigger}"`);
else if ("sessionKey" in route) if (classifySessionKeyShape(route.sessionKey) !== "malformed_agent") {
const canonicalRouteSession = loadSessionEntry(route.sessionKey, { clone: false }).canonicalKey;
const routedAgentId = resolveAgentIdFromSessionKey(canonicalRouteSession);
if (knownAgents.includes(routedAgentId)) {
requestedSessionKey = canonicalRouteSession;
agentId = routedAgentId;
} else context.logGateway.warn(`voicewake routing ignored unknown session agent="${routedAgentId}" sessionKey="${canonicalRouteSession}" trigger="${voiceWakeTrigger}"`);
} else context.logGateway.warn(`voicewake routing ignored malformed sessionKey="${route.sessionKey}" trigger="${voiceWakeTrigger}"`);
} catch (err) {
context.logGateway.warn(`voicewake routing load failed: ${formatForLog(err)}`);
}
let resolvedSessionId = requestedSessionId;
let sessionEntry;
let bestEffortDeliver = requestedBestEffortDeliver ?? false;
let cfgForAgent;
let resolvedSessionKey = requestedSessionKey;
let resolvedSessionAgentId;
let isNewSession = false;
let skipAgentInitialSessionTouch = false;
const resetCommandMatch = message.match(RESET_COMMAND_RE);
if (resetCommandMatch && requestedSessionKey) {
const postResetMessage = normalizeOptionalString(resetCommandMatch[2]) ?? "";
if (!clientHasAdminScope(client)) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `missing scope: ${ADMIN_SCOPE}`));
return;
}
const resetReason = normalizeOptionalLowercaseString(resetCommandMatch[1]) === "new" ? "new" : "reset";
const resetResult = await runSessionResetFromAgent({
key: requestedSessionKey,
...requestedSessionKey === "global" && agentId ? { agentId } : {},
reason: resetReason
});
if (!resetResult.ok) {
respond(false, void 0, resetResult.error);
return;
}
requestedSessionKey = resetResult.key;
resolvedSessionId = resetResult.sessionId ?? resolvedSessionId;
if (postResetMessage) message = postResetMessage;
else {
let resetAckResult;
try {
const deliverySession = request.deliver === true ? loadBareSessionResetDeliverySession({
cfg,
sessionKey: resetResult.key,
...agentId ? { agentId } : {}
}) : void 0;
resetAckResult = await resolveBareSessionResetResult({
cfg: deliverySession?.cfg ?? cfg,
context,
reason: resetReason,
sessionId: resetResult.sessionId,
sessionKey: resetResult.key,
agentId: deliverySession?.agentId ?? agentId,
sessionEntry: deliverySession?.entry,
request,
runId
});
} catch (err) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, formatForLog(err)));
return;
}
const responsePayload = buildBareSessionResetResponse({
runId,
result: resetAckResult
});
agentRunAccepted = true;
setGatewayDedupeEntries({
dedupe: context.dedupe,
keys: agentDedupeKeys,
entry: {
ts: Date.now(),
ok: true,
payload: responsePayload
}
});
respond(true, responsePayload, void 0, { runId });
emitSessionsChanged(context, {
sessionKey: resetResult.key,
...resetResult.key === "global" && agentId ? { agentId } : {},
reason: resetReason
});
return;
}
}
if (requestedSessionKey) {
const sessionLoadOptions = {
...agentId ? { agentId } : {},
clone: false
};
const { cfg: cfgLocal, storePath, entry, canonicalKey } = loadSessionEntry(requestedSessionKey, sessionLoadOptions);
cfgForAgent = cfgLocal;
const sessionMaintenanceConfig = resolveMaintenanceConfigFromInput(cfgLocal.session?.maintenance);
const canonicalSessionAgentId = canonicalKey === "global" ? agentId ?? resolveDefaultAgentId(cfgLocal) : resolveAgentIdFromSessionKey(canonicalKey);
const now = Date.now();
const resetPolicy = resolveSessionResetPolicy({
sessionCfg: cfgLocal.session,
resetType: resolveSessionResetType({ sessionKey: canonicalKey }),
resetOverride: resolveChannelResetConfig({
sessionCfg: cfgLocal.session,
channel: entry?.lastChannel ?? entry?.channel ?? request.channel
})
});
const lifecycleTimestamps = entry ? resolveSessionLifecycleTimestamps({
entry,
storePath,
agentId: canonicalSessionAgentId
}) : void 0;
let freshness = entry ? evaluateSessionFreshness({
updatedAt: entry.updatedAt,
...lifecycleTimestamps,
now,
policy: resetPolicy
}) : void 0;
const resolveFailedSessionTranscriptMissingForEntry = (candidateEntry) => {
if (candidateEntry?.status !== "failed" || !candidateEntry.sessionId?.trim()) return false;
try {
const sessionPathOpts = resolveSessionFilePathOptions({
storePath,
agentId: canonicalSessionAgentId
});
return !existsSync(resolveSessionFilePath(candidateEntry.sessionId, candidateEntry, sessionPathOpts));
} catch {
return true;
}
};
const failedSessionTranscriptMissing = resolveFailedSessionTranscriptMissingForEntry(entry);
const mainSessionKeyForRequest = resolveAgentMainSessionKey({
cfg: cfgLocal,
agentId: canonicalSessionAgentId
});
const isSystemGatewayRun = request.bootstrapContextRunKind === "cron" || request.bootstrapContextRunKind === "heartbeat";
const requestedSessionMatchesEntry = Boolean(requestedSessionId && entry?.sessionId?.trim() === requestedSessionId);
const terminalMainTranscriptNewerThanRegistry = (isSystemGatewayRun || requestedSessionMatchesEntry ? void 0 : resolveTerminalMainSessionTranscriptRegistryCheck({
entry,
sessionScope: cfgLocal.session?.scope,
sessionKey: canonicalKey,
agentId: canonicalSessionAgentId,
mainKey: cfgLocal.session?.mainKey,
storePath
})) ? hasTerminalMainSessionTranscriptNewerThanRegistrySync({
entry,
sessionScope: cfgLocal.session?.scope,
sessionKey: canonicalKey,
agentId: canonicalSessionAgentId,
mainKey: cfgLocal.session?.mainKey,
storePath
}) : false;
const canReuseSession = Boolean(entry?.sessionId) && (freshness?.fresh ?? false) && !failedSessionTranscriptMissing && !terminalMainTranscriptNewerThanRegistry;
let usableRequestedSessionId = requestedSessionId && (!entry?.sessionId || canReuseSession) ? requestedSessionId : void 0;
const sessionId = usableRequestedSessionId ? usableRequestedSessionId : (canReuseSession ? entry?.sessionId : void 0) ?? randomUUID();
isNewSession = !entry || !canReuseSession && !usableRequestedSessionId || Boolean(usableRequestedSessionId && entry?.sessionId !== usableRequestedSessionId);
let rotatedSessionId = Boolean(entry?.sessionId && entry.sessionId !== sessionId);
const touchInteraction = !isSystemGatewayRun && !request.internalEvents?.length;
const sessionAgent = canonicalSessionAgentId;
const requestDeliveryHint = normalizeDeliveryContext({
channel: request.channel?.trim(),
to: request.to?.trim(),
accountId: request.accountId?.trim(),
threadId: request.threadId
});
const buildSessionPatch = (freshEntry) => {
const freshSpawnedBy = canonicalizeSpawnedByForAgent(cfgLocal, sessionAgent, freshEntry?.spawnedBy);
const storedGroup = normalizeTrustedGroupMetadata(freshEntry);
let inheritedGroup;
if (freshSpawnedBy && (!storedGroup.groupId || !storedGroup.groupChannel || !storedGroup.groupSpace)) try {
const parentEntry = loadSessionEntry(freshSpawnedBy)?.entry;
inheritedGroup = normalizeTrustedGroupMetadata({
groupId: parentEntry?.groupId,
groupChannel: parentEntry?.groupChannel,
groupSpace: parentEntry?.space
});
} catch {
inheritedGroup = void 0;
}
const trustedGroup = resolveTrustedGroupMetadata({
sessionKey: canonicalKey,
spawnedBy: freshSpawnedBy,
stored: storedGroup,
inherited: inheritedGroup
});
const nextGroup = (trustedGroup.groupId ? resolveTrustedGroupId({
groupId: trustedGroup.groupId,
sessionKey: canonicalKey,
spawnedBy: freshSpawnedBy
}) : void 0)?.dropped === true ? {
groupId: void 0,
groupChannel: void 0,
groupSpace: void 0
} : (() => {
const trustRequestSelectors = Boolean(trustedGroup.groupId) && requestGroupMatchesTrusted({
requestGroupId: normalizedSpawned.groupId,
trustedGroupId: trustedGroup.groupId
});
return {
groupId: trustedGroup.groupId,
groupChannel: trustedGroup.groupChannel ?? (trustRequestSelectors ? normalizedSpawned.groupChannel : void 0),
groupSpace: trustedGroup.groupSpace ?? (trustRequestSelectors ? normalizedSpawned.groupSpace : void 0)
};
})();
const deliveryFields = normalizeSessionDeliveryFields(freshEntry);
const effectiveDelivery = mergeDeliveryContext(deliveryFields.deliveryContext, requestDeliveryHint);
const effectiveDeliveryFields = normalizeSessionDeliveryFields({
route: deliveryFields.route,
deliveryContext: effectiveDelivery
});
const labelValue = normalizeOptionalString(request.label) || freshEntry?.label;
const channelValue = freshEntry?.channel ?? request.channel?.trim();
const pluginOwnerId = freshEntry === void 0 ? normalizeOptionalString(client?.internal?.pluginRuntimeOwnerId) : void 0;
const freshSessionRotatedSinceLoad = Boolean(entry?.sessionId && freshEntry?.sessionId && freshEntry.sessionId !== entry.sessionId);
const freshLifecycleTimestamps = freshEntry ? resolveSessionLifecycleTimestamps({
entry: freshEntry,
storePath,
agentId: sessionAgent
}) : void 0;
const freshFreshness = freshEntry ? evaluateSessionFreshness({
updatedAt: freshEntry.updatedAt,
...freshLifecycleTimestamps,
now,
policy: resetPolicy
}) : void 0;
const freshRequestedSessionMatchesEntry = Boolean(requestedSessionId && freshEntry?.sessionId?.trim() === requestedSessionId);
const freshTerminalMainTranscriptNewerThanRegistry = isSystemGatewayRun || freshRequestedSessionMatchesEntry ? false : hasTerminalMainSessionTranscriptNewerThanRegistrySync({
entry: freshEntry,
sessionScope: cfgLocal.session?.scope,
sessionKey: canonicalKey,
agentId: sessionAgent,
mainKey: cfgLocal.session?.mainKey,
storePath
});
const freshFailedSessionTranscriptMissing = resolveFailedSessionTranscriptMissingForEntry(freshEntry);
const freshCanReuseSession = Boolean(freshEntry?.sessionId) && (freshFreshness?.fresh ?? false) && !freshFailedSessionTranscriptMissing && !freshTerminalMainTranscriptNewerThanRegistry;
const freshUsableRequestedSessionId = requestedSessionId && (!freshEntry?.sessionId || freshCanReuseSession) ? requestedSessionId : void 0;
const freshSessionId = freshUsableRequestedSessionId ? freshUsableRequestedSessionId : (freshCanReuseSession ? freshEntry?.sessionId : void 0) ?? sessionId;
const freshIsNewSession = !freshEntry || !freshCanReuseSession && !freshUsableRequestedSessionId || Boolean(freshUsableRequestedSessionId && freshEntry?.sessionId !== freshUsableRequestedSessionId);
const freshRotatedSessionId = Boolean(freshEntry?.sessionId && freshEntry.sessionId !== freshSessionId);
const patchSessionId = freshSessionRotatedSinceLoad ? freshEntry?.sessionId : freshSessionId;
const shouldClearRotatedState = freshRotatedSessionId && !freshSessionRotatedSinceLoad;
const patch = {
sessionId: patchSessionId,
updatedAt: now,
...freshIsNewSession && !freshSessionRotatedSinceLoad ? { sessionStartedAt: now } : {},
...touchInteraction ? { lastInteractionAt: now } : {},
...effectiveDeliveryFields.route ? { route: effectiveDeliveryFields.route } : {},
...effectiveDeliveryFields.deliveryContext ? { deliveryContext: effectiveDeliveryFields.deliveryContext } : {},
...effectiveDeliveryFields.lastChannel ? { lastChannel: effectiveDeliveryFields.lastChannel } : {},
...effectiveDeliveryFields.lastTo ? { lastTo: effectiveDeliveryFields.lastTo } : {},
...effectiveDeliveryFields.lastAccountId ? { lastAccountId: effectiveDeliveryFields.lastAccountId } : {},
...effectiveDeliveryFields.lastThreadId != null ? { lastThreadId: effectiveDeliveryFields.lastThreadId } : {},
...labelValue ? { label: labelValue } : {},
...freshSpawnedBy ? { spawnedBy: freshSpawnedBy } : {},
...channelValue ? { channel: channelValue } : {},
groupId: nextGroup.groupId,
groupChannel: nextGroup.groupChannel,
space: nextGroup.groupSpace,
...pluginOwnerId ? { pluginOwnerId } : {},
...shouldClearRotatedState ? {
status: void 0,
startedAt: void 0,
endedAt: void 0,
runtimeMs: void 0,
abortedLastRun: void 0,
sessionFile: void 0
} : {}
};
if (shouldClearRotatedState) clearAllCliSessions(patch);
return {
patch,
spawnedBy: freshSpawnedBy,
groupId: nextGroup.groupId,
groupChannel: nextGroup.groupChannel,
groupSpace: nextGroup.groupSpace,
freshSessionRotatedSinceLoad,
isNewSession: freshIsNewSession,
rotatedSessionId: freshRotatedSessionId,
usableRequestedSessionId: freshUsableRequestedSessionId,
freshness: freshFreshness
};
};
let patchBuild = buildSessionPatch(entry);
isNewSession = patchBuild.isNewSession;
rotatedSessionId = patchBuild.rotatedSessionId;
usableRequestedSessionId = patchBuild.usableRequestedSessionId;
freshness = patchBuild.freshness;
sessionEntry = mergeSessionEntry(entry, patchBuild.patch);
resolvedSessionId = sessionEntry?.sessionId ?? sessionId;
const canonicalSessionKey = canonicalKey;
resolvedSessionKey = canonicalSessionKey;
const sessionAgentId = canonicalSessionAgentId;
resolvedSessionAgentId = sessionAgentId;
const mainSessionKey = mainSessionKeyForRequest;
const recoveredSessionStartedAt = !isNewSession && entry !== void 0 && entry.sessionStartedAt === void 0 ? resolveSessionLifecycleTimestamps({
entry,
storePath,
agentId: sessionAgentId
}).sessionStartedAt : void 0;
if (storePath && !suppressVisibleSessionEffects) {
const requestedStoreKey = requestedSessionKey;
let deniedBySendPolicy = false;
let singleEntryPersistence;
const persisted = await updateSessionStore(storePath, (store) => {
const storeKeysBeforeMigration = new Set(Object.keys(store));
const preMigrationTarget = resolveGatewaySessionStoreTarget({
cfg: cfgLocal,
key: requestedStoreKey,
store,
...sessionAgentId ? { agentId: sessionAgentId } : {}
});
const hadLegacyStoreKey = preMigrationTarget.storeKeys.some((storeKey) => storeKey !== preMigrationTarget.canonicalKey && Object.hasOwn(store, storeKey));
const { target, primaryKey } = migrateAndPruneGatewaySessionStoreKey({
cfg: cfgLocal,
key: requestedStoreKey,
store
});
const prunedStoreKey = [...storeKeysBeforeMigration].some((storeKey) => !Object.hasOwn(store, storeKey));
const freshEntry = store[primaryKey];
patchBuild = buildSessionPatch(freshEntry);
const merged = mergeSessionEntry(freshEntry, recoveredSessionStartedAt !== void 0 && freshEntry?.sessionStartedAt === void 0 && freshEntry?.sessionId === entry?.sessionId ? {
...patchBuild.patch,
sessionStartedAt: recoveredSessionStartedAt
} : patchBuild.patch);
if ((request.deliver === true ? resolveSendPolicy({
cfg: cfgLocal,
entry: merged,
sessionKey: canonicalKey,
channel: merged?.channel,
chatType: merged?.chatType
}) : "allow") === "deny") {
deniedBySendPolicy = true;
return merged;
}
store[primaryKey] = merged;
const canonicalKeyChanged = target.canonicalKey !== preMigrationTarget.canonicalKey;
singleEntryPersistence = freshEntry && !hadLegacyStoreKey && !canonicalKeyChanged && !prunedStoreKey ? {
sessionKey: primaryKey,
entry: merged
} : void 0;
return merged;
}, {
takeCacheOwnership: true,
maintenanceConfig: sessionMaintenanceConfig,
resolveSingleEntryPersistence: () => singleEntryPersistence
});
if (persisted) {
sessionEntry = persisted;
resolvedSessionId = sessionEntry.sessionId;
}
skipAgentInitialSessionTouch = touchInteraction;
if (deniedBySendPolicy) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "send blocked by session policy"));
return;
}
}
isNewSession = patchBuild.isNewSession;
rotatedSessionId = patchBuild.rotatedSessionId;
usableRequestedSessionId = patchBuild.usableRequestedSessionId;
freshness = patchBuild.freshness;
spawnedByValue = patchBuild.spawnedBy;
resolvedGroupId = patchBuild.groupId;
resolvedGroupChannel = patchBuild.groupChannel;
resolvedGroupSpace = patchBuild.groupSpace;
if (!suppressVisibleSessionEffects && isNewSession && resolvedSessionId && storePath && !patchBuild.freshSessionRotatedSinceLoad) {
const previousSessionId = rotatedSessionId ? entry?.sessionId : void 0;
emitAgentSendSessionLifecycleTransition({
cfg: cfgLocal,
sessionKey: canonicalSessionKey,
sessionId: resolvedSessionId,
storePath,
sessionFile: sessionEntry?.sessionFile,
agentId: sessionAgentId,
previousSessionId,
previousSessionFile: previousSessionId ? entry?.sessionFile : void 0,
previousEndReason: previousSessionId ? freshness?.staleReason ?? (usableRequestedSessionId && entry?.sessionId !== usableRequestedSessionId ? "new" : "unknown") : void 0
});
}
if (request.deliver === true) {
if (resolveSendPolicy({
cfg: cfgLocal,
entry: sessionEntry,
sessionKey: canonicalKey,
channel: sessionEntry?.channel,
chatType: sessionEntry?.chatType
}) === "deny") {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "send blocked by session policy"));
return;
}
}
if (!suppressVisibleSessionEffects && (canonicalSessionKey === mainSessionKey || canonicalSessionKey === "global")) {
const selectedGlobalAgentId = canonicalSessionKey === "global" ? sessionAgentId : void 0;
context.addChatRun(idem, {
sessionKey: canonicalSessionKey,
...selectedGlobalAgentId ? { agentId: selectedGlobalAgentId } : {},
clientRunId: idem
});
if (requestedBestEffortDeliver === void 0) bestEffortDeliver = true;
}
registerAgentRunContext(idem, suppressVisibleSessionEffects ? { isControlUiVisible: false } : { sessionKey: canonicalSessionKey });
}
const activeSessionAgentId = resolvedSessionKey === "global" && resolvedSessionAgentId ? resolvedSessionAgentId : resolvedSessionKey ? resolveAgentIdFromSessionKey(resolvedSessionKey) : agentId ?? resolveDefaultAgentId(cfgForAgent ?? cfg);
const connId = typeof client?.connId === "string" ? client.connId : void 0;
const wantsToolEvents = hasGatewayClientCap(client?.connect?.caps, GATEWAY_CLIENT_CAPS.TOOL_EVENTS);
if (connId && wantsToolEvents) {
context.registerToolEventRecipient(runId, connId);
for (const [activeRunId, active] of context.chatAbortControllers) {
const sameSession = active.sessionKey === resolvedSessionKey;
const sameSelectedGlobalAgent = resolvedSessionKey === "global" ? active.agentId === activeSessionAgentId : true;
if (activeRunId !== runId && sameSession && sameSelectedGlobalAgent) context.registerToolEventRecipient(activeRunId, connId);
}
}
const wantsDelivery = request.deliver === true;
const explicitTo = normalizeOptionalString(request.replyTo) ?? normalizeOptionalString(request.to);
const explicitThreadId = normalizeOptionalString(request.threadId);
const turnSourceChannel = normalizeOptionalString(request.channel);
const turnSourceTo = normalizeOptionalString(request.to);
const turnSourceAccountId = normalizeOptionalString(request.accountId);
const deliveryPlan = await resolveAgentDeliveryPlanWithSessionRoute({
cfg: cfgForAgent ?? cfg,
agentId: activeSessionAgentId,
currentSessionKey: resolvedSessionKey,
sessionEntry,
requestedChannel: request.replyChannel ?? request.channel,
explicitTo,
explicitThreadId,
accountId: request.replyAccountId ?? request.accountId,
wantsDelivery,
turnSourceChannel,
turnSourceTo,
turnSourceAccountId,
turnSourceThreadId: explicitThreadId
});
let resolvedChannel = deliveryPlan.resolvedChannel;
let deliveryTargetMode = deliveryPlan.deliveryTargetMode;
const resolvedAccountId = deliveryPlan.resolvedAccountId;
let resolvedTo = deliveryPlan.resolvedTo;
let effectivePlan = deliveryPlan;
let deliveryDowngradeReason = null;
let deliveryTargetResolutionError;
if (wantsDelivery && resolvedChannel === "webchat") {
const cfgResolved = cfgForAgent ?? cfg;
try {
resolvedChannel = (await resolveMessageChannelSelection({ cfg: cfgResolved })).channel;
deliveryTargetMode = deliveryTargetMode ?? "implicit";
effectivePlan = {
...deliveryPlan,
resolvedChannel,
deliveryTargetMode,
resolvedAccountId
};
} catch (err) {
if (!shouldDowngradeDeliveryToSessionOnly({
wantsDelivery,
bestEffortDeliver,
resolvedChannel
})) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, formatForLog(err)));
return;
}
deliveryDowngradeReason = String(err);
}
}
if (!resolvedTo && isDeliverableMessageChannel(resolvedChannel)) {
const fallback = resolveAgentOutboundTarget({
cfg: cfgForAgent ?? cfg,
plan: effectivePlan,
targetMode: deliveryTargetMode ?? "implicit",
validateExplicitTarget: false
});
if (fallback.resolvedTarget?.ok) resolvedTo = fallback.resolvedTo;
else if (fallback.resolvedTarget && !fallback.resolvedTarget.ok) deliveryTargetResolutionError = fallback.resolvedTarget.error;
}
if (wantsDelivery && isDeliverableMessageChannel(resolvedChannel) && !resolvedTo) {
if (!bestEffortDeliver) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, deliveryTargetResolutionError ? String(deliveryTargetResolutionError) : `delivery target is required for ${resolvedChannel}: pass --to/--reply-to or configure a default target`));
return;
}
context.logGateway.info(deliveryTargetResolutionError ? `agent delivery target missing (bestEffortDeliver): ${String(deliveryTargetResolutionError)}` : "agent delivery target missing (bestEffortDeliver): no deliverable target");
}
if (wantsDelivery && resolvedChannel === "webchat") {
if (!shouldDowngradeDeliveryToSessionOnly({
wantsDelivery,
bestEffortDeliver,
resolvedChannel
})) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "delivery channel is required: pass --channel/--reply-channel or use a main session with a previous channel"));
return;
}
context.logGateway.info(deliveryDowngradeReason ? `agent delivery downgraded to session-only (bestEffortDeliver): ${deliveryDowngradeReason}` : "agent delivery downgraded to session-only (bestEffortDeliver): no deliverable channel");
}
const normalizedTurnSource = normalizeMessageChannel(turnSourceChannel);
const originMessageChannel = (normalizedTurnSource && isKnownGatewayChannel(normalizedTurnSource) ? normalizedTurnSource : void 0) ?? (client?.connect && isWebchatConnect(client.connect) ? "webchat" : resolvedChannel);
const deliver = request.deliver === true && resolvedChannel !== "webchat";
const preRegistrationAbort = readGatewayDedupeEntry({
dedupe: context.dedupe,
keys: agentDedupeKeys
});
if (isPreRegistrationAbortedAgentDedupeEntryForSession({
entry: preRegistrationAbort,
runId,
sessionKey: resolvedSessionKey,
alternateSessionKeys: [preAcceptedReservedSessionKey, requestedSessionKey]
})) {
agentRunAccepted = true;
respond(true, preRegistrationAbort?.payload, void 0, {
cached: true,
runId
});
return;
}
const now = Date.now();
const timeoutMs = resolveAgentTimeoutMs({
cfg: cfgForAgent ?? cfg,
overrideSeconds: typeof request.timeout === "number" ? request.timeout : void 0
});
const activeModelProvider = providerOverride ?? resolveSessionModelRef(cfgForAgent ?? cfg, sessionEntry, activeSessionAgentId).provider;
const activeAuthProvider = resolveProviderIdForAuth(activeModelProvider, { config: cfgForAgent ?? cfg });
const activeRunAbort = registerChatAbortController({
chatAbortControllers: context.chatAbortControllers,
runId,
sessionId: resolvedSessionId ?? runId,
sessionKey: resolvedSessionKey,
agentId: resolvedSessionKey === "global" ? activeSessionAgentId : void 0,
timeoutMs,
now,
expiresAtMs: resolveAgentRunExpiresAtMs({
now,
timeoutMs
}),
ownerConnId,
ownerDeviceId,
providerId: activeModelProvider,
authProviderId: activeAuthProvider,
controlUiVisible: !suppressVisibleSessionEffects,
kind: "agent"
});
const existingRunAbort = context.chatAbortControllers.get(runId);
if (!activeRunAbort.registered && existingRunAbort) {
agentRunAccepted = existingRunAbort.kind === "agent";
respond(true, {
runId,
status: "in_flight"
}, void 0, {
cached: true,
runId
});
return;
}
const resolvedThreadId = explicitThreadId ?? deliveryPlan.resolvedThreadId;
const taskTrackingMode = resolveGatewayAgentTaskTrackingMode({
client,
sessionKey: resolvedSessionKey,
inputProvenance
});
let dispatchTaskTrackingMode = taskTrackingMode === "cli" ? "cli" : "none";
if (taskTrackingMode === "plugin_subagent" && resolvedSessionKey) try {
await registerPluginSubagentRunFromGateway({
cfg,
runId,
childSessionKey: resolvedSessionKey,
task: request.message.trim(),
requesterOrigin: normalizeDeliveryContext({
channel: resolvedChannel,
to: resolvedTo,
accountId: resolvedAccountId,
threadId: resolvedThreadId
}),
pluginId: normalizeOptionalString(client?.internal?.pluginRuntimeOwnerId)
});
} catch (err) {
context.logGateway.warn(`failed to register plugin subagent run ${runId}; falling back to cli task tracking: ${formatForLog(err)}`);
dispatchTaskTrackingMode = "cli";
}
const accepted = {
runId,
sessionKey: resolvedSessionKey,
...resolvedSessionKey === "global" ? { agentId: activeSessionAgentId } : {},
status: "accepted",
acceptedAt: Date.now()
};
const acceptedDedupePayload = {
...accepted,
controlUiVisible: !suppressVisibleSessionEffects,
dedupeKeys: agentDedupeKeys,
ownerConnId,
ownerDeviceId
};
agentRunAccepted = true;
setGatewayDedupeEntries({
dedupe: context.dedupe,
keys: agentDedupeKeys,
entry: {
ts: Date.now(),
ok: true,
payload: acceptedDedupePayload
}
});
respond(true, accepted, void 0, { runId });
(async () => {
await yieldAfterAgentAcceptedAck();
let dispatched = false;
try {
if (activeRunAbort.controller.signal.aborted) {
const stopReason = resolveAbortedAgentStopReason(activeRunAbort.entry);
setAbortedAgentDedupeEntries({
dedupe: context.dedupe,
keys: agentDedupeKeys,
agentId: resolvedSessionKey === "global" ? activeSessionAgentId : void 0,
runId,
stopReason
});
respond(true, {
runId,
status: "timeout",
summary: "aborted",
stopReason,
timeoutPhase: "queue",
providerStarted: false
}, void 0, { runId });
return;
}
if (resolvedSessionKey) await reactivateCompletedSubagentSession({
sessionKey: resolvedSessionKey,
runId
});
if (requestedSessionKey && resolvedSessionKey && isNewSession) emitSessionsChanged(context, {
sessionKey: resolvedSessionKey,
...resolvedSessionKey === "global" ? { agentId: activeSessionAgentId } : {},
reason: "create"
});
if (resolvedSessionKey) emitSessionsChanged(context, {
sessionKey: resolvedSessionKey,
...resolvedSessionKey === "global" ? { agentId: activeSessionAgentId } : {},
reason: "send"
});
if (!isRawModelRun) message = annotateInterSessionPromptText(message, inputProvenance);
const ingressAgentId = resolvedSessionKey === "global" ? activeSessionAgentId : agentId && (!resolvedSessionKey || resolveAgentIdFromSessionKey(resolvedSessionKey) === agentId) ? agentId : void 0;
let execApprovalFollowupRuntimeHandoff = canUseInternalRuntimeHandoff && execApprovalFollowupApprovalId ? consumeExecApprovalFollowupRuntimeHandoff({
handoffId: request.internalRuntimeHandoffId,
approvalId: execApprovalFollowupApprovalId,
idempotencyKey: idem,
sessionKey: resolvedSessionKey
}) : void 0;
if (!execApprovalFollowupRuntimeHandoff && canUseInternalRuntimeHandoff && execApprovalFollowupApprovalId && requestedSessionKeyRaw && requestedSessionKeyRaw !== resolvedSessionKey) execApprovalFollowupRuntimeHandoff = consumeExecApprovalFollowupRuntimeHandoff({
handoffId: request.internalRuntimeHandoffId,
approvalId: execApprovalFollowupApprovalId,
idempotencyKey: idem,
sessionKey: requestedSessionKeyRaw
});
const execApprovalFollowupElevatedDefaults = execApprovalFollowupRuntimeHandoff?.bashElevated;
dispatchAgentRunFromGateway({
ingressOpts: {
message,
images,
imageOrder,
agentId: ingressAgentId,
provider: providerOverride,
model: modelOverride,
to: resolvedTo,
sessionId: resolvedSessionId,
sessionKey: resolvedSessionKey,
thinking: request.thinking,
deliver,
deliveryTargetMode,
channel: resolvedChannel,
accountId: resolvedAccountId,
threadId: resolvedThreadId,
runContext: {
messageChannel: originMessageChannel,
accountId: resolvedAccountId,
groupId: resolvedGroupId,
groupChannel: resolvedGroupChannel,
groupSpace: resolvedGroupSpace,
currentThreadTs: resolvedThreadId != null ? String(resolvedThreadId) : void 0
},
...execApprovalFollowupElevatedDefaults ? { bashElevated: execApprovalFollowupElevatedDefaults } : {},
groupId: resolvedGroupId,
groupChannel: resolvedGroupChannel,
groupSpace: resolvedGroupSpace,
spawnedBy: spawnedByValue,
timeout: request.timeout?.toString(),
bestEffortDeliver,
messageChannel: originMessageChannel,
runId,
lane: request.lane,
modelRun: request.modelRun === true,
promptMode: request.promptMode,
extraSystemPrompt: request.extraSystemPrompt,
bootstrapContextMode: request.bootstrapContextMode,
bootstrapContextRunKind: request.bootstrapContextRunKind,
acpTurnSource: request.acpTurnSource,
internalEvents: request.internalEvents,
inputProvenance,
sessionEffects,
skipInitialSessionTouch: skipAgentInitialSessionTouch,
preserveUserFacingSessionModelState,
sourceReplyDeliveryMode: request.sourceReplyDeliveryMode,
disableMessageTool: request.disableMessageTool,
suppressPromptPersistence: requestedPromptPersistenceSuppression || shouldSuppressAgentPromptPersistence({
inputProvenance,
internalEvents: request.internalEvents
}),
cleanupBundleMcpOnRunEnd: request.cleanupBundleMcpOnRunEnd,
abortSignal: activeRunAbort.controller.signal,
onActiveModelSelected: ({ provider }) => {
updateChatRunProvider(context.chatAbortControllers, {
runId,
providerId: provider,
authProviderId: resolveProviderIdForAuth(provider, { config: cfgForAgent ?? cfg })
});
},
workspaceDir: resolveIngressWorkspaceOverrideForSpawnedRun({
spawnedBy: spawnedByValue,
workspaceDir: sessionEntry?.spawnedWorkspaceDir
}),
cwd: resolveSessionRuntimeCwd({
spawnedBy: spawnedByValue,
sessionEntry
}),
allowModelOverride
},
runId,
dedupeKeys: agentDedupeKeys,
abortController: activeRunAbort.controller,
respond,
context,
taskTrackingMode: dispatchTaskTrackingMode
});
dispatched = true;
} catch (err) {
const error = errorShape(ErrorCodes.UNAVAILABLE, formatForLog(err));
const payload = {
runId,
status: "error",
summary: formatForLog(err)
};
setGatewayDedupeEntries({
dedupe: context.dedupe,
keys: agentDedupeKeys,
entry: {
ts: Date.now(),
ok: false,
payload,
error
}
});
respond(false, payload, error, {
runId,
error: formatForLog(err)
});
} finally {
if (!dispatched) activeRunAbort.cleanup();
}
})();
} finally {
clearUnacceptedExecApprovalFollowupDedupe();
}
},
"agent.identity.get": ({ params, respond, context }) => {
if (!validateAgentIdentityParams(params)) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.identity.get params: ${formatValidationErrors(validateAgentIdentityParams.errors)}`));
return;
}
const p = params;
const agentIdRaw = normalizeOptionalString(p.agentId) ?? "";
const sessionKeyRaw = normalizeOptionalString(p.sessionKey) ?? "";
let agentId = agentIdRaw ? normalizeAgentId(agentIdRaw) : void 0;
if (sessionKeyRaw) {
if (classifySessionKeyShape(sessionKeyRaw) === "malformed_agent") {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.identity.get params: malformed session key "${sessionKeyRaw}"`));
return;
}
const resolved = resolveAgentIdFromSessionKey(sessionKeyRaw);
if (agentId && resolved !== agentId) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.identity.get params: agent "${agentIdRaw}" does not match session key agent "${resolved}"`));
return;
}
agentId = resolved;
}
const cfg = context.getRuntimeConfig();
const identity = resolveAssistantIdentity({
cfg,
agentId
});
const avatarValue = resolveAssistantAvatarUrl({
avatar: identity.avatar,
agentId: identity.agentId,
basePath: cfg.gateway?.controlUi?.basePath
}) ?? identity.avatar;
const avatarResolution = resolveAgentAvatar(cfg, identity.agentId, { includeUiOverride: true });
respond(true, {
...identity,
avatar: avatarValue,
avatarSource: resolvePublicAgentAvatarSource(avatarResolution),
avatarStatus: avatarResolution.kind,
avatarReason: avatarResolution.kind === "none" ? avatarResolution.reason : void 0
}, void 0);
},
"agent.wait": async ({ params, respond, context }) => {
if (!validateAgentWaitParams(params)) {
respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid agent.wait params: ${formatValidationErrors(validateAgentWaitParams.errors)}`));
return;
}
const p = params;
const runId = (p.runId ?? "").trim();
const timeoutMs = typeof p.timeoutMs === "number" && Number.isFinite(p.timeoutMs) ? Math.max(0, Math.floor(p.timeoutMs)) : 3e4;
const activeChatEntry = context.chatAbortControllers.get(runId);
const hasActiveChatRun = activeChatEntry !== void 0 && activeChatEntry.kind !== "agent";
const cachedGatewaySnapshot = readTerminalSnapshotFromGatewayDedupe({
dedupe: context.dedupe,
runId,
ignoreAgentTerminalSnapshot: hasActiveChatRun
});
if (cachedGatewaySnapshot) {
respond(true, {
runId,
status: cachedGatewaySnapshot.status,
startedAt: cachedGatewaySnapshot.startedAt,
endedAt: cachedGatewaySnapshot.endedAt,
error: cachedGatewaySnapshot.error,
stopReason: cachedGatewaySnapshot.stopReason,
livenessState: cachedGatewaySnapshot.livenessState,
yielded: cachedGatewaySnapshot.yielded,
pendingError: cachedGatewaySnapshot.pendingError,
timeoutPhase: cachedGatewaySnapshot.timeoutPhase,
providerStarted: cachedGatewaySnapshot.providerStarted
});
return;
}
const lifecycleAbortController = new AbortController();
const dedupeAbortController = new AbortController();
const dedupePromise = waitForTerminalGatewayDedupe({
dedupe: context.dedupe,
runId,
timeoutMs,
signal: dedupeAbortController.signal,
ignoreAgentTerminalSnapshot: hasActiveChatRun
});
if (hasActiveChatRun) {
const snapshot = await dedupePromise;
dedupeAbortController.abort();
if (!snapshot) {
respond(true, {
runId,
status: "timeout",
timeoutPhase: "gateway_draining"
});
return;
}
respond(true, {
runId,
status: snapshot.status,
startedAt: snapshot.startedAt,
endedAt: snapshot.endedAt,
error: snapshot.error,
stopReason: snapshot.stopReason,
livenessState: snapshot.livenessState,
yielded: snapshot.yielded,
pendingError: snapshot.pendingError,
timeoutPhase: snapshot.timeoutPhase,
providerStarted: snapshot.providerStarted
});
return;
}
const lifecyclePromise = waitForAgentJob({
runId,
timeoutMs,
signal: lifecycleAbortController.signal
});
const first = await Promise.race([lifecyclePromise.then((snapshot) => ({
source: "lifecycle",
snapshot
})), dedupePromise.then((snapshot) => ({
source: "dedupe",
snapshot
}))]);
let snapshot = first.snapshot;
if (snapshot) if (first.source === "lifecycle") dedupeAbortController.abort();
else lifecycleAbortController.abort();
else {
snapshot = first.source === "lifecycle" ? await dedupePromise : await lifecyclePromise;
lifecycleAbortController.abort();
dedupeAbortController.abort();
}
if (!snapshot) {
const activeRunRegistered = activeChatEntry !== void 0;
respond(true, {
runId,
status: "timeout",
timeoutPhase: activeRunRegistered ? "gateway_draining" : "queue",
...activeRunRegistered ? {} : { providerStarted: false }
});
return;
}
respond(true, {
runId,
status: snapshot.status,
startedAt: snapshot.startedAt,
endedAt: snapshot.endedAt,
error: snapshot.error,
stopReason: snapshot.stopReason,
livenessState: snapshot.livenessState,
yielded: snapshot.yielded,
pendingError: snapshot.pendingError,
timeoutPhase: snapshot.timeoutPhase,
providerStarted: snapshot.providerStarted
});
}
};
//#endregion
export { agentHandlers };