openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,996 lines • 88.3 kB
JavaScript
import "./src-vebZIeLe.js";
import { c as isRecord } from "./record-coerce-DItp3I4t.js";
import { c as normalizeOptionalLowercaseString, l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { d as normalizeStringEntries, y as uniqueStrings } from "./string-normalization-DsCfAx8q.js";
import { n as createLazyPromise } from "./lazy-promise-DGqyc4Y4.js";
import { r as createLazyRuntimeModule } from "./lazy-runtime-CgCh8H_K.js";
import { n as isTruthyEnvValue } from "./env-M3R40TOb.js";
import { n as getRuntimeConfig } from "./io.runtime-B9iJRs3w.js";
import { v as resolveSecretInputRef } from "./types.secrets-kC0nOetj.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { t as createSubsystemLogger } from "./subsystem-Dy2tqXOS.js";
import { a as getNodeSqliteKysely, i as executeSqliteQueryTakeFirstSync, r as executeSqliteQuerySync } from "./kysely-sync-COmh4HWh.js";
import { s as runOpenClawStateWriteTransaction } from "./openclaw-state-db-BRTnL-D8.js";
import { i as resolveManifestContractOwnerPluginId } from "./plugin-registry-contributions-BwuDvm89.js";
import "./plugin-registry-DD_0nL_t.js";
import { t as cloneConfigWithResolutionFacts } from "./resolution-facts-Dks1tbik.js";
import { l as getRuntimeConfigSourceSnapshot, o as getRuntimeConfigSnapshot } from "./runtime-snapshot-BaQikjTR.js";
import { f as assertExpectedResolvedSecretValue, i as createResolverContext } from "./runtime-shared-CqbaocQu.js";
import { r as discoverConfigSecretTargetsByIds } from "./target-registry-query-qVhyD-Hq.js";
import { i as setPathExistingStrict } from "./path-utils-BSrJtisq.js";
import "./target-registry-BHDUOLUU.js";
import { a as READ_SCOPE$1, n as APPROVALS_SCOPE$1 } from "./operator-scopes-Dw7Gu2cA.js";
import "./io-bdCzpGWJ.js";
import "./config-Cs0XXL3x.js";
import { n as resolveSecretRefValue } from "./resolve-224YoYfx.js";
import { i as loadOrCreateProcessDeviceIdentity } from "./device-identity-J83pn_rP.js";
import { r as roleScopesAllow } from "./operator-scope-compat-iV7_Lmth.js";
import { n as resolveControlUiWebPushUrl } from "./control-ui-shared-BiO6QP54.js";
import { g as registerAgentRunDelegatedAuthorityClosedHandler } from "./agent-run-registry-CKYKdfNd.js";
import "./method-scopes-K6J_UQGL.js";
import { C as runWithRetainedGatewayRootWork } from "./gateway-work-admission-R1IpuDim.js";
import { n as normalizeMessageChannel } from "./message-channel-core-AaEWic-A.js";
import { t as isDeliverableMessageChannel } from "./message-channel-normalize-B81s0XMO.js";
import "./message-channel-BQrhwUEA.js";
import { t as AsyncWorkScope } from "./async-work-scope-CMQS2uTf.js";
import { i as resolveProjectedMcpCodexToolApprovalMode } from "./mcp-codex-tool-approval-UMdM31mU.js";
import { d as isTrustedSecretSurfaceUnavailableError, f as listActiveCredentialDegradedOwners } from "./runtime-degraded-state-D5EZZ925.js";
import { t as formatFencedCodeBlock } from "./markdown-code-Buzx6wvi.js";
import { n as channelRouteDedupeKey } from "./channel-route--2in3Ckg.js";
import { n as getLoadedChannelPlugin } from "./registry-Cz6cv4VC.js";
import { t as resolveChannelApprovalAdapter } from "./plugins-Cozj1Enf.js";
import { C as getUserPreferences, c as resolveUserProfileId } from "./user-profiles-4AB7AmiH.js";
import { u as resolveExecApprovalRequestAllowedDecisions } from "./exec-approvals-policy-CCFUzTzd.js";
import { i as sanitizeExecApprovalWarningText } from "./exec-approval-text-sanitize-C1BzZQH8.js";
import { c as buildPluginApprovalExpiredMessage, l as buildPluginApprovalRequestMessage, s as approvalDecisionLabel } from "./plugin-approvals-BUrr4V_c.js";
import { t as resolveCanonicalPluginApprovalRequestAllowedDecisions } from "./plugin-approval-canonical-decisions-CwBFXTtw.js";
import "./exec-approvals-BSZ-fPIY.js";
import { c as getActiveSecretsRuntimeSnapshotRevisionState, l as getActiveSecretsRuntimeSnapshotState, o as getActiveSecretsRuntimeEnvState } from "./runtime-state-C4aJ8Hzz.js";
import { t as analyzeCommandSecretAssignmentsFromSnapshot } from "./command-config-B9C-NY8K.js";
import { t as resolveRuntimeWebTools } from "./runtime-web-tools-DYZFauRm.js";
import { c as listDevicePairing, i as hasEffectivePairedDeviceRole } from "./device-pairing-DFIQp3ZY.js";
import { t as matchesApprovalRequestFilters } from "./approval-request-filters-lAF2sz9A.js";
import { f as formatExecApprovalExpiresIn } from "./exec-approval-reply-ZdeEqz7J.js";
import { a as buildTypedApprovalPendingReplyPayload, i as buildPluginApprovalResolvedReplyPayload, n as buildApprovalResolvedReplyPayload, o as buildTypedPluginApprovalPendingReplyPayload } from "./approval-renderers-DKlGmVsG.js";
import { t as createPendingApprovalRegistry } from "./pending-approval-registry-Ba8xUSFo.js";
import { t as resolveExecApprovalCommandDisplay } from "./exec-approval-command-display-03IqfHT_.js";
import { t as SYSTEM_AGENT_APPROVAL_DECISIONS } from "./system-agent-approvals-r0J6k0r_.js";
import { b as sameWorkerSessionTurnClaim } from "./placement-record-BOfTzlFO.js";
import { t as diffConfigPaths } from "./config-diff-D4CS9rJs.js";
import { t as buildGatewayReloadPlan } from "./config-reload-plan-DUT3qlnP.js";
import { n as find } from "./placement-row-codec-CuXIyKoc.js";
import { f as pruneTerminalOperatorApprovals, n as closeOrphanedOperatorApprovals, o as getOperatorApprovalDetailed } from "./operator-approval-store-BxnBnd-1.js";
import { t as resolveApprovalSessionAudienceWithFallback } from "./approval-session-audience-ChaFPoso.js";
import { t as canAccessOperatorApproval } from "./operator-approval-authorization-DgI7PHhT.js";
import { A as resolveEffectiveWebPushPreferences, E as isWebPushQuietHours, M as webPushCategoryEnabled, O as normalizeWebPushDisplayLabel, T as WEB_PUSH_USER_PREFERENCES_KEY, g as listWebPushApprovalDeliveryTargets, h as listTerminalWebPushApprovalDeliveryIds, j as webPushAgentAllowed, m as listBoundWebPushSubscriptions, o as deleteWebPushApprovalDeliveryTargets, v as prepareWebPushApprovalDeliveries } from "./push-web-store-DllT5THN.js";
import { r as prepareWebPushNotificationSender } from "./push-web-Q11I0BtA.js";
import { f as canAccessApprovalSession, p as isApprovalRecordVisibleToClient } from "./approval-shared-ahH5xF_7.js";
import { n as webPushTargetClient, t as listCurrentWebPushTargets } from "./web-push-authority-ggw8QaCn.js";
import { a as sendApnsPluginApprovalAlert, c as resolveApnsAuthConfigFromEnv, i as sendApnsExecApprovalResolvedWake, o as sendApnsPluginApprovalResolvedWake, r as sendApnsExecApprovalAlert, s as shouldClearStoredApnsRegistration } from "./push-apns-n6D4554g.js";
import { g as resolveApnsRelayConfigFromEnv, i as clearApnsRegistrationIfCurrent, l as loadApnsRegistrations } from "./push-apns-store-cDZzCfro.js";
import { t as ExecApprovalManager } from "./exec-approval-manager-CvF_La8z.js";
import { t as QuestionManager } from "./question-manager-CpjwI9hn.js";
import { t as publishAppliedApprovalResolution } from "./approval-publication-C03af9bl.js";
import { t as refreshModelRuntimeAfterHotReload } from "./server-reload-model-runtime-scope-BIyY3PYA.js";
import { c as isSharedGatewaySessionGenerationOwnershipCurrent, i as disconnectStaleSharedGatewayAuthClients, l as replaceOwnedSharedGatewaySessionGenerationState, n as claimSharedGatewaySessionGenerationIfOwned, o as finalizeOwnedSharedGatewaySessionGeneration, t as captureSharedGatewaySessionGenerationOwnership } from "./server-shared-auth-generation-BzQsL2E3.js";
import { createHash, randomUUID } from "node:crypto";
//#region src/infra/exec-approval-forwarder.messages.ts
function formatApprovalCommand(command) {
return !command.includes("\n") && !command.includes("`") ? {
inline: true,
text: `\`${command}\``
} : {
inline: false,
text: formatFencedCodeBlock(command)
};
}
function buildForwardedExecApprovalRequest(request, nowMs) {
const allowedDecisions = resolveExecApprovalRequestAllowedDecisions(request.request);
const decisionText = allowedDecisions.join("|");
const lines = ["🔒 Exec approval required", `ID: ${request.id}`];
const warningText = request.request.warningText?.trim();
if (warningText) lines.push("", warningText);
const analysisWarningLines = normalizeStringEntries(request.request.commandAnalysis?.warningLines.map(sanitizeExecApprovalWarningText)).slice(0, 5);
if (analysisWarningLines && analysisWarningLines.length > 0) {
lines.push("", "Command analysis:");
for (const line of analysisWarningLines) lines.push(`- ${line}`);
}
const command = formatApprovalCommand(resolveExecApprovalCommandDisplay(request.request).commandText);
if (command.inline) lines.push(`Command: ${command.text}`);
else lines.push("Command:", command.text);
if (request.request.cwd) lines.push(`CWD: ${request.request.cwd}`);
if (request.request.nodeId) lines.push(`Node: ${request.request.nodeId}`);
if (Array.isArray(request.request.envKeys) && request.request.envKeys.length > 0) lines.push(`Env overrides: ${request.request.envKeys.join(", ")}`);
if (request.request.host) lines.push(`Host: ${request.request.host}`);
if (request.request.agentId) lines.push(`Agent: ${request.request.agentId}`);
if (request.request.security) lines.push(`Security: ${request.request.security}`);
if (request.request.ask) lines.push(`Ask: ${request.request.ask}`);
lines.push(`Expires in: ${formatExecApprovalExpiresIn(request.expiresAtMs, nowMs)}`);
lines.push("Mode: foreground (interactive approvals available in this chat).");
lines.push(allowedDecisions.includes("allow-always") ? "Background mode note: non-interactive runs cannot wait for chat approvals; use pre-approved policy (allow-always or ask=off)." : "Background mode note: non-interactive runs cannot wait for chat approvals; the effective policy still requires per-run approval unless ask=off.");
lines.push(`Reply with: /approve ${request.id} ${decisionText}`);
if (!allowedDecisions.includes("allow-always")) lines.push("Allow Always is unavailable for this command.");
return lines.join("\n");
}
function buildForwardedExecApprovalResolved(resolved) {
return `${`✅ Exec approval ${approvalDecisionLabel(resolved.decision)}.`}${resolved.resolvedBy ? ` Resolved by ${resolved.resolvedBy}.` : ""} ID: ${resolved.id}`;
}
function buildForwardedExecApprovalExpired(request) {
return `⏱️ Exec approval expired. ID: ${request.id}`;
}
function buildApprovalRenderPayload(params) {
const channel = normalizeMessageChannel(params.target.channel) ?? params.target.channel;
return (channel ? params.resolveRenderer(resolveChannelApprovalAdapter(getLoadedChannelPlugin(channel)))?.(params.renderParams) : null) ?? params.buildFallback();
}
function buildForwardedExecPendingPayload(params) {
return buildApprovalRenderPayload({
target: params.target,
renderParams: params,
resolveRenderer: (adapter) => adapter?.render?.exec?.buildPendingPayload,
buildFallback: () => buildTypedApprovalPendingReplyPayload({
approvalKind: "exec",
approvalId: params.request.id,
approvalSlug: params.request.id.slice(0, 8),
text: buildForwardedExecApprovalRequest(params.request, params.nowMs),
agentId: params.request.request.agentId ?? null,
allowedDecisions: resolveExecApprovalRequestAllowedDecisions(params.request.request),
sessionKey: params.request.request.sessionKey ?? null
})
});
}
function buildForwardedExecResolvedPayload(params) {
return buildApprovalRenderPayload({
target: params.target,
renderParams: params,
resolveRenderer: (adapter) => adapter?.render?.exec?.buildResolvedPayload,
buildFallback: () => buildApprovalResolvedReplyPayload({
approvalId: params.resolved.id,
approvalSlug: params.resolved.id.slice(0, 8),
text: buildForwardedExecApprovalResolved(params.resolved)
})
});
}
function buildForwardedPluginPendingPayload(params) {
return buildApprovalRenderPayload({
target: params.target,
renderParams: params,
resolveRenderer: (adapter) => adapter?.render?.plugin?.buildPendingPayload,
buildFallback: () => buildTypedPluginApprovalPendingReplyPayload({
request: params.request,
nowMs: params.nowMs,
text: buildPluginApprovalRequestMessage(params.request, params.nowMs),
allowedDecisions: resolveCanonicalPluginApprovalRequestAllowedDecisions(params.request.request)
})
});
}
function buildForwardedPluginResolvedPayload(params) {
return buildApprovalRenderPayload({
target: params.target,
renderParams: params,
resolveRenderer: (adapter) => adapter?.render?.plugin?.buildResolvedPayload,
buildFallback: () => buildPluginApprovalResolvedReplyPayload({ resolved: params.resolved })
});
}
//#endregion
//#region src/infra/exec-approval-forwarder.ts
const log = createSubsystemLogger("gateway/exec-approvals");
const SYNTHETIC_APPROVAL_REQUEST_ID = "__approval-routing__";
const loadExecApprovalForwarderRuntime = createLazyRuntimeModule(() => import("./exec-approval-forwarder.runtime.js"));
function shouldForwardRoute(params) {
const config = params.config;
if (!config?.enabled) return false;
return matchesApprovalRequestFilters({
request: params.routeRequest,
agentFilter: config.agentFilter,
sessionFilter: config.sessionFilter,
fallbackAgentIdFromSessionKey: true
});
}
function buildTargetKey(target) {
const channel = normalizeMessageChannel(target.channel) ?? target.channel;
return channelRouteDedupeKey({
channel,
to: target.to,
accountId: target.accountId,
threadId: target.threadId
});
}
function buildSyntheticApprovalRequest(routeRequest) {
return {
approvalKind: "exec",
id: SYNTHETIC_APPROVAL_REQUEST_ID,
request: {
command: "",
agentId: routeRequest.agentId ?? null,
sessionKey: routeRequest.sessionKey ?? null,
turnSourceChannel: routeRequest.turnSourceChannel ?? null,
turnSourceTo: routeRequest.turnSourceTo ?? null,
turnSourceAccountId: routeRequest.turnSourceAccountId ?? null,
turnSourceThreadId: routeRequest.turnSourceThreadId ?? null
},
createdAtMs: 0,
expiresAtMs: 0
};
}
function shouldSkipForwardingFallback(params) {
const channel = normalizeMessageChannel(params.target.channel) ?? params.target.channel;
if (!channel) return false;
return resolveChannelApprovalAdapter(getLoadedChannelPlugin(channel))?.delivery?.shouldSuppressForwardingFallback?.({
cfg: params.cfg,
approvalKind: params.approvalKind,
target: params.target,
request: buildSyntheticApprovalRequest(params.routeRequest)
}) ?? false;
}
function normalizeTurnSourceChannel(value) {
const normalized = value ? normalizeMessageChannel(value) : void 0;
if (!normalized || !isDeliverableMessageChannel(normalized) && normalized !== "webchat" && normalized !== "tui") return;
return normalized;
}
function normalizeForwardingTurnSourceChannel(value, approvalKind) {
const normalized = normalizeTurnSourceChannel(value);
if (approvalKind === "exec" && normalized && !isDeliverableMessageChannel(normalized)) return;
return normalized;
}
function extractApprovalRouteRequest(request) {
if (!request) return null;
return {
agentId: request.agentId ?? null,
sessionKey: request.sessionKey ?? null,
turnSourceChannel: request.turnSourceChannel ?? null,
turnSourceTo: request.turnSourceTo ?? null,
turnSourceAccountId: request.turnSourceAccountId ?? null,
turnSourceThreadId: request.turnSourceThreadId ?? null
};
}
function defaultResolveSessionTarget(params) {
return loadExecApprovalForwarderRuntime().then(({ resolveExecApprovalSessionTarget }) => {
const resolvedTarget = resolveExecApprovalSessionTarget({
cfg: params.cfg,
request: params.request,
turnSourceChannel: normalizeTurnSourceChannel(params.request.request.turnSourceChannel),
turnSourceTo: normalizeOptionalString(params.request.request.turnSourceTo),
turnSourceAccountId: normalizeOptionalString(params.request.request.turnSourceAccountId),
turnSourceThreadId: params.request.request.turnSourceThreadId ?? void 0
});
if (!resolvedTarget?.channel || !resolvedTarget.to) return null;
const channel = resolvedTarget.channel;
if (!isDeliverableMessageChannel(channel)) return null;
return {
channel,
to: resolvedTarget.to,
accountId: resolvedTarget.accountId,
threadId: resolvedTarget.threadId
};
});
}
async function deliverToTargets(params) {
const deliveries = params.targets.map(async (target) => {
if (params.shouldSend && !params.shouldSend()) return;
const channel = normalizeMessageChannel(target.channel) ?? target.channel;
if (!isDeliverableMessageChannel(channel)) return;
try {
const payload = params.buildPayload(target);
await params.beforeDeliver?.(target, payload);
const send = await params.deliver({
cfg: params.cfg,
channel,
to: target.to,
accountId: target.accountId,
threadId: target.threadId,
payloads: [payload]
});
if (send.status === "failed" || send.status === "partial_failed") throw send.error;
} catch (err) {
log.error(`exec approvals: failed to deliver to ${channel}:${target.to}: ${String(err)}`);
}
});
await Promise.allSettled(deliveries);
}
async function resolveForwardTargets(params) {
const mode = params.config?.mode ?? "session";
const targets = [];
const seen = /* @__PURE__ */ new Set();
if (mode === "session" || mode === "both") {
const sessionRouteRequest = {
...params.routeRequest,
turnSourceChannel: normalizeForwardingTurnSourceChannel(params.routeRequest.turnSourceChannel, params.approvalKind)
};
const sessionTarget = await params.resolveSessionTarget({
cfg: params.cfg,
request: buildSyntheticApprovalRequest(sessionRouteRequest)
});
if (sessionTarget) {
const key = buildTargetKey(sessionTarget);
if (!seen.has(key)) {
seen.add(key);
targets.push({
...sessionTarget,
source: "session"
});
}
}
}
if (mode === "targets" || mode === "both") {
const explicitTargets = params.config?.targets ?? [];
for (const target of explicitTargets) {
const key = buildTargetKey(target);
if (seen.has(key)) continue;
seen.add(key);
targets.push({
...target,
source: "target"
});
}
}
return targets;
}
function createApprovalHandlers(params) {
const pending = createPendingApprovalRegistry();
const work = new AsyncWorkScope();
let stopped = false;
let stopPromise;
const trackDelivery = (run) => work.track(() => runWithRetainedGatewayRootWork(run));
const resolveTargets = async (paramsForRoute) => {
if (!shouldForwardRoute(paramsForRoute)) return [];
return (await resolveForwardTargets({
...paramsForRoute,
approvalKind: params.strategy.kind,
resolveSessionTarget: params.resolveSessionTarget
})).filter((target) => !shouldSkipForwardingFallback({
approvalKind: params.strategy.kind,
target,
cfg: paramsForRoute.cfg,
routeRequest: paramsForRoute.routeRequest
}));
};
const deliverResolved = async (resolved, entry) => {
const cfg = params.getConfig();
const routeRequest = entry?.routeRequest ?? extractApprovalRouteRequest(resolved.request);
const targets = entry?.targets ?? (routeRequest ? await resolveTargets({
cfg,
config: params.strategy.config(cfg),
routeRequest
}) : []);
if (!targets.length) return;
await deliverToTargets({
cfg,
targets,
buildPayload: (target) => params.strategy.buildResolvedPayload({
cfg,
resolved,
target
}),
deliver: params.deliver
});
};
const handleRequested = async (request) => {
const cfg = params.getConfig();
const config = params.strategy.config(cfg);
const requestId = request.id;
const routeRequest = extractApprovalRouteRequest(request.request) ?? {};
const pendingEntry = pending.begin(requestId, {
routeRequest,
targets: []
});
let filteredTargets;
try {
filteredTargets = await resolveTargets({
cfg,
config,
routeRequest
});
} catch (error) {
pending.remove(requestId, pendingEntry);
throw error;
}
if (filteredTargets.length === 0) {
pending.remove(requestId, pendingEntry);
return false;
}
pendingEntry.value = {
routeRequest,
targets: filteredTargets
};
const expiresInMs = Math.max(0, request.expiresAtMs - params.nowMs());
pending.scheduleExpiry(pendingEntry, expiresInMs, (expired) => trackDelivery(() => deliverToTargets({
cfg,
targets: expired.value.targets,
buildPayload: () => ({ text: params.strategy.buildExpiredText(request) }),
deliver: params.deliver
})).catch((err) => {
log.error(`${params.strategy.kind} approvals: failed to deliver expiry notification for ${requestId}: ${String(err)}`);
}));
trackDelivery(() => deliverToTargets({
cfg,
targets: filteredTargets,
buildPayload: (target) => params.strategy.buildPendingPayload({
cfg,
request,
target,
nowMs: params.nowMs()
}),
beforeDeliver: async (target, payload) => {
const channel = normalizeMessageChannel(target.channel) ?? target.channel;
if (!channel) return;
await getLoadedChannelPlugin(channel)?.outbound?.beforeDeliverPayload?.({
cfg,
target,
payload,
hint: {
kind: "approval-pending",
approvalKind: params.strategy.kind
}
});
},
deliver: params.deliver,
shouldSend: () => pending.isCurrent(pendingEntry)
}).then(() => pending.completeDelivery(pendingEntry, pendingEntry.value))).catch((err) => {
log.error(`${params.strategy.kind} approvals: failed to deliver request ${requestId}: ${String(err)}`);
});
return true;
};
const handleResolved = async (resolved) => {
const settled = pending.settle(resolved.id, (entry) => deliverResolved(resolved, entry.value));
if (settled.status === "queued") return;
if (settled.status === "taken") {
await settled.terminal(settled.entry);
return;
}
await deliverResolved(resolved);
};
return {
handleRequested: (request) => stopped ? Promise.resolve(false) : trackDelivery(() => handleRequested(request)),
handleResolved: (resolved) => stopped ? Promise.resolve() : trackDelivery(() => handleResolved(resolved)),
stop: () => {
if (!stopPromise) {
stopped = true;
pending.stopExpiryTimers();
stopPromise = work.drain().then(() => pending.clear());
}
return stopPromise;
}
};
}
const execApprovalStrategy = {
kind: "exec",
config: (cfg) => cfg.approvals?.exec,
buildExpiredText: buildForwardedExecApprovalExpired,
buildPendingPayload: buildForwardedExecPendingPayload,
buildResolvedPayload: buildForwardedExecResolvedPayload
};
const pluginApprovalStrategy = {
kind: "plugin",
config: (cfg) => cfg.approvals?.plugin,
buildExpiredText: buildPluginApprovalExpiredMessage,
buildPendingPayload: buildForwardedPluginPendingPayload,
buildResolvedPayload: buildForwardedPluginResolvedPayload
};
function createExecApprovalForwarder(deps = {}) {
const getConfig = deps.getConfig ?? getRuntimeConfig;
const deliver = deps.deliver ?? (async (params) => {
const { sendDurableMessageBatchCore } = await loadExecApprovalForwarderRuntime();
return sendDurableMessageBatchCore(params);
});
const nowMs = deps.nowMs ?? Date.now;
const resolveSessionTarget = deps.resolveSessionTarget ?? defaultResolveSessionTarget;
const execHandlers = createApprovalHandlers({
strategy: execApprovalStrategy,
getConfig,
deliver,
nowMs,
resolveSessionTarget
});
const pluginHandlers = createApprovalHandlers({
strategy: pluginApprovalStrategy,
getConfig,
deliver,
nowMs,
resolveSessionTarget
});
return {
handleRequested: execHandlers.handleRequested,
handleResolved: execHandlers.handleResolved,
handlePluginApprovalRequested: pluginHandlers.handleRequested,
handlePluginApprovalResolved: pluginHandlers.handleResolved,
stop: async () => {
await Promise.all([execHandlers.stop(), pluginHandlers.stop()]);
}
};
}
//#endregion
//#region src/secrets/runtime-command-secrets.ts
/** Resolves command-scoped secrets, including web provider override credentials. */
function hasProviderOverrides(overrides) {
return normalizeOptionalString(overrides?.webSearch) !== void 0 || normalizeOptionalString(overrides?.webFetch) !== void 0;
}
function applyProviderOverridesToConfig(config, overrides) {
if (!hasProviderOverrides(overrides)) return config;
const next = cloneConfigWithResolutionFacts(config);
const tools = next.tools ??= {};
const web = tools.web ??= {};
const webSearch = normalizeOptionalString(overrides?.webSearch);
if (webSearch) {
const search = web.search ??= {};
search.provider = webSearch;
}
const webFetch = normalizeOptionalString(overrides?.webFetch);
if (webFetch) {
const fetch = web.fetch ??= {};
fetch.provider = webFetch;
}
return next;
}
function pluginIdFromRuntimeWebPath(path) {
return /^plugins\.entries\.([^.]+)\.config\.(webSearch|webFetch)\.apiKey$/.exec(path)?.[1];
}
function isWebCommandSecretPath(path) {
return /^plugins\.entries\.[^.]+\.config\.(webSearch|webFetch)\.apiKey$/.test(path);
}
function isProviderOverridePath(params) {
const webSearch = normalizeOptionalString(params.providerOverrides?.webSearch);
if (webSearch) {
if (params.config.tools?.web?.search?.enabled === false) return false;
const pluginId = pluginIdFromRuntimeWebPath(params.path);
if (pluginId && params.path.endsWith(".config.webSearch.apiKey")) return resolveManifestContractOwnerPluginId({
contract: "webSearchProviders",
value: webSearch,
origin: "bundled",
config: params.config
}) === pluginId;
}
const webFetch = normalizeOptionalString(params.providerOverrides?.webFetch);
if (webFetch) {
if (params.config.tools?.web?.fetch?.enabled === false) return false;
const pluginId = pluginIdFromRuntimeWebPath(params.path);
if (pluginId && params.path.endsWith(".config.webFetch.apiKey")) return resolveManifestContractOwnerPluginId({
contract: "webFetchProviders",
value: webFetch,
origin: "bundled",
config: params.config
}) === pluginId;
}
return false;
}
function restoreInactiveWebCommandSecretTargets(params) {
if (!hasProviderOverrides(params.providerOverrides)) return params.inactiveRefPaths;
const inactive = new Set(params.inactiveRefPaths);
const defaults = params.sourceConfig.secrets?.defaults;
for (const target of discoverConfigSecretTargetsByIds(params.sourceConfig, params.targetIds)) {
if (params.allowedPaths && !params.allowedPaths.has(target.path)) continue;
if (!isWebCommandSecretPath(target.path)) continue;
const { ref } = resolveSecretInputRef({
value: target.value,
refValue: target.refValue,
defaults
});
if (!ref) continue;
if (params.forcedActivePaths?.has(target.path) || params.optionalActivePaths?.has(target.path)) continue;
if (isProviderOverridePath({
config: params.sourceConfig,
path: target.path,
providerOverrides: params.providerOverrides
})) continue;
inactive.add(target.path);
setPathExistingStrict(params.resolvedConfig, target.pathSegments, target.value);
}
return [...inactive];
}
function filterInactiveRefPaths(params) {
return params.inactiveRefPaths.filter((path) => {
if (params.allowedPaths && !params.allowedPaths.has(path)) return false;
if (params.forcedActivePaths?.has(path) || params.optionalActivePaths?.has(path)) return false;
if (!hasProviderOverrides(params.providerOverrides)) return true;
return !isProviderOverridePath({
config: params.config,
path,
providerOverrides: params.providerOverrides
});
});
}
async function resolveForcedActiveCommandSecretTargets(params) {
const activePaths = /* @__PURE__ */ new Set([...params.forcedActivePaths ?? [], ...params.optionalActivePaths ?? []]);
if (activePaths.size === 0) return;
const context = createResolverContext({
sourceConfig: params.sourceConfig,
env: getActiveSecretsRuntimeEnvState()
});
const defaults = params.sourceConfig.secrets?.defaults;
for (const target of discoverConfigSecretTargetsByIds(params.sourceConfig, params.targetIds)) {
if (params.allowedPaths && !params.allowedPaths.has(target.path)) continue;
if (!activePaths.has(target.path)) continue;
const { ref } = resolveSecretInputRef({
value: target.value,
refValue: target.refValue,
defaults
});
if (!ref) continue;
try {
const resolved = await resolveSecretRefValue(ref, {
config: params.sourceConfig,
env: context.env,
cache: context.cache
});
assertExpectedResolvedSecretValue({
value: resolved,
expected: target.entry.expectedResolvedValue,
errorMessage: target.entry.expectedResolvedValue === "string" ? `${target.path} resolved to a non-string or empty value.` : `${target.path} resolved to an unsupported value type.`
});
setPathExistingStrict(params.resolvedConfig, target.pathSegments, resolved);
} catch {}
}
}
/**
* Resolves command-scoped SecretRef assignments from the active runtime snapshot.
* Provider overrides are evaluated against cloned snapshot config.
*/
/** Resolves command secret assignments from the active prepared runtime snapshot. */
function resolveCommandSecretsFromActiveRuntimeSnapshot(params) {
const activeSnapshot = getActiveSecretsRuntimeSnapshotState();
if (!activeSnapshot) throw new Error("Secrets runtime snapshot is not active.");
if (params.targetIds.size === 0) return Promise.resolve({
assignments: [],
diagnostics: [],
inactiveRefPaths: []
});
return resolveCommandSecretsFromSnapshot({
activeSnapshot,
commandName: params.commandName,
targetIds: params.targetIds,
allowedPaths: params.allowedPaths,
forcedActivePaths: params.forcedActivePaths,
optionalActivePaths: params.optionalActivePaths,
providerOverrides: params.providerOverrides
});
}
async function resolveCommandSecretsFromSnapshot(params) {
const hasOverrides = hasProviderOverrides(params.providerOverrides);
const sourceConfig = applyProviderOverridesToConfig(params.activeSnapshot.sourceConfig, params.providerOverrides);
const resolvedConfig = applyProviderOverridesToConfig(params.activeSnapshot.config, params.providerOverrides);
const context = hasOverrides ? createResolverContext({
sourceConfig,
env: getActiveSecretsRuntimeEnvState()
}) : void 0;
if (context) await resolveRuntimeWebTools({
sourceConfig,
resolvedConfig,
context
});
await resolveForcedActiveCommandSecretTargets({
sourceConfig,
resolvedConfig,
targetIds: params.targetIds,
allowedPaths: params.allowedPaths,
forcedActivePaths: params.forcedActivePaths,
optionalActivePaths: params.optionalActivePaths
});
const warningSource = context?.warnings ?? params.activeSnapshot.warnings;
let inactiveRefPaths = filterInactiveRefPaths({
config: sourceConfig,
providerOverrides: params.providerOverrides,
allowedPaths: params.allowedPaths,
forcedActivePaths: params.forcedActivePaths,
optionalActivePaths: params.optionalActivePaths,
inactiveRefPaths: [...new Set(warningSource.filter((warning) => warning.code === "SECRETS_REF_IGNORED_INACTIVE_SURFACE").map((warning) => warning.path))]
});
inactiveRefPaths = restoreInactiveWebCommandSecretTargets({
sourceConfig,
resolvedConfig,
targetIds: params.targetIds,
inactiveRefPaths,
providerOverrides: params.providerOverrides,
allowedPaths: params.allowedPaths,
forcedActivePaths: params.forcedActivePaths,
optionalActivePaths: params.optionalActivePaths
});
let analyzed = analyzeCommandSecretAssignmentsFromSnapshot({
sourceConfig,
resolvedConfig,
targetIds: params.targetIds,
inactiveRefPaths: new Set(inactiveRefPaths),
...params.allowedPaths ? { allowedPaths: params.allowedPaths } : {}
});
if (hasOverrides) {
const impliedInactivePaths = analyzed.unresolved.filter((entry) => isWebCommandSecretPath(entry.path)).filter((entry) => !isProviderOverridePath({
config: sourceConfig,
path: entry.path,
providerOverrides: params.providerOverrides
})).map((entry) => entry.path);
if (impliedInactivePaths.length > 0) {
inactiveRefPaths = uniqueStrings([...inactiveRefPaths, ...impliedInactivePaths]);
analyzed = analyzeCommandSecretAssignmentsFromSnapshot({
sourceConfig,
resolvedConfig,
targetIds: params.targetIds,
inactiveRefPaths: new Set(inactiveRefPaths),
...params.allowedPaths ? { allowedPaths: params.allowedPaths } : {}
});
}
}
const optionalActiveUnresolvedPaths = analyzed.unresolved.filter((entry) => params.optionalActivePaths?.has(entry.path)).map((entry) => entry.path);
if (optionalActiveUnresolvedPaths.length > 0) {
inactiveRefPaths = uniqueStrings([...inactiveRefPaths, ...optionalActiveUnresolvedPaths]);
analyzed = analyzeCommandSecretAssignmentsFromSnapshot({
sourceConfig,
resolvedConfig,
targetIds: params.targetIds,
inactiveRefPaths: new Set(inactiveRefPaths),
...params.allowedPaths ? { allowedPaths: params.allowedPaths } : {}
});
}
return {
assignments: analyzed.assignments,
diagnostics: analyzed.diagnostics,
inactiveRefPaths
};
}
//#endregion
//#region src/gateway/approval-web-push.ts
const WEB_PUSH_APPROVAL_TIMEOUT_MS = 1e4;
const WEB_PUSH_TERMINAL_TTL_SECONDS = 300;
function approvalPreferences(params) {
const profileId = params.subscription.userProfileId ? resolveUserProfileId(params.subscription.userProfileId) : void 0;
const storedUser = profileId ? getUserPreferences(profileId, [WEB_PUSH_USER_PREFERENCES_KEY], params.stateDir ? { env: {
...process.env,
OPENCLAW_STATE_DIR: params.stateDir
} } : {})[WEB_PUSH_USER_PREFERENCES_KEY] : void 0;
return resolveEffectiveWebPushPreferences({
user: storedUser,
device: params.subscription.devicePreferences
});
}
function approvalNotificationCopy(params) {
const label = params.preferences.label ? `${params.preferences.label} · ` : "";
const agent = params.agentLabel ? ` for ${params.agentLabel}` : "";
if (params.terminal) return {
title: `${label}OpenClaw approval updated`,
body: params.preferences.detailLevel === "private" ? "This approval is no longer pending." : `Approval${agent} is no longer pending.`
};
return {
title: `${label}OpenClaw approval requested`,
body: params.preferences.detailLevel === "private" ? "Open OpenClaw to review this request." : `Open OpenClaw to review an approval${agent}.`
};
}
function approvalWebPushTag(approvalId) {
return `openclaw-approval-${approvalId}`;
}
function approvalWebPushTopic(approvalId) {
return createHash("sha256").update(`openclaw-approval:${approvalId}`).digest("base64url").slice(0, 32);
}
async function deliverBoundApprovalWebPush(params) {
if (params.record.resolvedAtMs !== void 0 || params.record.expiresAtMs <= Date.now()) return null;
const sendWebPushNotifications = await prepareWebPushNotificationSender(params.stateDir);
const cfg = params.getRuntimeConfig();
const subscriptions = listCurrentWebPushTargets({
cfg,
requiredScopes: [APPROVALS_SCOPE$1, READ_SCOPE$1],
stateDir: params.stateDir
}).flatMap((target) => {
const subscription = target.subscription;
const preferences = approvalPreferences({
subscription,
stateDir: params.stateDir
});
const source = isRecord(params.record.request) ? params.record.request : void 0;
const agentId = normalizeOptionalString(source?.agentId);
return webPushCategoryEnabled(preferences, "approval-requested") && !isWebPushQuietHours(preferences) && webPushAgentAllowed(preferences, agentId) && isApprovalRecordVisibleToClient({
record: params.record,
client: webPushTargetClient(target),
cfg
}) ? [subscription] : [];
});
if (subscriptions.length === 0) return null;
const now = Date.now();
if (params.record.resolvedAtMs !== void 0 || params.record.expiresAtMs <= now) return null;
if (!prepareWebPushApprovalDeliveries({
approvalId: params.record.id,
subscriptions,
preparedAtMs: now,
stateDir: params.stateDir
})) return null;
const ttlSeconds = Math.ceil((params.record.expiresAtMs - now) / 1e3);
const source = isRecord(params.record.request) ? params.record.request : void 0;
const agentId = normalizeOptionalString(source?.agentId);
const agentLabel = normalizeWebPushDisplayLabel(agentId);
const requestGroups = /* @__PURE__ */ new Map();
for (const subscription of subscriptions) {
const copy = approvalNotificationCopy({
terminal: false,
preferences: approvalPreferences({
subscription,
stateDir: params.stateDir
}),
agentLabel
});
const key = JSON.stringify(copy);
const group = requestGroups.get(key) ?? {
copy,
subscriptions: []
};
group.subscriptions.push(subscription);
requestGroups.set(key, group);
}
const results = (await Promise.all([...requestGroups.values()].map(({ copy, subscriptions: groupedSubscriptions }) => sendWebPushNotifications({
subscriptions: groupedSubscriptions,
payload: {
...copy,
renotify: false,
tag: approvalWebPushTag(params.record.id),
url: resolveControlUiWebPushUrl(cfg, `approve/${encodeURIComponent(params.record.id)}`)
},
deliveryOptions: {
TTL: ttlSeconds,
urgency: "high",
timeout: WEB_PUSH_APPROVAL_TIMEOUT_MS,
topic: approvalWebPushTopic(params.record.id)
}
})))).flat();
const definitelyRejectedSubscriptionIds = results.filter((result) => !result.ok && result.statusCode !== void 0).map((result) => result.subscriptionId);
deleteWebPushApprovalDeliveryTargets({
approvalId: params.record.id,
subscriptionIds: definitelyRejectedSubscriptionIds,
stateDir: params.stateDir
});
return new Set(results.filter((result) => result.ok || result.statusCode === void 0).map((result) => result.subscriptionId)).size > 0 ? {
record: params.record,
sender: sendWebPushNotifications
} : null;
}
/** Retains successful request targets so terminal state replaces their tagged alert. */
function createApprovalWebPushDelivery(params) {
const deliveriesByApprovalId = /* @__PURE__ */ new Map();
const terminalDeliveriesByApprovalId = /* @__PURE__ */ new Map();
const handleTerminal = (approval) => {
const active = terminalDeliveriesByApprovalId.get(approval.id);
if (active) return active;
const terminalDelivery = (async () => {
const deliveryState = deliveriesByApprovalId.get(approval.id);
deliveriesByApprovalId.delete(approval.id);
const requestDelivery = deliveryState ? await deliveryState.requestPushPromise : null;
const sender = requestDelivery?.sender ?? await prepareWebPushNotificationSender(params.stateDir);
const cfg = params.getRuntimeConfig();
const currentTargets = listCurrentWebPushTargets({
cfg,
requiredScopes: [APPROVALS_SCOPE$1, READ_SCOPE$1],
stateDir: params.stateDir
});
const currentTargetsBySubscriptionId = new Map(currentTargets.map((target) => [target.subscription.subscriptionId, target]));
const subscriptions = listWebPushApprovalDeliveryTargets({
approvalId: approval.id,
stateDir: params.stateDir
});
if (subscriptions.length === 0) return;
const durableLookup = requestDelivery ? null : getOperatorApprovalDetailed({
id: approval.id,
databaseOptions: params.stateDir ? { env: {
...process.env,
OPENCLAW_STATE_DIR: params.stateDir
} } : void 0
});
const durableRecord = durableLookup?.outcome === "found" ? durableLookup.record : null;
const terminalGroups = /* @__PURE__ */ new Map();
const suppressedSubscriptionIds = [];
for (const subscription of subscriptions) {
const target = currentTargetsBySubscriptionId.get(subscription.subscriptionId);
const client = target ? webPushTargetClient(target) : null;
const visible = requestDelivery ? Boolean(client && isApprovalRecordVisibleToClient({
record: requestDelivery.record,
client,
cfg
})) : Boolean(client && durableRecord && canAccessOperatorApproval({
client,
binding: { reviewerDeviceIds: durableRecord.reviewerDeviceIds }
}) && canAccessApprovalSession({
cfg,
client,
sessionKey: durableRecord.source.sessionKey,
agentId: durableRecord.source.agentId
}));
const preferences = approvalPreferences({
subscription,
stateDir: params.stateDir
});
if (!visible) {
suppressedSubscriptionIds.push(subscription.subscriptionId);
continue;
}
const copy = approvalNotificationCopy({
terminal: true,
preferences
});
const key = JSON.stringify(copy);
const group = terminalGroups.get(key) ?? {
copy,
subscriptions: []
};
group.subscriptions.push(subscription);
terminalGroups.set(key, group);
}
const successfulSubscriptionIds = (await Promise.all([...terminalGroups.values()].map(({ copy, subscriptions: groupedSubscriptions }) => sender({
subscriptions: groupedSubscriptions,
payload: {
...copy,
renotify: false,
tag: approvalWebPushTag(approval.id),
url: resolveControlUiWebPushUrl(cfg, `approve/${encodeURIComponent(approval.id)}`)
},
deliveryOptions: {
TTL: WEB_PUSH_TERMINAL_TTL_SECONDS,
urgency: "high",
timeout: WEB_PUSH_APPROVAL_TIMEOUT_MS,
topic: approvalWebPushTopic(approval.id)
}
})))).flat().filter((result) => result.ok).map((result) => result.subscriptionId);
deleteWebPushApprovalDeliveryTargets({
approvalId: approval.id,
subscriptionIds: [...successfulSubscriptionIds, ...suppressedSubscriptionIds],
stateDir: params.stateDir
});
if (successfulSubscriptionIds.length + suppressedSubscriptionIds.length < subscriptions.length) params.log?.warn?.(`approval Web Push terminal replacement reached ${successfulSubscriptionIds.length}/${subscriptions.length - suppressedSubscriptionIds.length} eligible browsers approvalId=${approval.id}`);
})();
terminalDeliveriesByApprovalId.set(approval.id, terminalDelivery);
const releaseTerminalDelivery = () => {
if (terminalDeliveriesByApprovalId.get(approval.id) === terminalDelivery) terminalDeliveriesByApprovalId.delete(approval.id);
};
terminalDelivery.then(releaseTerminalDelivery, releaseTerminalDelivery);
return terminalDelivery;
};
return {
/** Sends a request notification only when at least one browser has a durable binding. */
handleRequested(record) {
if (listBoundWebPushSubscriptions(params.stateDir).length === 0) return false;
const deliveryState = { requestPushPromise: deliverBoundApprovalWebPush({
record,
getRuntimeConfig: params.getRuntimeConfig,
stateDir: params.stateDir
}) };
deliveriesByApprovalId.set(record.id, deliveryState);
return deliveryState.requestPushPromise.then((delivery) => {
if (!delivery && deliveriesByApprovalId.get(record.id) === deliveryState) deliveriesByApprovalId.delete(record.id);
return Boolean(delivery);
}, (error) => {
if (deliveriesByApprovalId.get(record.id) === deliveryState) deliveriesByApprovalId.delete(record.id);
throw error;
});
},
handleResolved: handleTerminal,
handleExpired: handleTerminal,
async recoverTerminalDeliveries() {
let afterApprovalId;
let throughApprovalId;
do {
const page = listTerminalWebPushApprovalDeliveryIds({
stateDir: params.stateDir,
...afterApprovalId ? { afterApprovalId } : {},
...throughApprovalId ? { throughApprovalId } : {}
});
throughApprovalId = page.throughApprovalId ?? void 0;
for (const approvalId of page.approvalIds) await handleTerminal({ id: approvalId });
afterApprovalId = page.nextAfterApprovalId ?? void 0;
} while (afterApprovalId);
}
};
}
//#endregion
//#region src/gateway/exec-approval-ios-push.ts
const APPROVALS_SCOPE = "operator.approvals";
const READ_SCOPE = "operator.read";
const OPERATOR_ROLE = "operator";
function isIosPlatform(platform) {
const normalized = normalizeOptionalLowercaseString(platform) ?? "";
return normalized.startsWith("ios") || normalized.startsWith("ipados");
}
function resolveActiveOperatorToken(device) {
const operatorToken = device.tokens?.[OPERATOR_ROLE];
if (!operatorToken || operatorToken.revokedAtMs) return null;
return operatorToken;
}
function canReceiveApprovalRequests(device) {
const operatorToken = resolveActiveOperatorToken(device);
if (!operatorToken) return false;
return roleScopesAllow({
role: OPERATOR_ROLE,
requestedScopes: [APPROVALS_SCOPE, READ_SCOPE],
allowedScopes: operatorToken.scopes
});
}
function shouldTargetDevice(params) {
if (!isIosPlatform(params.device.platform)) return false;
if (!hasEffectivePairedDeviceRole(params.device, OPERATOR_ROLE)) return false;
if (!params.requireApprovalScope) return true;
return canReceiveApprovalRequests(params.device);
}
async function loadRegisteredTargets(params) {
if (params.deviceIds.length === 0) return [];
return await loadApnsRegistrations(params.deviceIds);
}
async function resolvePairedTargets(params) {
return await loadRegisteredTargets({ deviceIds: (await listDevicePairing()).paired.filter((device) => {
if (!shouldTargetDevice({
device,
requireApprovalScope: params.requireApprovalScope
})) return false;
const operatorToken = resolveActiveOperatorToken(device);
if (params.isTargetVisible && !params.isTargetVisible({
deviceId: device.deviceId,
scopes: operatorToken?.scopes ?? []
})) return false;
return true;
}).map((device) => device.deviceId) });
}
async function resolveDeliveryPlan(params) {
const targets = params.explicitNodeIds?.length ? await loadRegisteredTargets({ deviceIds: params.explicitNodeIds }) : await resolvePairedTargets({
requireApprovalScope: params.requireApprovalScope,
isTargetVisible: params.isTargetVisible
});
if (targets.length === 0) return { targets: [] };
const needsDirect = targets.some((target) => target.registration.transport === "direct");
const needsRelay = targets.some((target) => target.registration.transport === "relay");
let directAuth;
if (needsDirect) {
const auth = await resolveApnsAuthConfigFromEnv(process.env);
if (auth.ok) directAuth = auth.value;
else params.log.warn?.(`${params.approvalKind} approvals: iOS direct APNs auth unavailable: ${auth.error}`);
}
const relayConfigByNodeId = /* @__PURE__ */ new Map();
if (needsRelay) for (const target of targets) {
if (target.registration.transport !== "relay") continue;
const relay = resolveApnsRelayConfigFromEnv(process.env, getRuntimeConfig().gateway, { registrationRelayOrigin: target.registration.relayOrigin });
if (relay.ok) relayConfigByNodeId.set(target.nodeId, relay.value);
else params.log.warn?.(`${params.approvalKind} approvals: iOS relay APNs config unavailable: ${relay.error}`);
}
const relayConfig = relayConfigByNodeId.values().next().value;
return {
targets: targets.filter((target) => target.registration.transport === "direct" ? Boolean(directAuth) : relayConfigByNodeId.has(target.nodeId) && relayConfigByNodeId.get(target.nodeId)?.baseUrl === relayConfig?.baseUrl),
directAuth,
relayConfig
};
}
async function clearStaleApnsRegistrationIfNeeded(params) {
if (shouldClearStoredApnsRegistration({
registration: params.registration,
result: params.result
})) await clearApnsRegistrationIfCurrent({
nodeId: params.nodeId,
registration: params.registration
});
}
async function sendRequestedPushes(params) {
const gatewayDeviceId = loadOrCreateProcessDeviceIdentity().deviceId;
return await sendApprovalPushes({
approvalId: params.request.id,
plan: params.plan,
log: params.log,
approvalKind: params.driver.approvalKind,
label: "request",
logThrown: true,
send: async ({ target, plan }) => await params.driver.sendRequested({
request: params.request,
target,
plan,
gatewayDeviceId
})
});
}
async function sendApprovalPushes(params) {
const results = await Promise.allSettled(params.plan.targets.map(async (target) => {
const result = await params.send({
target,
approvalId: params.approvalId,
plan: params.plan
});
await clearStaleApnsRegistrationIfNeeded({
nodeId: target.nodeId,
registration: target.registration,
result
});
if (!result.ok) params.log.warn?.(`${params.approvalKind} approvals: iOS ${params.label} push failed node=${target.nodeId} status=${result.status} reason=${result.reason ?? "unknown"}`);
return {
nodeId: target.nodeId,
ok: result.ok
};
}));
for (const result of results) if (params.logThrown && result.status === "rejected") {
const message = formatErrorMessage(result.reason);
params.log.warn?.(`${params.approvalKind} approvals: iOS ${params.label} push threw error: ${message}`);
}
return {
attempted: params.plan.targets.length,
delivered: results.filter((result) => result.status === "fulfilled" && result.value.ok).length
};
}
async function sendResolvedPushes(params) {
const gatewayDeviceId = loadOrCreateProcessDeviceIdentity().deviceId;
await sendApprovalPushes({
approvalId: params.approvalId,
plan: params.plan,
log: params.log,
approvalKind: params.driver.approvalKind,
label: "cleanup",
logThrown: false,
send: async ({ target, approvalId, plan }) => await params.driver.sendResolved({
approvalId,
target,
plan,
gatewayDeviceId
})
});
}
function createApprovalIosPushDelivery(params) {
const approvalDeliveriesById = /* @__PURE__ */ new Map();
const pendingDeliveryStateById = /* @__PURE__ */ new Map();
const sendCleanupPushForApproval = async (approvalId) => {
const deliveryState = approvalDeliveriesById.get(approvalId) ?? await pendingDeliveryStateById.get(approvalId);
approvalDeliveriesById.delete(approvalId);
pendingDeliveryStateById.delete(approvalId);
if (!deliveryState?.nodeIds.length) {
params.log.debug?.(`${params.driver.approvalKind} approvals: iOS cleanup push skipped approvalId=${approvalId} reason=missing-targets`);
return;
}
await deliveryState.requestPushPromise;
const plan = await resolveDeliveryPlan({
approvalKind: params.driver.approvalKind,
requireApprovalScope: false,
explicitNodeIds: deliveryState.nodeIds,
log: params.log
});
if (plan.targets.length === 0) return;
await sendResolvedPushes({
approvalId,
plan,
log: params.log,
driver: params.driver
});
};
return {
/** Sends the initial approval notification to visible iOS operator devices. */
async handleRequested(request, opts) {
const deliveryStatePromise = (async () => {
const plan = await resolveDeliveryPlan({
approvalKind: params.driver.approvalKind,
requireApprovalScope: true,
isTargetVisible: opts?.isTargetVisible,
log: params.log
});
if (plan.targets.length === 0) {
approvalDeliveriesById.delete(request.id);
return null;
}
const deliveryState = {
nodeIds: plan.targets.map((target) => target.nodeId),
requestPushPromise: sendRequestedPushes({
request,
plan,
log: params.log,
driver: params.driver
}).catch((err) => {
const message = formatErrorMessage(err);
params.log.error?.(`${params.driver.approvalKind} approvals: iOS request push failed: ${message}`);
return {
attempted: plan.targets.length,
delivered: 0
};
})
};
approvalDeliveriesById.set(request.id, deliveryState);
return deliveryState;
})();
pendingDeliveryStateById.set(request.id, deliveryStatePromise);
const deliveryState = await deliveryStatePromise;
if (pendingDeliveryStateById.get(request.id) === deliveryStatePromise) pendingDeliveryStateById.delete(request.id);
if (!deliveryState) return false;
const { attempted, delivered } = await deliveryState.requestPushPromise;
if (attempted > 0 && delivered === 0) {
params.log.warn?.(`${params.driver.approvalKind} approvals: iOS request push reached no devices approvalId=${request.id} attempted=${attempted}`);
if (approvalDeliveriesById.get(request.id)?.requestPushPromise === deliveryState.requestPushPromise) approvalDeliveriesById.delete(request.id);
return false;
}
return true;
},
/** Sends cleanup wakes for resolved approval requests. */
async handleResolved(resolved) {
await sendCleanupPushForApproval(resolved.id);
},
/** Sends cleanup wakes for expired approval requests. */
async handleExpired(request) {
await sendCleanupPushForApproval(request.id);
}
};
}
/** Creates iOS push delivery for exec approval requests. */
function createExecApprovalIosPushDelivery(params) {
return createApprovalIosPushDelivery({
log: params.log,
driver: {
approvalKind: "exec",
sendRequested: async ({ request, target, plan, gatewayDeviceId }) => target.registration.transport === "direct" ? await sendApnsExecApprovalAlert({
registration: target.registration,
nodeId: target.nodeId,
approvalId: request.id,
gatewayDeviceId,
auth: plan.directAuth
}) : await sendApnsExecApprovalAlert({
registration: target.registration,
nodeId: target.nodeId,
approvalId: request.id,
gatewayDeviceId,
relayConfig: plan.relayConfig
}),
sendResolved: async ({ approvalId, target, plan, gatewayDeviceId }) => target.registration.transport === "direct" ? await sendApnsExecApprovalResolvedWake({
registration: target.registration,
nodeId: target.nodeId,
approvalId,
gatewayDeviceId,
auth: plan.directAuth
}) : await sendApnsExecApprovalResolvedWake({
registration: target.registration,
nodeId: target.nodeId,
approvalId,
gatewayDeviceId,
relayConfig: plan.relayConfig
})
}
});
}
/** Creates iOS push delivery for plugin approval requests. */
function createPluginApprovalIosPushDelivery(params) {
return createApprovalIosPushDelivery({
log: params.log,
driver: {
approvalKind: "plugin",
sendRequested: async ({ request, target, plan, gatewayDeviceId }) => target.registration.transport === "direct" ? await sendApnsPluginApprovalAlert({
registration: target.registration,
nodeId: target.nodeId,
approvalId: request.id,
gatewayDeviceId,
title: request.request.title,
description: request.request.description,
auth: plan.directAuth
}) : await sendApnsPluginApprovalAlert({
registration: target.registration,
nodeId: target.nodeId,
approvalId: request.id,
gatewayDeviceId,
title: request.request.title,
description: request.request.description,
relayConfig: plan.relayConfig
}),
sendResolved: async ({ approvalId, target, plan, gatewayDeviceId }) => target.registration.transport === "direct" ? await sendApnsPluginApprovalResolvedWake({
registration: target.registration,
nodeId: target.nodeId,
approvalId,
gatewayDeviceId,
auth: plan.directAuth
}) : await sendApnsPluginApprovalResolvedWake({
registration: target.registration,
nodeId: target.nodeId,
approvalId,
gatewayDeviceId,
relayConfig: plan.relayConfig
})
}
});
}
//#endregion
//#region src/gateway/lazy-handler.ts
function createLazyHandler(method, loadHandlers) {
return async (opts) => {
const handler = (await loadHandlers())[method];
if (!handler) throw new Error(`lazy gateway handler not found: ${method}`);
await handler(opts);
};
}
//#endregion
//#region src/gateway/operator-approval-placement-grants.ts
const PLACEMENT_GRANT_TTL_MS = 2592e6;
function hasExactAttachedSession(value, sessionId) {
try {
const parsed = JSON.parse(value);
return Array.isArray(parsed) && parsed.length === 1 && parsed[0] === sessionId;
} catch {
return false;
}
}
function isPlacementBindingCurrent(database, binding) {
const placement = find(database.db, binding.sessionId);
if (placement?.state !== "active" || placement.executionMode !== "remote-exec" || placement.agentId !== binding.agentId || placement.sessionKey !== binding.sessionKey || placement.environmentId !== binding.environmentId || placement.activeOwnerEpoch !== binding.ownerEpoch || placement.generation !== binding.placementGeneration || placement.remoteWorkspaceDir !== binding.cwd) return false;
const stateDb = getNodeSqliteKysely(database.db);
const environment = executeSqliteQueryTakeFirstSync(database.db, stateDb.selectFrom("worker_environments").select([
"state",
"node_device_id",
"owner_epoch",
"attached_session_ids_json"
]).where("environment_id", "=", binding.environmentId));
return environment?.state === "attached" && environment.node_device_id === binding.nodeId && environment.owner_epoch === binding.ownerEpoch && hasExactAttachedSession(environment.attached_session_ids_json, binding.sessionId);
}
/** Resolves the exact active node-backed placement from Gateway-owned rows. */
function resolvePlacementStandingGrantBinding(input) {
if (!input.pluginId.trim() || !input.command.trim() || !input.approvalScope.trim() || !input.agentId.trim() || !input.sessionKey.trim() || !input.nodeId.trim() || !input.pairingGeneration.trim()) return null;
return runOpenClawStateWriteTransaction((database) => {
const stateDb = getNodeSqliteKysely(database.db);
const candidates = executeSqliteQuerySync(database.db, stateDb.selectFrom("worker_session_placements").select("session_id").where("agent_id", "=", input.agentId).where("session_key", "=", input.sessionKey).where("state", "=", "active").where("execution_mode", "=", "remote-exec").limit(2)).rows;
if (candidates.length !== 1) return null;
const placement = find(database.db, candidates[0].session_id);
if (placement?.state !== "active" || placement.executionMode !== "remote-exec" || !placement.environmentId || !placement.activeOwnerEpoch || !placement.remoteWorkspaceDir) return null;
const binding = {
pluginId: input.pluginId,
command: input.command,
approvalScope: input.approvalScope,
agentId: input.agentId,
sessionKey: input.sessionKey,
sessionId: placement.sessionId,
nodeId: input.nodeId,
pairingGeneration: input.pairingGeneration,
environmentId: placement.environmentId,
ownerEpoch: placement.activeOwnerEpoch,
placementGeneration: placement.generation,
cwd: placement.remoteWorkspaceDir
};
return isPlacementBindingCurrent(database, binding) ? binding : null;
}, input.databaseOptions);
}
function placementGrantKey(binding) {
return JSON.stringify([
binding.pluginId,
binding.command,
binding.approvalScope,
binding.agentId,
binding.sessionId
]);
}
function resolveRetainedGrant(params) {
const key = placementGrantKey(params.binding);
const grant = params.grants.get(key);
if (!grant) return { outcome: "no-grant" };
if (grant.expiresAtMs <= params.nowMs) {
params.grants.delete(key);
return { outcome: "expired" };
}
if (grant.nodeId !== params.binding.nodeId) {
params.grants.delete(key);
return { outcome: "node-changed" };
}
if (grant.pairingGeneration !== params.binding.pairingGeneration) {
params.grants.delete(key);
return { outcome: "pairing-changed" };
}
return runOpenClawStateWriteTransaction((database) => {
if (!(grant.sessionKey === params.binding.sessionKey && grant.environmentId === params.binding.environmentId && grant.ownerEpoch === params.binding.ownerEpoch && grant.placementGeneration === params.binding.placementGeneration && grant.cwd === params.binding.cwd) || !isPlacementBindingCurrent(database, params.binding)) {
params.grants.delete(key);
return find(database.db, params.binding.sessionId) ? { outcome: "placement-changed" } : { outcome: "placement-missing" };
}
const stateDb = getNodeSqliteKysely(database.db);
const approval = executeSqliteQueryTakeFirstSync(database.db, stateDb.selectFrom("operator_approvals").select([
"status",
"decision",
"runtime_epoch"
]).where("approval_id", "=", grant.mintedByApprovalId));
if (!approval) {
params.grants.delete(key);
return { outcome: "approval-missing" };
}
if (approval.runtime_epoch !== params.runtimeEpoch || approval.status !== "allowed" || approval.decision !== "allow-always") {
params.grants.delete(key);
return { outcome: "approval-not-allow-always" };
}
return {
outcome: "consumed",
grant
};
}, params.databaseOptions);
}
function createPlacementStandingGrantRuntime(params) {
const grants = /* @__PURE__ */ new Map();
const now = params.now ?? Date.now;
return {
resolveBinding: (input) => resolvePlacementStandingGrantBinding({
...input,
databaseOptions: params.databaseOptions
}),
retain: (grant) => {
const maxExpiresAtMs = grant.nowMs + PLACEMENT_GRANT_TTL_MS;
const expiresAtMs = Math.min(grant.expiresAtMs ?? maxExpiresAtMs, maxExpiresAtMs);
if (expiresAtMs <= grant.nowMs) return false;
try {
const retained = runOpenClawStateWriteTransaction((database) => {
if (!isPlacementBindingCurrent(database, grant)) return null;
const stateDb = getNodeSqliteKysely(database.db);
const approval = executeSqliteQueryTakeFirstSync(database.db, stateDb.selectFrom("operator_approvals").select([
"status",
"decision",
"runtime_epoch"
]).where("approval_id", "=", grant.approvalId));
if (approval?.runtime_epoch !== params.runtimeEpoch || approval.status !== "allowed" || approval.decision !== "allow-always") return null;
return {
pluginId: grant.pluginId,
command: grant.command,
approvalScope: grant.approvalScope,
agentId: grant.agentId,
sessionKey: grant.sessionKey,
sessionId: grant.sessionId,
nodeId: grant.nodeId,
pairingGeneration: grant.pairingGeneration,
environmentId: grant.environmentId,
ownerEpoch: grant.ownerEpoch,
placementGeneration: grant.placementGeneration,
cwd: grant.cwd,
mintedByApprovalId: grant.approvalId,
expiresAtMs
};
}, params.databaseOptions);
if (!retained) return false;
grants.set(placementGrantKey(retained), retained);
return true;
} catch {
return false;
}
},
validate: (binding) => resolveRetainedGrant({
grants,
binding,
runtimeEpoch: params.runtimeEpoch,
nowMs: now(),
databaseOptions: params.databaseOptions
}),
consume: (binding) => resolveRetainedGrant({
grants,
binding,
runtimeEpoch: params.runtimeEpoch,
nowMs: now(),
databaseOptions: params.databaseOptions
})
};
}
//#endregion
//#region src/gateway/server-methods/approval-run-cancellation.ts
function cancelMatchingApprovals(params) {
let cancelled = 0;
for (const pending of params.manager.listPendingRecords()) {
if (!params.matches(pending)) continue;
const resolverId = params.reason && params.reason !== "run-aborted" ? params.reason : null;
const result = params.manager.forceDenyDetailed(pending.id, "run-aborted", {
kind: "system",
id: resolverId
}, "cancelled", void 0, false, resolverId);
if (result.outcome === "denied" && result.liveRecord) {
cancelled += 1;
params.publish(result.record, result.liveRecord);
}
}
return cancelled;
}
function cancelAgentRuntimeBoundApprovals(params) {
return cancelMatchingApprovals({
reason: params.reason,
manager: params.manager,
publish: params.publish,
matches: (pending) => {
const bound = pending.agentRuntimeDelegatedAuthority;
return bound?.claimId === params.authority.claimId && bound.lifecycleGeneration === params.authority.lifecycleGeneration && bound.operationalRunInstance.instanceId === params.authority.operationalRunInstance.instanceId && bound.operationalRunInstance.runId === params.authority.operationalRunInstance.runId;
}
});
}
/** Settles approvals whose authoritative worker turn claim has been fenced. */
function cancelWorkerTurnClaimBoundApprovals(params) {
return cancelMatchingApprovals({
manager: params.manager,
publish: params.publish,
matches: (pending) => {
const authority = pending.agentRuntimeDelegatedAuthority;
return authority?.kind === "worker" && params.claim.owner.kind === "worker" && sameWorkerSessionTurnClaim(authority.turnClaim, params.claim);
}
});
}
/** Preserves legacy run-id abort cleanup only for records without delegated authority. */
function cancelUnboundRunApprovals(params) {
return cancelMatchingApprovals({
manager: params.manager,
publish: params.publish,
matches: (pending) => !pending.agentRuntimeDelegatedAuthority && pending.request.runId === params.runId
});
}
//#endregion
//#region src/gateway/server-secrets-reload.ts
async function activateSnapshotIfCurrent(snapshot, expectedRevision, options) {
const runtime = await import("./runtime-BTfxhatB.js");
if (!options.canActivate() || !runtime.activateSecretsRuntimeSnapshotIfCurrent(snapshot, expectedRevision, { runtimeSourceConfig: options.runtimeSourceConfig })) return null;
options.onActivated();
return runtime.getActiveSecretsRuntimeSnapshotRevision();
}
async function restoreSnapshotIfCurrent(snapshot, expectedRevision, ownedSnapshot, onActivated, runtimeSourceConfig) {
if ((await import("./runtime-BTfxhatB.js")).restoreSecretsRuntimeSnapshotIfCurrent(snapshot, expectedRevision, ownedSnapshot, { runtimeSourceConfig })) onActivated();
}
/** Keeps snapshot CAS, generation ownership, and exact account recovery in one transaction. */
function createGatewaySecretsReloader(params) {
const buildReloadPlan = params.buildReloadPlan ?? buildGatewayReloadPlan;
const manager = params.channelManager;
const capturePublication = (generationOwnership) => {
const publishedSnapshotRevision = getActiveSecretsRuntimeSnapshotRevisionState();
const runtimeConfig = getRuntimeConfigSnapshot();
if (!runtimeConfig) throw new Error("Secrets runtime activation did not publish config.");
const isCurrent = () => getActiveSecretsRuntimeSnapshotRevisionState() === publishedSnapshotRevision && getRuntimeConfigSnapshot() === runtimeConfig && isSharedGatewaySessionGenerationOwnershipCurrent(params.sharedGatewaySessionGenerationState, generationOwnership);
const modelPublication = refreshModelRuntimeAfterHotReload({
config: runtimeConfig,
agentIds: void 0,
pluginMetadataSnapshot: void 0,
isPublicationCurrent: isCurrent
});
modelPublication.catch(() => void 0);
return {
publishedSnapshotRevision,
generationOwnership,
modelPublication,
isCurrent
};
};
let reloadInFlight = null;
const runExclusiveReload = (fn, options = {}) => {
if (reloadInFlight) return options.joinInFlight === false ? reloadInFlight.catch(() => void 0).then(() => runExclusiveReload(fn, options)) : reloadInFlight;
const run = (async () => {
try {
return await fn();
} finally {
reloadInFlight = null;
}
})();
reloadInFlight = run;
return run;
};
return (reloadOptions) => runExclusiveReload(async () => {
let transaction;
const touchedTargets = [];
const startTarget = ({ channel, accountId }) => accountId ? manager.startChannel(channel, accountId, { preserveManualStop: true }) : manager.startChannel(channel);
const stopTarget = ({ channel, accountId }) => accountId ? manager.stopChannel(channel, accountId, { manual: false }) : manager.stopChannel(channel);
try {
for (;;) {
const previousSnapshot = getActiveSecretsRuntimeSnapshotState();
if (!previousSnapshot) throw new Error("Secrets runtime snapshot is not active.");
const previousRevision = getActiveSecretsRuntimeSnapshotRevisionState();
const previousRuntimeSourceConfig = getRuntimeConfigSourceSnapshot() ?? void 0;
const previousOwnership = captureSharedGatewaySessionGenerationOwnership(params.sharedGatewaySessionGenerationState);
const previousGeneration = previousOwnership.generation;
const previousRequiredGeneration = params.sharedGatewaySessionGenerationState.required;
const prepared = await params.activateRuntimeSecrets(previousSnapshot.sourceConfig, {
reason: "reload",
activate: false,
publishFailureAsDegraded: true,
forceColdRefKeys: reloadOptions?.forceColdRefKeys,
canPublishFailureAsDegraded: () => getActiveSecretsRuntimeSnapshotRevisionState() === previousRevision
});
const plan = buildReloadPlan(diffConfigPaths(previousSnapshot.config, prepared.config));
const nextGeneration = params.resolveSharedGatewaySessionGenerationForConfig(prepared.config);
const credentialOwners = listActiveCredentialDegradedOwners();
const claimGeneration = () => {
const generationOwnership = claimSharedGatewaySessionGenerationIfOwned(params.sharedGatewaySessionGenerationState, previousOwnership, nextGeneration);
if (!generationOwnership) throw new Error("Secrets runtime activation did not publish ownership.");
if (previousGeneration !== nextGeneration) disconnectStaleSharedGatewayAuthClients({
clients: params.clients,
expectedGeneration: nextGeneration
});
transaction = {
...capturePublication(generationOwnership),
previousSnapshot,
previousRuntimeSourceConfig,
previousGeneration,
previousRequiredGeneration,
prepared,
plan,
credentialOwners,
generationChanged: previousGeneration !== nextGeneration
};
};
const ownsPreviousGeneration = () => isSharedGatewaySessionGenerationOwnershipCurrent(params.sharedGatewaySessionGenerationState, previousOwnership);
const activateIfCurrent = params.activateRuntimeSecrets.activatePreparedSnapshotIfCurrent;
if (activateIfCurrent) {
if (!await activateIfCurrent(prepared, previousRevision, {
reason: "reload",
activate: true,
runtimeSourceConfig: previousRuntimeSourceConfig
}, claimGeneration, ownsPreviousGeneration)) continue;
} else if (await activateSnapshotIfCurrent(prepared, previousRevision, {
canActivate: ownsPreviousGeneration,
onActivated: claimGeneration,
runtimeSourceConfig: previousRuntimeSourceConfig
}) === null) continue;
if (!transaction) throw new Error("Secrets runtime activation did not publish ownership.");
if (!transaction.isCurrent()) throw new Error("secrets.reload was superseded by a newer config write");
break;
}
const { prepared, plan, credentialOwners, generationOwnership, isCurrent } = transaction;
await transaction.modelPublication;
if (!isCurrent()) throw new Error("secrets.reload was superseded by a newer config write");
const targets = [...plan.restartChannels].map((channel) => ({ channel }));
const accountTargets = /* @__PURE__ */ new Map();
for (const [channel, accountIds] of plan.restartChannelAccounts ?? []) {
if (plan.restartChannels.has(channel)) continue;
for (const accountId of accountIds) {
const target = {
channel,
accountId
};
accountTargets.set(`${channel}\0${accountId}`, target);
targets.push(target);
}
}
for (const owner of credentialOwners) {
if (owner.ownerKind !== "account") continue;
const separator = owner.ownerId.indexOf(":");
if (separator < 0) continue;
const channel = owner.ownerId.slice(0, separator);
if (plan.restartChannels.has(channel)) continue;
const accountId = manager.resolveRuntimeAccountId(channel, owner.ownerId.slice(separator + 1));
if (!accountId || manager.isManuallyStopped(channel, accountId)) continue;
const key = `${channel}\0${accountId}`;
const existing = accountTargets.get(key);
if (existing) {
existing.credentialOwnerId = owner.ownerId;
continue;
}
const target = {
channel,
accountId,
credentialOwnerId: owner.ownerId,
inspectOnly: true
};
accountTargets.set(key, target);
targets.push(target);
}
const restartTargets = targets.filter(({ channel, accountId }) => !accountId || !manager.isManuallyStopped(channel, accountId));
if (restartTargets.length > 0) {
const restartChannels = [...new Set(restartTargets.map(({ channel }) => channel))];
if (isTruthyEnvValue(process.env.OPENCLAW_SKIP_CHANNELS) || isTruthyEnvValue(process.env.OPENCLAW_SKIP_PROVIDERS)) throw new Error(`secrets.reload requires restarting channels: ${restartChannels.join(", ")}`);
if (params.getChannelAutostartSuppression?.()) throw new Error(`secrets.reload requires restarting channels but channel autostart is suppressed by crash-loop breaker: ${restartChannels.join(", ")}`);
const failures = [];
for (const target of restartTargets) {
const { channel, accountId, credentialOwnerId, inspectOnly } = target;
const label = accountId ? `${channel} account ${accountId}` : `${channel} channel`;
const assertGenerationOwned = () => {
if (!isCurrent()) throw new Error("secrets.reload was superseded by a newer config write");
};
assertGenerationOwned();
params.logChannels.info(`${inspectOnly ? "reinspecting" : "restarting"} ${label} after secrets reload`);
const touched = {
target,
restarted: false
};
touchedTargets.push(touched);
try {
if (!inspectOnly) {
await stopTarget(target);
assertGenerationOwned();
}
await startTarget(target);
touched.restarted = true;
assertGenerationOwned();
} catch (error) {
if (credentialOwnerId && isTrustedSecretSurfaceUnavailableError(error) && error.ownerKind === "account" && error.ownerId === credentialOwnerId && listActiveCredentialDegradedOwners().some((owner) => owner.ownerKind === "account" && owner.ownerId === credentialOwnerId)) {
touchedTargets.pop();
continue;
}
params.logChannels.info(`failed to restart ${label} after secrets reload`);
failures.push(accountId ? `${channel}:${accountId}` : channel);
}
}
if (failures.length > 0) throw new Error(`failed to restart channels after secrets reload: ${failures.join(", ")}`);
}
if (!isCurrent() || !finalizeOwnedSharedGatewaySessionGeneration(params.sharedGatewaySessionGenerationState, generationOwnership)) throw new Error("secrets.reload was superseded by a newer config write");
return { warningCount: prepared.warnings.length };
} catch (error) {
if (transaction) {
const failedTransaction = transaction;
let restoration;
try {
await restoreSnapshotIfCurrent(failedTransaction.previousSnapshot, failedTransaction.publishedSnapshotRevision, failedTransaction.prepared, () => {
if (replaceOwnedSharedGatewaySessionGenerationState(params.sharedGatewaySessionGenerationState, failedTransaction.generationOwnership, {
current: failedTransaction.previousGeneration,
required: failedTransaction.previousRequiredGeneration
}) && failedTransaction.generationChanged) disconnectStaleSharedGatewayAuthClients({
clients: params.clients,
expectedGeneration: failedTransaction.previousGeneration
});
restoration = capturePublication(captureSharedGatewaySessionGenerationOwnership(params.sharedGatewaySessionGenerationState));
}, failedTransaction.previousRuntimeSourceConfig);
await restoration?.modelPublication;
} catch {
params.logChannels.info("failed to restore model runtime after secrets reload");
}
}
for (const { target, restarted } of touchedTargets) {
const { channel, accountId, inspectOnly } = target;
const label = accountId ? `${channel} account ${accountId}` : `${channel} channel`;
params.logChannels.info(`rolling back ${label} after secrets reload failure`);
try {
if (restarted || inspectOnly) await stopTarget(target);
if (!inspectOnly) await startTarget(target);
} catch {
params.logChannels.info(`failed to roll back ${label} after secrets reload`);
}
}
throw error;
}
}, reloadOptions);
}
//#endregion
//#region src/gateway/server-aux-handlers.ts
/** Create auxiliary gateway handlers that are not part of the core descriptor set. */
function createGatewayAuxHandlers(params) {
const approvalPersistence = { runtimeEpoch: randomUUID() };
const placementStandingGrants = createPlacementStandingGrantRuntime({ runtimeEpoch: approvalPersistence.runtimeEpoch });
const approvalStartupNowMs = Date.now();
closeOrphanedOperatorApprovals({
runtimeEpoch: approvalPersistence.runtimeEpoch,
nowMs: approvalStartupNowMs
});
pruneTerminalOperatorApprovals({ nowMs: approvalStartupNowMs });
const presentationWork = new AsyncWorkScope();
const trackPresentationWork = (run) => presentationWork.track(() => runWithRetainedGatewayRootWork(run));
const createApprovalManager = (approvalKind, resolveAllowedDecisions, resolveStandingGrantMint, retainPlacementStandingGrant) => new ExecApprovalManager({
approvalKind,
persistence: approvalPersistence,
resolveAudienceSessionKeys: resolveApprovalSessionAudienceWithFallback,
resolveAllowedDecisions,
...resolveStandingGrantMint ? { resolveStandingGrantMint } : {},
...retainPlacementStandingGrant ? { retainPlacementStandingGrant } : {},
...params.resolveGrantDefaultExpiresAtMs ? { resolveStandingGrantExpiresAtMs: params.resolveGrantDefaultExpiresAtMs } : {},
onLifecycle: params.onApprovalLifecycle,
onExpired: (record, liveRecord) => publishAuthorityClosure({
kind: approvalKind,
record,
liveRecord
}),
validateAgentRuntimeDelegatedAuthority: params.validateAgentRuntimeDelegatedAuthority,
onError: (error, context) => params.log.error?.(`${context.approvalKind} approval ${context.operation} failed for ${context.approvalId}: ${String(error)}`)
});
const execApprovalManager = createApprovalManager("exec", resolveExecApprovalRequestAllowedDecisions, (request) => {
const source = request.cronExecutionSource;
const operationBinding = request.cronOperationBinding?.trim();
const agentId = request.agentId?.trim();
if (!source || !operationBinding || !agentId) return null;
if (request.runId && params.hasRunAbortMarker?.(request.runId) === true) return null;
return {
kind: "cron",
agentId,
cronJobId: source.jobId,
jobConfigRevision: source.jobConfigRevision,
operationBinding
};
});
const execApprovalForwarder = createExecApprovalForwarder();
const approvalWebPushDelivery = createApprovalWebPushDelivery({
getRuntimeConfig,
log: params.log
});
const execApprovalIosPushDelivery = createExecApprovalIosPushDelivery({ log: params.log });
const loadExecApprovalHandlers = createLazyPromise(() => import("./exec-approval-DvIYPvQX.js").then(({ createExecApprovalHandlers }) => createExecApprovalHandlers(execApprovalManager, {
forwarder: execApprovalForwarder,
iosPushDelivery: execApprovalIosPushDelivery
})), { cacheRejections: true });
const reloadSecrets = createGatewaySecretsReloader(params);
const loadSecretsModule = createLazyPromise(() => import("./secrets-3Me7Vscx.js"), { cacheRejections: true });
const loadSecretStoreWriteService = createLazyPromise(async () => {
const { createSecretStoreWriteService } = await loadSecretsModule();
return createSecretStoreWriteService({
reloadSecrets,
log: params.log
});
}, { cacheRejections: true });
const questionManager = new QuestionManager();
const loadQuestionHandlers = createLazyPromise(async () => {
const [{ createQuestionHandlers }, storeWriteService] = await Promise.all([import("./question-TPg-CTpZ.js"), loadSecretStoreWriteService()]);
return createQuestionHandlers(questionManager, storeWriteService);
}, { cacheRejections: true });
const pluginApprovalManager = createApprovalManager("plugin", resolveCanonicalPluginApprovalRequestAllowedDecisions, (request) => {
if (request.runId && params.hasRunAbortMarker?.(request.runId) === true) return null;
if (request.mcpTool && request.agentId && request.agentId !== "*") {
const servers = getRuntimeConfig().mcp?.servers;
const server = servers && Object.hasOwn(servers, request.mcpTool.server) ? servers[request.mcpTool.server] : void 0;
const mode = server && resolveProjectedMcpCodexToolApprovalMode(request.mcpTool.server, server, server);
if (server && server.enabled !== false && (mode === void 0 || mode === "auto")) return {
kind: "mcp-tool",
agentId: request.agentId,
...request.mcpTool
};
}
if (!request.placementGrant) return null;
return {
kind: "placement",
...request.placementGrant
};
}, placementStandingGrants.retain);
const systemAgentApprovalManager = createApprovalManager("system-agent", () => SYSTEM_AGENT_APPROVAL_DECISIONS);
const approvalManagers = [
execApprovalManager,
pluginApprovalManager,
systemAgentApprovalManager
];
const pluginApprovalIosPushDelivery = createPluginApprovalIosPushDelivery({ log: params.log });
let approvalPublicationContext;
const pendingAuthorityPublications = [];
const publishResolution = ({ kind, record, liveRecord }, context, reason) => {
trackPresentationWork(() => publishAppliedApprovalResolution({
record,
liveRecord,
context,
forwarder: execApprovalForwarder,
...kind === "exec" ? { iosPushDelivery: execApprovalIosPushDelivery } : kind === "plugin" ? { pluginIosPushDelivery: pluginApprovalIosPushDelivery } : {}
}).catch((error) => {
context.logGateway?.error?.(`${kind} approvals: ${reason} publication failed: ${String(error)}`);
}));
};
const publishAuthorityClosure = (publication) => {
if (presentationWork.isClosing) return;
if (approvalPublicationContext) publishResolution(publication, approvalPublicationContext, "authority-close");
else pendingAuthorityPublications.push(publication);
};
const bindApprovalPublicationContext = (context) => {
if (presentationWork.isClosing) return;
approvalPublicationContext = context;
for (const publication of pendingAuthorityPublications.splice(0)) publishAuthorityClosure(publication);
};
const unregisterApprovalAuthorityClosedObserver = registerAgentRunDelegatedAuthorityClosedHandler((authority, approvalReason) => {
for (const manager of approvalManagers) {
const kind = manager.approvalKind;
try {
cancelAgentRuntimeBoundApprovals({
authority,
reason: approvalReason,
manager,
publish: (record, liveRecord) => publishAuthorityClosure({
kind,
record,
liveRecord
})
});
} catch (error) {
params.log.error?.(`${kind} approvals: authority-close settlement failed: ${String(error)}`);
}
}
questionManager.cancelClosedAuthorities();
if (!approvalReason) params.onAgentRunAuthorityClosed?.(authority);
});
const unregisterWorkerTurnClaimClosedObserver = params.registerWorkerTurnClaimClosedHandler?.((claim) => {
for (const manager of approvalManagers) {
const kind = manager.approvalKind;
try {
cancelWorkerTurnClaimBoundApprovals({
claim,
manager,
publish: (record, liveRecord) => publishAuthorityClosure({
kind,
record,
liveRecord
})
});
} catch (error) {
params.log.error?.(`${kind} approvals: worker-claim settlement failed: ${String(error)}`);
}
}
questionManager.cancelClosedAuthorities();
});
const unregisterApprovalAuthorityObserver = () => {
unregisterWorkerTurnClaimClosedObserver?.();
unregisterApprovalAuthorityClosedObserver();
};
const cancelRunBoundApprovals = (target, context) => {
if (presentationWork.isClosing) return 0;
let cancelled = 0;
for (const manager of approvalManagers) {
const kind = manager.approvalKind;
const publish = (record, liveRecord) => publishResolution({
kind,
record,
liveRecord
}, context, "run-abort");
cancelled += typeof target === "string" ? cancelUnboundRunApprovals({
runId: target,
manager,
publish
}) : cancelAgentRuntimeBoundApprovals({
authority: target,
reason: "permission-change",
manager,
publish
});
}
return cancelled;
};
const loadPluginApprovalHandlers = createLazyPromise(() => import("./plugin-approval-DVFsTwdU.js").then(({ createPluginApprovalHandlers }) => createPluginApprovalHandlers(pluginApprovalManager, {
forwarder: execApprovalForwarder,
iosPushDelivery: pluginApprovalIosPushDelivery
})), { cacheRejections: true });
const loadApprovalHandlers = createLazyPromise(() => import("./approval-BxfgWYV4.js").then(({ createApprovalHandlers }) => createApprovalHandlers({
execApprovalManager,
pluginApprovalManager,
systemAgentApprovalManager,
forwarder: execApprovalForwarder,
iosPushDelivery: execApprovalIosPushDelivery,
pluginIosPushDelivery: pluginApprovalIosPushDelivery
})), { cacheRejections: true });
const loadSecretsHandlers = createLazyPromise(async () => {
const [{ createSecretsHandlers }, storeWriteService] = await Promise.all([loadSecretsModule(), loadSecretStoreWriteService()]);
return createSecretsHandlers({
reloadSecrets,
storeWriteService,
log: params.log,
resolveSecrets: async ({ allowedPaths, commandName, forcedActivePaths, optionalActivePaths, providerOverrides, targetIds }) => {
const { assignments, diagnostics, inactiveRefPaths } = await resolveCommandSecretsFromActiveRuntimeSnapshot({
commandName,
targetIds: new Set(targetIds),
...allowedPaths ? { allowedPaths: new Set(allowedPaths) } : {},
...forcedActivePaths ? { forcedActivePaths: new Set(forcedActivePaths) } : {},
...optionalActivePaths ? { optionalActivePaths: new Set(optionalActivePaths) } : {},
...providerOverrides ? { providerOverrides } : {}
});
return {
assignments,
diagnostics,
inactiveRefPaths
};
}
});
}, { cacheRejections: true });
const beginCloseApprovalObservers = () => {
for (const manager of approvalManagers) manager.beginClose();
};
let stopPromise;
const stopOperatorInteractions = () => {
if (!stopPromise) stopPromise = (async () => {
unregisterApprovalAuthorityObserver();
beginCloseApprovalObservers();
for (const manager of approvalManagers) manager.retire();
questionManager.close();
await Promise.all(approvalManagers.map((manager) => manager.drain()));
await presentationWork.drain();
await execApprovalForwarder.stop();
approvalPublicationContext = void 0;
pendingAuthorityPublications.length = 0;
})();
return stopPromise;
};
trackPresentationWork(() => approvalWebPushDelivery.recoverTerminalDeliveries().catch((error) => {
params.log.error?.(`approval Web Push restart recovery failed: ${String(error)}`);
}));
return {
execApprovalManager,
cancelRunBoundApprovals,
forwardPluginApprovalRequest: execApprovalForwarder.handlePluginApprovalRequested,
approvalWebPushDelivery,
pluginApprovalIosPushDelivery,
pluginApprovalManager,
placementStandingGrants,
systemAgentApprovalManager,
bindApprovalPublicationContext,
beginCloseApprovalObservers,
stopOperatorInteractions,
questionManager,
extraHandlers: {
"exec.approval.get": createLazyHandler("exec.approval.get", loadExecApprovalHandlers),
"exec.approval.list": createLazyHandler("exec.approval.list", loadExecApprovalHandlers),
"exec.approval.request": createLazyHandler("exec.approval.request", loadExecApprovalHandlers),
"exec.approval.waitDecision": createLazyHandler("exec.approval.waitDecision", loadExecApprovalHandlers),
"exec.approval.resolve": createLazyHandler("exec.approval.resolve", loadExecApprovalHandlers),
"exec.approval.grants.list": createLazyHandler("exec.approval.grants.list", loadExecApprovalHandlers),
"exec.approval.grants.revoke": createLazyHandler("exec.approval.grants.revoke", loadExecApprovalHandlers),
"plugin.approval.list": createLazyHandler("plugin.approval.list", loadPluginApprovalHandlers),
"plugin.approval.request": createLazyHandler("plugin.approval.request", loadPluginApprovalHandlers),
"plugin.approval.waitDecision": createLazyHandler("plugin.approval.waitDecision", loadPluginApprovalHandlers),
"plugin.approval.resolve": createLazyHandler("plugin.approval.resolve", loadPluginApprovalHandlers),
"approval.get": createLazyHandler("approval.get", loadApprovalHandlers),
"approval.history": createLazyHandler("approval.history", loadApprovalHandlers),
"approval.resolve": createLazyHandler("approval.resolve", loadApprovalHandlers),
"question.request": createLazyHandler("question.request", loadQuestionHandlers),
"question.waitAnswer": createLazyHandler("question.waitAnswer", loadQuestionHandlers),
"question.resolve": createLazyHandler("question.resolve", loadQuestionHandlers),
"question.get": createLazyHandler("question.get", loadQuestionHandlers),
"question.list": createLazyHandler("question.list", loadQuestionHandlers),
"secrets.reload": createLazyHandler("secrets.reload", loadSecretsHandlers),
"secrets.resolve": createLazyHandler("secrets.resolve", loadSecretsHandlers),
"secrets.store.list": createLazyHandler("secrets.store.list", loadSecretsHandlers),
"secrets.store.set": createLazyHandler("secrets.store.set", loadSecretsHandlers),
"secrets.store.delete": createLazyHandler("secrets.store.delete", loadSecretsHandlers)
}
};
}
//#endregion
export { createGatewayAuxHandlers };