openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,188 lines • 51.6 kB
JavaScript
import { r as createLazyRuntimeModule } from "./lazy-runtime-CgCh8H_K.js";
import { n as isTruthyEnvValue } from "./env-M3R40TOb.js";
import { o as listAgentIds } from "./agent-scope-config-DcbEhP0R.js";
import { w as resolveStateDir } from "./paths-D2sRr1a_.js";
import { r as getPluginModuleLoaderStats } from "./plugin-module-loader-cache-lf3kAaBw.js";
import { t as createDeferredCore } from "./deferred-D0La5CRk.js";
import { f as withPluginRuntimeRegistryScope } from "./gateway-request-scope-BCMYlsDI.js";
import { a as getActiveGatewayRootWorkCount, b as runWithGatewayIndependentRootWorkAdmission, l as isGatewayRestartDrainError } from "./gateway-work-admission-R1IpuDim.js";
import { v as sweepSessionStateWatchNotices } from "./session-state-events-DNCKmH78.js";
import { s as hasRestartSentinel } from "./restart-sentinel-FmKS9LVR.js";
import { r as loadGetReplyFromConfigRuntime } from "./dispatch-from-config.runtime.js";
import { a as canReadDetailedUpdateMetadata, o as projectUpdateAvailable, r as GATEWAY_EVENT_UPDATE_AVAILABLE } from "./events-DFzVQFw-.js";
import { n as wakeUpdateRunWatcher, t as startUpdateRunWatcher } from "./update-run-watcher-CTHy8LKD.js";
import { t as scheduleGatewayIdleTask } from "./server-idle-task-sVCvNzia.js";
import { n as measureStartup } from "./server-startup-trace-DfOgyIYF.js";
import { t as beginMacOSSystemCaWarmupOnce } from "./system-ca-warmup-dlHipq-P.js";
import { t as resolveInternalHookSelection } from "./configured-DEfAaEqY.js";
import { monitorEventLoopDelay, performance } from "node:perf_hooks";
import { setTimeout as setTimeout$1 } from "node:timers/promises";
//#region src/gateway/server-startup-context-cache-prewarm.ts
const CONTEXT_CACHE_PREWARM_START_DELAY_MS = 5e3;
const CONTEXT_CACHE_PREWARM_RETRY_DELAY_MS = 250;
function scheduleContextCachePrewarm(params) {
let stopped = false;
const warm = async () => {
if (stopped) return;
const { prewarmContextWindowCacheAfterReady } = await import("./context-M2p7wU0f.js");
if (!stopped) await prewarmContextWindowCacheAfterReady({
config: params.getConfig(),
isCancelled: () => stopped
});
};
const idleTask = scheduleGatewayIdleTask({
delayMs: CONTEXT_CACHE_PREWARM_START_DELAY_MS,
retryDelayMs: CONTEXT_CACHE_PREWARM_RETRY_DELAY_MS,
isClosing: () => stopped,
isBusy: () => getActiveGatewayRootWorkCount({ excludeCurrent: true }) > 0,
run: () => params.startupTrace ? params.startupTrace.measure("post-ready.context-window-cache", warm) : warm(),
log: params.log,
errorMessage: "post-ready.context-window-cache failed after gateway ready"
});
return { stop: () => {
stopped = true;
return idleTask.stop();
} };
}
//#endregion
//#region src/gateway/server-startup-handler-prewarm.ts
const SIDEBAR_PREWARM_MAX_SESSION_ENTRIES = 2e3;
const GATEWAY_HANDLER_PREWARM_RETRY_DELAY_MS = 250;
async function prewarmGatewaySessionListData(cfg, agentId) {
const [{ loadCombinedSessionStoreForGatewayCore }, { listSessionsFromStoreAsync }] = await Promise.all([import("./combined-store-gateway-DZ-MbZhK.js"), import("./session-utils-list-CbXPyOH-.js")]);
const { durableStorePath, storePath, store } = loadCombinedSessionStoreForGatewayCore(cfg, {
agentId,
projection: "list"
});
await listSessionsFromStoreAsync({
cfg,
durableStorePath,
storePath,
store,
opts: {
agentId,
configuredAgentsOnly: true,
includeDerivedTitles: true,
includeGlobal: true,
includeUnknown: true,
limit: 200
}
});
}
function dashboardDataPrewarmItems(cfg, log) {
const agentIds = listAgentIds(cfg);
let sessionDataPrewarmChecked = false;
let sessionDataPrewarmAllowed = false;
const shouldPrewarmSessionData = async () => {
if (sessionDataPrewarmChecked) return sessionDataPrewarmAllowed;
sessionDataPrewarmChecked = true;
const { canPrewarmCombinedSessionStoresForGateway } = await import("./combined-store-gateway-DZ-MbZhK.js");
sessionDataPrewarmAllowed = canPrewarmCombinedSessionStoresForGateway(cfg, {
agentIds,
maxRows: SIDEBAR_PREWARM_MAX_SESSION_ENTRIES
});
if (!sessionDataPrewarmAllowed) log.info?.(`skipping optional dashboard session prewarm: combined stores exceed ${SIDEBAR_PREWARM_MAX_SESSION_ENTRIES} rows`);
return sessionDataPrewarmAllowed;
};
return [...agentIds.map((agentId) => ({
name: `sessions.${agentId}`,
load: async () => {
if (!await shouldPrewarmSessionData()) return;
await prewarmGatewaySessionListData(cfg, agentId);
}
})), {
name: "plugins",
load: async () => {
const { listManagedPlugins } = await import("./management-service-DrzJyi5c.js");
await listManagedPlugins({ config: cfg });
}
}];
}
function scheduleGatewayHandlerPrewarm(params) {
const items = params.items ?? dashboardDataPrewarmItems(params.cfgAtStart, params.log);
let stopped = false;
let nextIndex = 0;
let currentItemName = "unknown";
let idleTask;
const scheduleNext = () => {
if (stopped || nextIndex >= items.length) return;
(async () => {
await params.waitForPostReadyWork?.();
if (stopped) return;
const item = items[nextIndex++];
if (!item) return;
currentItemName = item.name;
const load = () => item.load();
idleTask = scheduleGatewayIdleTask({
delayMs: 0,
retryDelayMs: GATEWAY_HANDLER_PREWARM_RETRY_DELAY_MS,
isClosing: () => stopped,
isBusy: () => getActiveGatewayRootWorkCount({ excludeCurrent: true }) > 0,
run: async () => {
try {
await (params.startupTrace ? params.startupTrace.measure(`post-ready.gateway-data.${item.name}`, load) : load());
} finally {
Promise.resolve(idleTask?.stop()).then(scheduleNext, scheduleNext);
}
},
log: params.log,
errorMessage: `post-ready gateway data prewarm failed for ${item.name}`
});
})().catch((err) => {
params.log.warn(`post-ready gateway data prewarm failed for ${currentItemName}: ${String(err)}`);
scheduleNext();
});
};
scheduleNext();
return { stop: () => {
stopped = true;
return idleTask?.stop();
} };
}
//#endregion
//#region src/gateway/server-startup-outcomes.ts
const GATEWAY_STARTUP_SUBSYSTEMS = [
"internal-hooks",
"internal-startup-hook",
"gateway-start-hooks",
"gmail-watcher",
"gmail-model"
];
function skipped(subsystem, reason) {
return {
subsystem,
status: "skipped",
reason
};
}
function resolveOutcomePlan(params) {
const internalHooks = params.cfg.hooks?.internal?.enabled === false ? "hooks-disabled" : resolveInternalHookSelection(params.cfg).configured ? "configured" : "not-configured";
const gmailWatcher = !params.cfg.hooks?.enabled ? "hooks-disabled" : !params.cfg.hooks.gmail?.account ? "no-gmail-account" : isTruthyEnvValue((params.env ?? process.env).OPENCLAW_SKIP_GMAIL_WATCHER) ? "disabled-by-environment" : "scheduled";
return {
internalHooks,
gatewayStartHooks: params.gatewayStartHooks,
gmailWatcher,
gmailModel: params.cfg.hooks?.gmail?.model ? "scheduled" : "not-configured"
};
}
/** Create the complete initial outcome set; awaited startup work may replace entries later. */
function createGatewayStartupOutcomeRecorder(params) {
const plan = resolveOutcomePlan(params);
const internalHooks = plan.internalHooks === "configured" ? skipped("internal-hooks", "no-handlers-loaded") : skipped("internal-hooks", plan.internalHooks);
const internalStartupHook = plan.internalHooks === "hooks-disabled" ? skipped("internal-startup-hook", "hooks-disabled") : skipped("internal-startup-hook", "no-handlers-loaded");
const outcomes = /* @__PURE__ */ new Map([
["internal-hooks", internalHooks],
["internal-startup-hook", internalStartupHook],
["gateway-start-hooks", plan.gatewayStartHooks ? {
subsystem: "gateway-start-hooks",
status: "scheduled"
} : skipped("gateway-start-hooks", "no-handlers-loaded")],
["gmail-watcher", plan.gmailWatcher === "scheduled" ? {
subsystem: "gmail-watcher",
status: "scheduled"
} : skipped("gmail-watcher", plan.gmailWatcher)],
["gmail-model", plan.gmailModel === "scheduled" ? {
subsystem: "gmail-model",
status: "scheduled"
} : skipped("gmail-model", "not-configured")]
]);
return {
record: (outcome) => {
outcomes.set(outcome.subsystem, outcome);
},
snapshot: () => GATEWAY_STARTUP_SUBSYSTEMS.flatMap((subsystem) => {
const outcome = outcomes.get(subsystem);
return outcome ? [outcome] : [];
})
};
}
/** Format outcomes in canonical order regardless of collection order. */
function formatGatewayStartupOutcomes(outcomes) {
const bySubsystem = new Map(outcomes.map((outcome) => [outcome.subsystem, outcome]));
return `gateway startup outcomes: ${GATEWAY_STARTUP_SUBSYSTEMS.flatMap((subsystem) => {
const outcome = bySubsystem.get(subsystem);
if (!outcome) return [];
const detail = "reason" in outcome ? ` (${outcome.reason})` : "";
return `${outcome.subsystem}=${outcome.status}${detail}`;
}).join("; ")}`;
}
//#endregion
//#region src/gateway/server-startup-post-attach.ts
const ACP_BACKEND_READY_TIMEOUT_MS = 5e3;
const ACP_BACKEND_READY_POLL_MS = 50;
const PROVIDER_AUTH_PREWARM_START_DELAY_MS = 5e3;
const PROVIDER_AUTH_REWARM_DELAY_MS = 1e3;
const DEFERRED_SIDECAR_START_DELAY_MS = 100;
const SKIP_STARTUP_MODEL_PREWARM_ENV = "OPENCLAW_SKIP_STARTUP_MODEL_PREWARM";
const loadMainSessionRestartRecoveryModule = createLazyRuntimeModule(() => import("./main-session-restart-recovery-btGC4Qn8.js"));
const loadMainSessionRestartRecoveryMarkingModule = createLazyRuntimeModule(() => import("./main-session-restart-recovery-marking-5GikjpEh.js"));
const loadAgentDefaultsModule = createLazyRuntimeModule(() => import("./defaults-RjT9WtG0.js"));
const loadAgentModelSelectionModule = createLazyRuntimeModule(() => import("./model-selection-DEDE6TxI.js"));
const loadInternalHooksModule = createLazyRuntimeModule(() => import("./internal-hooks-CmaF6-3-.js"));
const loadGatewayRestartSentinelModule = createLazyRuntimeModule(() => import("./server-restart-sentinel-a6E8pkSs.js"));
/** Measure provider-auth warming without letting event-loop stalls hide in wall time. */
async function measureProviderAuthWarm(run) {
const eventLoopDelay = monitorEventLoopDelay({ resolution: 10 });
eventLoopDelay.enable();
const startMs = performance.now();
try {
await run();
} finally {
eventLoopDelay.disable();
}
return {
elapsedMs: performance.now() - startMs,
eventLoopMaxMs: eventLoopDelay.max / 1e6
};
}
function formatProviderAuthWarmMetrics(metrics) {
return `in ${metrics.elapsedMs.toFixed(0)}ms eventLoopMax=${metrics.eventLoopMaxMs.toFixed(1)}ms`;
}
function shouldCheckRestartSentinel(env = process.env) {
return !env.VITEST && env.NODE_ENV !== "test";
}
function shouldSkipStartupModelPrewarm(env = process.env) {
return isTruthyEnvValue(env[SKIP_STARTUP_MODEL_PREWARM_ENV]);
}
function scheduleProviderAuthStatePrewarm(params) {
let stopped = false;
let startupTimer;
let rewarmTimer;
let rewarmInFlight = false;
let pendingRewarmReason;
const isStopped = () => stopped;
const delayMs = params.delayMs ?? PROVIDER_AUTH_PREWARM_START_DELAY_MS;
const logProviderAuthWarmFailure = (operation, error) => {
if (!isGatewayRestartDrainError(error)) params.log.warn(`provider auth state ${operation} failed: ${String(error)}`);
};
runWithGatewayIndependentRootWorkAdmission(async () => {
const [{ setAuthProfileFailureHook }, { clearCurrentProviderAuthState }] = await Promise.all([import("./failure-hook-DL7zdDho.js"), import("./model-provider-auth-state-BpSKvECc.js")]);
const loadProviderAuthWarmModule = () => import("./model-provider-auth-Cv35e_tv.js");
const runRewarm = async (reason) => {
await runWithGatewayIndependentRootWorkAdmission(async () => {
if (isStopped()) return;
const cfg = params.getConfig();
rewarmInFlight = true;
try {
const { warmCurrentProviderAuthStateOffMainThread } = await loadProviderAuthWarmModule();
const metrics = await measureProviderAuthWarm(() => warmCurrentProviderAuthStateOffMainThread(cfg, { isCancelled: isStopped }));
if (isStopped()) return;
params.log.info(`provider auth state re-warmed (${reason}) ${formatProviderAuthWarmMetrics(metrics)}`);
} catch (err) {
logProviderAuthWarmFailure("rewarm", err);
} finally {
rewarmInFlight = false;
const nextReason = pendingRewarmReason;
pendingRewarmReason = void 0;
if (nextReason && !isStopped()) scheduleAuthMapRewarm(nextReason);
}
}, "runtime:provider-auth-rewarm");
};
const scheduleAuthMapRewarm = (reason) => {
if (isStopped()) return;
pendingRewarmReason = reason;
if (rewarmTimer || rewarmInFlight) return;
rewarmTimer = setTimeout(() => {
rewarmTimer = void 0;
const nextReason = pendingRewarmReason ?? reason;
pendingRewarmReason = void 0;
runRewarm(nextReason).catch((error) => logProviderAuthWarmFailure("rewarm", error));
}, PROVIDER_AUTH_REWARM_DELAY_MS);
rewarmTimer.unref?.();
};
if (isStopped()) return;
setAuthProfileFailureHook(() => {
if (isStopped()) return;
clearCurrentProviderAuthState();
scheduleAuthMapRewarm("auth-profile-failure");
});
if (!params.startupWarmEnabled) return;
startupTimer = setTimeout(() => {
runWithGatewayIndependentRootWorkAdmission(async () => {
if (isStopped()) return;
const cfg = params.getConfig();
const { warmCurrentProviderAuthStateOffMainThread } = await loadProviderAuthWarmModule();
const metrics = await measureProviderAuthWarm(() => warmCurrentProviderAuthStateOffMainThread(cfg, { isCancelled: isStopped }));
if (isStopped()) return;
params.log.info(`provider auth state pre-warmed ${formatProviderAuthWarmMetrics(metrics)}`);
}, "startup:provider-auth-prewarm").catch((error) => logProviderAuthWarmFailure("pre-warm", error));
}, Math.max(0, delayMs));
startupTimer.unref?.();
}, "startup:provider-auth").catch((error) => logProviderAuthWarmFailure("pre-warm setup", error));
return { stop: () => {
stopped = true;
if (startupTimer) {
clearTimeout(startupTimer);
startupTimer = void 0;
}
if (rewarmTimer) {
clearTimeout(rewarmTimer);
rewarmTimer = void 0;
}
} };
}
function schedulePostReadySidecarTask(params) {
let stopped = false;
const abortController = new AbortController();
const isStopped = () => stopped;
const handle = setImmediate(() => {
(async () => {
await params.waitForPostReadyWork?.();
if (isStopped()) return;
await runWithGatewayIndependentRootWorkAdmission(async () => {
await measureStartup(params.startupTrace, params.name, () => params.run(isStopped, abortController.signal));
}, `startup:${params.name}`);
})().catch((err) => {
params.log.warn(`${params.name} failed after gateway ready: ${String(err)}`);
});
});
handle.unref?.();
return { stop: async () => {
stopped = true;
abortController.abort();
clearImmediate(handle);
await params.stop?.();
} };
}
function scheduleGatewayGenerationTimer(params) {
let stopped = false;
let timer;
const isStopped = () => stopped;
timer = setTimeout(() => {
timer = void 0;
if (isStopped()) return;
runWithGatewayIndependentRootWorkAdmission(async () => {
await params.run(isStopped);
}, params.origin).catch((err) => {
if (!isStopped()) params.onError(err);
});
}, params.delayMs);
timer.unref?.();
return { stop: () => {
stopped = true;
if (timer) {
clearTimeout(timer);
timer = void 0;
}
} };
}
function scheduleRestartSentinelWakeAfterReady(params) {
return scheduleGatewayGenerationTimer({
delayMs: 750,
origin: "restart-sentinel:wake",
run: async (isStopped) => {
const { scheduleRestartSentinelWake } = await loadGatewayRestartSentinelModule();
if (isStopped()) return;
await scheduleRestartSentinelWake({ deps: params.deps });
},
onError: (err) => params.log.warn(`restart sentinel wake failed to schedule: ${String(err)}`)
});
}
function scheduleTranscriptsAutoStartSidecar(params) {
let stopTranscriptsAutoStart;
return schedulePostReadySidecarTask({
startupTrace: params.startupTrace,
name: "sidecars.transcripts-auto-start",
log: params.log,
waitForPostReadyWork: params.waitForPostReadyWork,
run: async (isStopped) => {
const { createTranscriptsAutoStartService } = await import("./transcripts-tool-BLwxNPsF.js");
if (isStopped()) return;
const service = createTranscriptsAutoStartService({
config: params.cfg,
stateDir: resolveStateDir(),
logger: params.log
});
stopTranscriptsAutoStart = () => service.stop();
service.start();
},
stop: async () => {
await stopTranscriptsAutoStart?.();
}
});
}
async function hasRestartSentinelFast(env = process.env) {
return await hasRestartSentinel(env);
}
async function refreshLatestUpdateRestartSentinelIfPresent() {
if (!await hasRestartSentinelFast()) return null;
return await (await loadGatewayRestartSentinelModule()).refreshLatestUpdateRestartSentinel();
}
function hasGatewayStartHooks(pluginRegistry) {
return pluginRegistry.typedHooks.some((hook) => hook.hookName === "gateway_start");
}
async function hasGatewayStartupInternalHookListeners() {
const { hasInternalHookListeners } = await loadInternalHooksModule();
return hasInternalHookListeners("gateway", "startup");
}
async function waitForAcpRuntimeBackendReady(params) {
const { getAcpRuntimeBackend } = await import("./registry-e7sDH4YC.js");
const timeoutMs = params.timeoutMs ?? ACP_BACKEND_READY_TIMEOUT_MS;
const pollMs = params.pollMs ?? ACP_BACKEND_READY_POLL_MS;
const deadline = Date.now() + timeoutMs;
do {
const backend = getAcpRuntimeBackend(params.backendId);
if (backend) try {
if (!backend.healthy || backend.healthy()) return true;
} catch {}
await setTimeout$1(pollMs, void 0, { ref: false });
} while (Date.now() < deadline);
return false;
}
async function prewarmConfiguredPrimaryModel(params) {
await publishConfiguredModelRuntimeSnapshots(params);
}
async function hydrateConfiguredExternalCliAuth(params) {
const deps = await params.deps ?? await Promise.all([
import("./agent-scope-BHLMs19p.js"),
import("./prepared-model-runtime.configured-BtBX4nPK.js"),
import("./store-DNxWSPNc.js"),
import("./external-cli-discovery-DRqPQqMy.js")
]).then(([scope, configured, store, external]) => ({
listAgentIds: scope.listAgentIds,
resolveAgentDir: scope.resolveAgentDir,
collectConfiguredRefs: configured.collectPreparedModelRuntimeConfiguredRefs,
hydrate: (cfg, agentDir, providers) => {
const discovery = external.externalCliDiscoveryForProviders({
cfg,
providers
});
if (discovery.mode === "none") return;
store.ensureAuthProfileStore(agentDir, {
config: cfg,
externalCli: discovery,
allowKeychainPrompt: false,
readOnly: true,
syncExternalCli: false
});
}
}));
const cfg = params.getConfig();
const hydratedDirs = /* @__PURE__ */ new Set();
for (const agentId of deps.listAgentIds(cfg)) {
const providers = deps.collectConfiguredRefs(cfg, agentId).flatMap(({ value }) => {
const separator = value.indexOf("/");
return separator > 0 ? [value.slice(0, separator)] : [];
});
const agentDir = deps.resolveAgentDir(cfg, agentId);
if (providers.length === 0 || hydratedDirs.has(agentDir)) continue;
hydratedDirs.add(agentDir);
try {
deps.hydrate(cfg, agentDir, providers);
} catch (error) {
params.log.warn(`startup external CLI auth hydration failed for agent ${agentId}: ${String(error)}`);
}
}
return cfg;
}
async function publishConfiguredModelRuntimeSnapshots(params) {
const { refreshPreparedModelRuntimeSnapshots } = await import("./prepared-model-runtime-D7Vr1U41.js");
if (params.isCurrent?.() === false) return;
await refreshPreparedModelRuntimeSnapshots(params.getConfig ?? params.cfg, {
gatewayLifecycle: true,
catalogMode: "static",
allowGatewaySubagentBinding: true,
...params.isCurrent ? { isPublicationCurrent: params.isCurrent } : {},
...params.pluginMetadataSnapshot ? { pluginMetadataSnapshot: params.pluginMetadataSnapshot } : {},
...params.workspaceDir ? { defaultWorkspaceDir: params.workspaceDir } : {},
...params.startupTrace ? { onBuildStats: (stats) => params.startupTrace?.detail("sidecars.model-runtime-build", [
["agentCount", stats.agentCount],
["workspaceGroupCount", stats.workspaceGroupCount],
["configuredFactsGroupCount", stats.configuredFactsGroupCount],
["catalogSourceCount", stats.catalogSourceCount],
["credentialGroupCount", stats.credentialGroupCount],
["catalogGroupCount", stats.catalogGroupCount],
["runtimeRegistryCount", stats.runtimeRegistryCount],
["configuredRuntimeModelCount", stats.configuredRuntimeModelCount],
["generatedCatalogPluginCount", stats.generatedCatalogPluginCount],
["generatedCatalogReadCount", stats.generatedCatalogReadCount],
["workspaceFactsMs", stats.workspaceFactsMs],
["runtimePluginMs", stats.runtimePluginMs],
["pluginMetadataMs", stats.pluginMetadataMs],
["staticProviderCatalogMs", stats.staticProviderCatalogMs],
["ambientCredentialsMs", stats.ambientCredentialsMs],
["agentFactsMs", stats.agentFactsMs],
["configuredProjectionMs", stats.configuredProjectionMs],
["catalogSourceMs", stats.catalogSourceMs],
["registryMs", stats.registryMs],
["sourceConcurrencyLimitCount", stats.sourceConcurrencyLimit],
["fullCatalogConcurrencyLimitCount", stats.fullCatalogConcurrencyLimit]
]) } : {}
});
}
async function publishStartupModelRuntime(params, prewarm = prewarmConfiguredPrimaryModel) {
await (shouldSkipStartupModelPrewarm() ? publishConfiguredModelRuntimeSnapshots : prewarm)(params);
}
/** Start post-ready sidecars such as channels, hooks, plugin services, and cleanup tasks. */
async function startGatewaySidecars(params) {
const postReadySidecars = [];
const internalHooksConfigured = resolveInternalHookSelection(params.cfg).configured;
await measureStartup(params.startupTrace, "sidecars.internal-hooks", async () => {
try {
const { prepareInternalHooks } = await import("./loader-CD3EerDC.js");
const prepared = await prepareInternalHooks(params.cfg, params.defaultWorkspaceDir, { failureMode: "best-effort" });
if (params.shouldCreatePostReadySidecars?.() === false || !prepared.commit({ initial: true })) {
params.startupOutcomes?.record({
subsystem: "internal-hooks",
status: "skipped",
reason: "superseded"
});
return;
}
if (!internalHooksConfigured) return;
const { loadedCount } = prepared;
if (loadedCount > 0) {
params.startupOutcomes?.record({
subsystem: "internal-hooks",
status: "loaded"
});
params.logHooks.info(`loaded ${loadedCount} internal hook handler${loadedCount > 1 ? "s" : ""}`);
} else params.startupOutcomes?.record({
subsystem: "internal-hooks",
status: "skipped",
reason: "no-handlers-loaded"
});
} catch (err) {
params.startupOutcomes?.record({
subsystem: "internal-hooks",
status: "failed",
reason: "see earlier log"
});
params.logHooks.error(`failed to load hooks: ${String(err)}`);
}
});
const mainSessionRecoveryStartupCheckedStorePaths = params.mainSessionRecoveryStartupCheckedStorePaths ?? /* @__PURE__ */ new Set();
const skipChannels = isTruthyEnvValue(process.env.OPENCLAW_SKIP_CHANNELS) || isTruthyEnvValue(process.env.OPENCLAW_SKIP_PROVIDERS);
await measureStartup(params.startupTrace, "sidecars.main-session-recovery", async () => {
try {
const { markStartupOrphanedMainSessionsForRecovery } = await measureStartup(params.startupTrace, "sidecars.main-session-recovery-load", loadMainSessionRestartRecoveryMarkingModule);
await measureStartup(params.startupTrace, "sidecars.main-session-recovery-scan", () => markStartupOrphanedMainSessionsForRecovery({
cfg: params.cfg,
startupCheckedStorePaths: mainSessionRecoveryStartupCheckedStorePaths
}));
} catch (err) {
params.log.warn(`main-session startup orphan marking failed before channel startup: ${String(err)}`);
}
});
const getModelRuntimeConfig = params.getModelRuntimeConfig ?? (() => params.cfg);
if (await params.pluginRuntimeClaim?.waitForUnblocked() !== false) await measureStartup(params.startupTrace, "sidecars.model-runtime", () => withPluginRuntimeRegistryScope(params.pluginRegistry, () => publishStartupModelRuntime({
cfg: params.cfg,
getConfig: async () => await measureStartup(params.startupTrace, "sidecars.model-auth", () => hydrateConfiguredExternalCliAuth({
getConfig: getModelRuntimeConfig,
log: params.log
})),
isCurrent: params.pluginRuntimeClaim?.isCurrent,
...params.pluginMetadataSnapshot ? { pluginMetadataSnapshot: params.pluginMetadataSnapshot } : {},
workspaceDir: params.defaultWorkspaceDir,
log: params.log,
startupTrace: params.startupTrace
}, params.prewarmPrimaryModel)));
await measureStartup(params.startupTrace, "sidecars.reply-runtime", async () => {
const { prewarmConfigDrivenReplyRuntime } = await loadGetReplyFromConfigRuntime();
await prewarmConfigDrivenReplyRuntime();
});
await measureStartup(params.startupTrace, "sidecars.chat-metadata", async () => {
await params.refreshChatMetadata?.();
});
const shouldStartChannels = params.shouldStartChannels?.() !== false;
await measureStartup(params.startupTrace, "sidecars.channels", async () => {
const channelStart = skipChannels ? measureStartup(params.startupTrace, "sidecars.channel-skip", () => params.logChannels.info("skipping channel start (OPENCLAW_SKIP_CHANNELS=1 or OPENCLAW_SKIP_PROVIDERS=1)")) : shouldStartChannels ? measureStartup(params.startupTrace, "sidecars.channel-start", params.startChannels).catch((err) => params.logChannels.error(`channel startup failed: ${String(err)}`)) : Promise.resolve();
const accountStartGateRelease = shouldStartChannels ? params.onChannelsStarted?.() : void 0;
await Promise.all([accountStartGateRelease, channelStart]);
});
await params.pluginRuntimeClaim?.waitForUnblocked();
const shouldStartPluginServices = params.pluginRuntimeClaim?.isCurrent() !== false && params.shouldStartPluginServices?.() !== false;
let pluginServicesStopRequested = false;
let resolvePluginServicesOwner;
if (shouldStartPluginServices) {
const ownedPluginServices = createDeferredCore();
const pluginServicesOwner = {
reload: async (config, serviceIds) => {
const handle = await ownedPluginServices.promise;
if (pluginServicesStopRequested || !handle) throw new Error("Plugin services are stopping");
await handle.reload(config, serviceIds);
},
stop: (options) => {
pluginServicesStopRequested = true;
const stopPromise = ownedPluginServices.promise.then(async (handle) => {
await handle?.stop(options);
});
if (!options?.strict) return stopPromise;
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
reject(new AggregateError([/* @__PURE__ */ new Error("Gateway plugin service startup did not settle before replacement")], "Gateway plugin service replacement cleanup failed"));
}, Math.max(0, options.deadlineAtMs - Date.now()));
stopPromise.then(() => {
clearTimeout(timer);
resolve();
}, (error) => {
clearTimeout(timer);
reject(error instanceof Error ? error : new Error(String(error)));
});
});
}
};
resolvePluginServicesOwner = ownedPluginServices.resolve;
params.onPluginServices?.(pluginServicesOwner);
}
const pluginServices = shouldStartPluginServices ? await measureStartup(params.startupTrace, "sidecars.plugin-services", async () => {
try {
const { startPluginServices } = await import("./services-wg5DeUW5.js");
if (pluginServicesStopRequested || params.shouldStartPluginServices?.() === false) {
resolvePluginServicesOwner?.(null);
return null;
}
const startedPluginServices = await startPluginServices({
registry: params.pluginRegistry,
config: params.cfg,
workspaceDir: params.defaultWorkspaceDir,
startupTrace: params.startupTrace,
broadcastPluginEvent: params.broadcastPluginEvent,
getCronService: params.getCronService,
onHandle: resolvePluginServicesOwner
});
resolvePluginServicesOwner?.(startedPluginServices);
return startedPluginServices;
} catch (err) {
resolvePluginServicesOwner?.(null);
params.log.warn(`plugin services failed to start: ${String(err)}`);
return null;
}
}) : null;
if (!shouldStartPluginServices) params.onPluginServices?.(null);
const shouldDispatchGatewayStartupInternalHook = internalHooksConfigured || await hasGatewayStartupInternalHookListeners();
if (params.shouldCreatePostReadySidecars?.() === false) return {
pluginServices,
postReadySidecars
};
if (shouldDispatchGatewayStartupInternalHook) {
params.startupOutcomes?.record({
subsystem: "internal-startup-hook",
status: "scheduled"
});
postReadySidecars.push(scheduleGatewayGenerationTimer({
delayMs: 250,
origin: "hooks:gateway-startup",
run: async (isStopped) => {
const { createInternalHookEvent, triggerInternalHook } = await loadInternalHooksModule();
if (isStopped()) return;
await triggerInternalHook(createInternalHookEvent("gateway", "startup", "gateway:startup", {
cfg: params.cfg,
deps: params.deps,
workspaceDir: params.defaultWorkspaceDir
}));
},
onError: (err) => params.logHooks.warn(`gateway startup hook failed: ${String(err)}`)
}));
}
if (params.cfg.acp?.enabled) runWithGatewayIndependentRootWorkAdmission(async () => {
const ready = await measureStartup(params.startupTrace, "sidecars.acp.runtime-ready", () => waitForAcpRuntimeBackendReady({ backendId: params.cfg.acp?.backend }));
params.startupTrace?.detail("sidecars.acp.runtime-ready", [["readyCount", ready ? 1 : 0], ["backend", params.cfg.acp?.backend ?? "default"]]);
await measureStartup(params.startupTrace, "sidecars.acp.identity-reconcile", async () => {
const [{ getAcpSessionManager }, { ACP_SESSION_IDENTITY_RENDERER_VERSION }] = await Promise.all([import("./acp/control-plane/manager.js"), import("./acp-core/runtime/session-identifiers.js")]);
const result = await getAcpSessionManager().reconcilePendingSessionIdentities({ cfg: params.cfg });
if (result.checked === 0) return;
params.log.warn(`acp startup identity reconcile (renderer=${ACP_SESSION_IDENTITY_RENDERER_VERSION}): checked=${result.checked} resolved=${result.resolved} failed=${result.failed}`);
});
}, "startup:acp-identity-reconcile").catch((err) => {
params.log.warn(`acp startup identity reconcile failed: ${String(err)}`);
});
let restartSentinelWake;
postReadySidecars.push(schedulePostReadySidecarTask({
startupTrace: params.startupTrace,
name: "sidecars.restart-sentinel",
log: params.log,
waitForPostReadyWork: params.waitForPostReadyWork,
run: async (isStopped) => {
if (!shouldCheckRestartSentinel() || isStopped()) return;
if (!await hasRestartSentinelFast() || isStopped()) return;
restartSentinelWake = scheduleRestartSentinelWakeAfterReady({
deps: params.deps,
log: params.log
});
},
stop: async () => {
await restartSentinelWake?.stop();
}
}));
if (params.cfg.hooks?.enabled && params.cfg.hooks.gmail?.account) postReadySidecars.push(schedulePostReadySidecarTask({
startupTrace: params.startupTrace,
name: "sidecars.gmail-watch",
log: params.log,
waitForPostReadyWork: params.waitForPostReadyWork,
run: async (isStopped, signal) => {
const { startGmailWatcherWithLogs } = await import("./gmail-watcher-lifecycle-ClRSdxyo.js");
if (isStopped()) return;
await startGmailWatcherWithLogs({
cfg: params.cfg,
log: params.logHooks,
signal
});
}
}));
if (params.cfg.hooks?.gmail?.model) postReadySidecars.push(schedulePostReadySidecarTask({
startupTrace: params.startupTrace,
name: "sidecars.gmail-model",
log: params.log,
waitForPostReadyWork: params.waitForPostReadyWork,
run: async (isStopped) => {
const [{ DEFAULT_MODEL, DEFAULT_PROVIDER }, { loadPreparedModelCatalog }, { getModelRefStatus, resolveConfiguredModelRef, resolveHooksGmailModel }] = await Promise.all([
loadAgentDefaultsModule(),
import("./prepared-model-catalog-BH7NC_p8.js"),
loadAgentModelSelectionModule()
]);
if (isStopped()) return;
const hooksModelRef = resolveHooksGmailModel({
cfg: params.cfg,
defaultProvider: DEFAULT_PROVIDER
});
if (hooksModelRef) {
const { provider: resolvedDefaultProvider, model: defaultModel } = resolveConfiguredModelRef({
cfg: params.cfg,
defaultProvider: DEFAULT_PROVIDER,
defaultModel: DEFAULT_MODEL
});
const catalog = await loadPreparedModelCatalog({
config: params.cfg,
readOnly: true,
providerDiscoveryProviderIds: [hooksModelRef.provider],
scopedLiveProviderDiscovery: true
});
const status = getModelRefStatus({
cfg: params.cfg,
catalog,
ref: hooksModelRef,
defaultProvider: resolvedDefaultProvider,
defaultModel
});
if (!status.allowed) params.logHooks.warn(`hooks.gmail.model "${status.key}" not allowed by agents.defaults.modelPolicy.allow (will use primary instead)`);
if (!status.inCatalog) params.logHooks.warn(`hooks.gmail.model "${status.key}" not in the model catalog (may fail at runtime)`);
}
}
}));
params.onPostReadySidecars?.(postReadySidecars);
return {
pluginServices,
postReadySidecars
};
}
const defaultGatewayPostAttachRuntimeDeps = {
getGlobalHookRunner: async () => (await import("./plugins/hook-runner-global.js")).getGlobalHookRunner(),
logGatewayStartup: async (params) => (await import("./server-startup-log-CYWzzhQM.js")).logGatewayStartup(params),
refreshLatestUpdateRestartSentinel: refreshLatestUpdateRestartSentinelIfPresent,
createGatewayUpdateCheck: async (...args) => (await import("./update-startup-C1M30Dpv.js")).createGatewayUpdateCheck(...args),
startGatewaySidecars,
warmSystemCa: beginMacOSSystemCaWarmupOnce,
loadSubagentRegistryActivation: async () => (await import("./subagent-registry-Dns8wAWm.js")).activateSubagentRegistry
};
function createDeferredGatewayUpdateCheck(params) {
let stopped = false;
let runWatcher;
let owner;
let ownerReady;
let initialization;
let stopPromise;
let latestUpdateAvailable = null;
let latestSchedule;
const broadcastUpdateAvailable = (payload) => {
if (stopped) return;
const detailedConnIds = params.getClientConnIds((client) => canReadDetailedUpdateMetadata(client.connect.role ?? "operator", client.connect.scopes ?? []));
const legacyConnIds = new Set(params.getClientConnIds());
for (const connId of detailedConnIds) legacyConnIds.delete(connId);
params.broadcastToConnIds(GATEWAY_EVENT_UPDATE_AVAILABLE, payload, detailedConnIds, { dropIfSlow: true });
params.broadcastToConnIds(GATEWAY_EVENT_UPDATE_AVAILABLE, { updateAvailable: projectUpdateAvailable(payload.updateAvailable, false) ?? null }, legacyConnIds, { dropIfSlow: true });
};
const stop = () => {
stopped = true;
runWatcher?.stop();
return stopPromise ??= (async () => {
const cleanup = owner?.stop();
await ownerReady;
await cleanup;
await initialization;
})();
};
const start = () => {
if (ownerReady || stopped) return;
runWatcher = startUpdateRunWatcher({
broadcast: (event, payload) => params.broadcastToConnIds(event, payload, params.getClientConnIds()),
log: params.log
});
ownerReady = (async () => {
try {
owner = await params.runtimeDeps.createGatewayUpdateCheck({
getConfig: params.getConfig,
onUpdateRunCreated: wakeUpdateRunWatcher,
log: params.log,
isNixMode: params.isNixMode,
...params.activeWorkInspectors ? { activeWorkInspectors: params.activeWorkInspectors } : {},
onUpdateAvailableChange: (updateAvailable) => {
latestUpdateAvailable = updateAvailable;
const payload = {
updateAvailable,
...latestSchedule ? { schedule: latestSchedule } : {}
};
broadcastUpdateAvailable(payload);
},
onUpdateScheduleChange: (schedule) => {
latestSchedule = schedule;
broadcastUpdateAvailable({
updateAvailable: latestUpdateAvailable,
schedule
});
}
});
} catch (err) {
if (!stopped) params.log.warn(`gateway update check failed to initialize: ${String(err)}`);
return;
}
if (stopped) {
await owner.stop();
return;
}
const updateCheck = owner;
initialization = (async () => updateCheck.initialize())().catch((err) => {
if (!stopped) params.log.warn(`gateway update status failed to initialize: ${String(err)}`);
});
})();
ownerReady.catch(() => {});
(async () => {
await params.waitForPostReadyWork?.();
await new Promise((resolve) => {
setImmediate(resolve);
});
await ownerReady;
if (!stopped) await runWithGatewayIndependentRootWorkAdmission(async () => await measureStartup(params.startupTrace, "post-attach.update-check", () => owner?.start()), "startup:update-check");
})().catch((err) => {
if (!stopped) params.log.warn(`gateway update check readiness wait failed: ${String(err)}`);
});
};
return {
start,
stop
};
}
/** Start work that depends on the HTTP server being attached and visible. */
async function startGatewayPostAttachRuntime(params, runtimeDeps = defaultGatewayPostAttachRuntimeDeps) {
const controlUiRootLifecycle = params.controlUiRootLifecycle;
const mainSessionRecoveryStartupCheckedStorePaths = /* @__PURE__ */ new Set();
const controlUiAssetsSidecar = !params.minimalTestGateway && controlUiRootLifecycle ? schedulePostReadySidecarTask({
name: "sidecars.control-ui-assets",
startupTrace: params.startupTrace,
log: params.log,
run: controlUiRootLifecycle.start,
stop: controlUiRootLifecycle.stop
}) : void 0;
if (controlUiAssetsSidecar) params.onGatewayLifetimeSidecars?.([controlUiAssetsSidecar]);
if (!params.minimalTestGateway) await measureStartup(params.startupTrace, "post-attach.system-ca", () => runtimeDeps.warmSystemCa({ log: params.log }));
let pluginRegistry = params.pluginRegistry;
let startupPluginsLoaded = false;
let startupPluginsLoadPromise = null;
const loadStartupPluginsIfNeeded = async () => {
if (params.minimalTestGateway || !params.loadStartupPlugins) return {
pluginRegistry,
gatewayMethods: []
};
if (startupPluginsLoaded) return {
pluginRegistry,
gatewayMethods: []
};
startupPluginsLoadPromise ??= (async () => {
params.onStartupPluginsLoading?.();
const loaded = await measureStartup(params.startupTrace, "plugins.runtime-post-bind", () => params.loadStartupPlugins());
await params.pluginRuntimeClaim?.waitForUnblocked();
if (params.isClosing?.() || params.pluginRuntimeClaim?.isCurrent() === false) {
loaded.retireGatewayRuntimeBindings?.();
pluginRegistry = params.getCurrentPluginRegistry?.() ?? pluginRegistry;
startupPluginsLoaded = true;
return {
pluginRegistry,
gatewayMethods: []
};
}
pluginRegistry = loaded.pluginRegistry;
startupPluginsLoaded = true;
params.startupTrace?.detail("plugins.runtime-post-bind", [["loadedPluginCount", pluginRegistry.plugins.filter((plugin) => plugin.status === "loaded").length], ["gatewayMethodCount", loaded.gatewayMethods.length]]);
await params.onStartupPluginsLoaded?.(loaded);
return loaded;
})();
return await startupPluginsLoadPromise;
};
let startupLogPromise;
const startupLogSettled = createDeferredCore();
startupLogSettled.promise.catch(() => {});
let startupLogOwnerAssigned = false;
const assignStartupLogOwner = (owner) => {
if (params.sidecarStartup !== "defer" || startupLogOwnerAssigned) return;
startupLogOwnerAssigned = true;
owner.then(startupLogSettled.resolve, startupLogSettled.reject);
};
const startStartupLog = () => {
if (startupLogPromise) return startupLogPromise;
startupLogPromise = params.trackStartupWork(() => measureStartup(params.startupTrace, "post-attach.log", () => runtimeDeps.logGatewayStartup({
cfg: params.cfgAtStart,
activationSourceConfig: params.activationSourceConfig,
env: process.env,
manifestRecords: params.pluginManifestRecords,
...params.ambientEnvTriggers ? { ambientEnvTriggers: params.ambientEnvTriggers } : {},
bindHost: params.bindHost,
bindHosts: params.bindHosts,
port: params.port,
tlsEnabled: params.tlsEnabled,
loadedPluginIds: pluginRegistry.plugins.filter((plugin) => plugin.status === "loaded").map((plugin) => plugin.id),
log: params.log,
isNixMode: params.isNixMode,
startupStartedAt: params.startupStartedAt
})));
startupLogPromise.catch(() => {});
assignStartupLogOwner(startupLogPromise);
return startupLogPromise;
};
const skipStartupLog = () => assignStartupLogOwner(Promise.resolve());
const updateCheck = params.minimalTestGateway ? {
start: () => {},
stop: async () => {}
} : createDeferredGatewayUpdateCheck({
startupTrace: params.startupTrace,
runtimeDeps,
getConfig: params.getConfig,
log: params.log,
isNixMode: params.isNixMode,
broadcastToConnIds: params.broadcastToConnIds,
getClientConnIds: params.getClientConnIds,
waitForPostReadyWork: params.waitForPostReadyWork,
activeWorkInspectors: params.activeWorkInspectors
});
if (!params.minimalTestGateway) params.onGatewayLifetimeSidecars?.([updateCheck]);
let pluginServicesReported = false;
let reportedPluginServices = null;
const reportPluginServices = (pluginServices) => {
if (params.pluginRuntimeClaim?.isCurrent() === false) return;
pluginServicesReported = true;
reportedPluginServices = pluginServices;
params.onPluginServices?.(pluginServices);
};
const waitForSidecarStartTurn = () => new Promise((resolve) => {
if (params.sidecarStartup === "defer") {
setTimeout(resolve, DEFERRED_SIDECAR_START_DELAY_MS).unref?.();
return;
}
setImmediate(resolve);
});
const emptySidecarResult = () => ({
pluginServices: null,
pluginRegistry,
postReadySidecars: [],
gatewayLifetimeSidecars: []
});
const startSidecars = () => params.minimalTestGateway ? startStartupLog().then(() => ({
pluginServices: null,
pluginRegistry,
postReadySidecars: [],
gatewayLifetimeSidecars: []
})) : waitForSidecarStartTurn().then(async () => {
if (params.isClosing?.()) {
skipStartupLog();
return emptySidecarResult();
}
await loadStartupPluginsIfNeeded();
if (params.isClosing?.()) {
skipStartupLog();
return emptySidecarResult();
}
const startupLog = startStartupLog();
const startupOutcomes = createGatewayStartupOutcomeRecorder({
cfg: params.gatewayPluginConfigAtStart,
gatewayStartHooks: hasGatewayStartHooks(pluginRegistry)
});
const workerEnvironmentSidecar = params.isClosing?.() ? null : await params.startWorkerEnvironmentRuntime?.() ?? null;
if (params.isClosing?.()) return emptySidecarResult();
params.log.info("starting channels and sidecars...");
const loaderStatsBefore = getPluginModuleLoaderStats();
const result = await (async () => {
try {
const startupRuntimeCurrent = params.pluginRuntimeClaim?.isCurrent() !== false;
const pluginMetadataSnapshot = startupRuntimeCurrent ? params.pluginMetadataSnapshot : params.getCurrentPluginMetadataSnapshot?.();
return await measureStartup(params.startupTrace, "sidecars.total", () => runtimeDeps.startGatewaySidecars({
cfg: startupRuntimeCurrent ? params.gatewayPluginConfigAtStart : params.getConfig(),
getModelRuntimeConfig: params.getConfig,
...pluginMetadataSnapshot ? { pluginMetadataSnapshot } : {},
pluginRegistry,
defaultWorkspaceDir: params.defaultWorkspaceDir,
deps: params.deps,
getCronService: params.getCronService,
startChannels: params.startChannels,
shouldStartChannels: () => params.isClosing?.() !== true,
refreshChatMetadata: params.refreshChatMetadata,
log: params.log,
logHooks: params.logHooks,
logChannels: params.logChannels,
startupTrace: params.startupTrace,
onChannelsStarted: params.onChannelsStarted,
onPluginServices: reportPluginServices,
onPostReadySidecars: (sidecars) => {
params.onPostReadySidecars?.(sidecars);
},
shouldCreatePostReadySidecars: () => params.isClosing?.() !== true,
shouldStartPluginServices: () => params.isClosing?.() !== true && params.pluginRuntimeClaim?.isCurrent() !== false,
...params.pluginRuntimeClaim ? { pluginRuntimeClaim: params.pluginRuntimeClaim } : {},
broadcastPluginEvent: params.broadcastPluginEvent,
startupOutcomes,
mainSessionRecoveryStartupCheckedStorePaths,
waitForPostReadyWork: params.waitForPostReadyWork
}));
} catch (error) {
try {
await workerEnvironmentSidecar?.stop();
if (workerEnvironmentSidecar) params.unregisterConnectionDependentSidecar(workerEnvironmentSidecar);
} catch (cleanupError) {
params.log.warn(`worker environment cleanup after sidecar startup failure failed: ${String(cleanupError)}`);
}
throw error;
}
})();
const retainStartupSidecars = (mainSessionRecoverySidecar) => {
if (mainSessionRecoverySidecar) params.onGatewayLifetimeSidecars?.([mainSessionRecoverySidecar]);
return emptySidecarResult();
};
if (params.isClosing?.()) return retainStartupSidecars();
const loaderStatsAfter = getPluginModuleLoaderStats();
params.startupTrace?.detail("sidecars.plugin-loader", [
["callsCount", loaderStatsAfter.calls - loaderStatsBefore.calls],
["nativeHitsCount", loaderStatsAfter.nativeHits - loaderStatsBefore.nativeHits],
["nativeMissesCount", loaderStatsAfter.nativeMisses - loaderStatsBefore.nativeMisses],
["sourceTransformForcedCount", loaderStatsAfter.sourceTransformForced - loaderStatsBefore.sourceTransformForced],
["sourceTransformFallbacksCount", loaderStatsAfter.sourceTransformFallbacks - loaderStatsBefore.sourceTransformFallbacks]
]);
let mainSessionRecoverySidecar;
await startupLog;
if (params.isClosing?.()) return retainStartupSidecars(mainSessionRecoverySidecar);
try {
const { scheduleRestartAbortedMainSessionRecovery } = await loadMainSessionRestartRecoveryModule();
if (params.isClosing?.() !== true) mainSessionRecoverySidecar = scheduleRestartAbortedMainSessionRecovery({
delayMs: 0,
getConfig: params.getConfig,
shouldContinue: () => params.isClosing?.() !== true,
startupCheckedStorePaths: mainSessionRecoveryStartupCheckedStorePaths,
waitForStart: params.waitForPostReadyWork,
gatewayRuntime: params.recoveryRuntime
});
} catch (err) {
params.log.warn(`main-session restart recovery failed to schedule: ${String(err)}`);
}
if (params.isClosing?.()) return retainStartupSidecars(mainSessionRecoverySidecar);
params.unlockStartupMethods();
if (!pluginServicesReported) reportPluginServices(result.pluginServices);
const postReadySidecars = [...result.postReadySidecars];
const newGatewayLifetimeSidecars = [
scheduleContextCachePrewarm(params),
scheduleGatewayHandlerPrewarm(params),
...mainSessionRecoverySidecar ? [mainSessionRecoverySidecar] : []
];
if (params.providerAuthPrewarm && params.providerAuthPrewarm.enabled !== false) newGatewayLifetimeSidecars.push(scheduleProviderAuthStatePrewarm({
getConfig: params.providerAuthPrewarm.getConfig ?? (() => params.cfgAtStart),
log: params.log,
delayMs: params.providerAuthPrewarm.delayMs,
startupWarmEnabled: params.providerAuthPrewarm.enabled === true
}));
if (params.gatewayPluginConfigAtStart.transcripts?.autoStart?.length) newGatewayLifetimeSidecars.push(scheduleTranscriptsAutoStartSidecar({
cfg: params.gatewayPluginConfigAtStart,
startupTrace: params.startupTrace,
log: params.log,
waitForPostReadyWork: params.waitForPostReadyWork
}));
params.onGatewayLifetimeSidecars?.(newGatewayLifetimeSidecars);
const gatewayLifetimeSidecars = [...controlUiAssetsSidecar ? [controlUiAssetsSidecar] : [], ...newGatewayLifetimeSidecars];
params.log.info(formatGatewayStartupOutcomes(startupOutcomes.snapshot()));
params.onSidecarsReady?.();
try {
const activateSubagentRegistry = await runtimeDeps.loadSubagentRegistryActivation();
if (params.isClosing?.() !== true) activateSubagentRegistry(params.resolveGatewayContext);
} catch (err) {
params.log.warn(`subagent restart recovery failed to activate: ${String(err)}`);
}
if (params.isClosing?.()) return retainStartupSidecars(mainSessionRecoverySidecar);
params.startupTrace?.detail("sidecars.ready", [["loadedPluginCount", pluginRegistry.plugins.filter((plugin) => plugin.status === "loaded").length], ["postReadySidecarCount", postReadySidecars.length + gatewayLifetimeSidecars.length]]);
params.startupTrace?.mark("sidecars.ready");
params.log.info("gateway ready");
return {
...result,
postReadySidecars,
gatewayLifetimeSidecars,
pluginRegistry
};
});
const sidecarsPromise = params.trackStartupWork(startSidecars);
params.trackStartupWork(async (signal) => {
const sidecarsResult = await sidecarsPromise;
if (params.minimalTestGateway) return;
await params.waitForPostReadyWork?.();
if (params.isClosing?.()) return;
await new Promise((resolve) => {
setImmediate(resolve);
});
if (params.isClosing?.()) return;
const sentinelRefresh = runWithGatewayIndependentRootWorkAdmission(async () => {
await measureStartup(params.startupTrace, "post-attach.update-sentinel", async () => {
if (!params.isClosing?.()) await runtimeDeps.refreshLatestUpdateRestartSentinel();
});
}, "startup:update-sentinel", signal).catch((err) => {
params.log.warn(`restart sentinel refresh failed: ${String(err)}`);
});
try {
sweepSessionStateWatchNotices();
if (!hasGatewayStartHooks(sidecarsResult.pluginRegistry)) return;
const hookRunner = await runtimeDeps.getGlobalHookRunner();
if (params.isClosing?.() || !hookRunner?.hasHooks("gateway_start")) return;
const { withPluginHttpRouteRegistry } = await import("./http-registry-BCkd9WEp.js");
if (params.isClosing?.()) return;
await runWithGatewayIndependentRootWorkAdmission(async () => {
if (params.isClosing?.()) return;
await withPluginHttpRouteRegistry(sidecarsResult.pluginRegistry, () => hookRunner.runGatewayStart({ port: params.port }, {
port: params.port,
config: params.gatewayPluginConfigAtStart,
workspaceDir: params.defaultWorkspaceDir,
getCron: () => params.getCronService?.() ?? params.deps.cron
}));
}, "hooks:gateway-start", signal).catch((err) => {
params.log.warn(`gateway_start hook failed: ${String(err)}`);
});
} finally {
await sentinelRefresh;
}
}).catch((err) => {
params.log.warn(`gateway sidecars failed to start: ${String(err)}`);
});
if (params.sidecarStartup !== "defer") {
const sidecarsResult = await sidecarsPromise;
updateCheck.start();
return {
stopGatewayUpdateCheck: updateCheck.stop,
pluginServices: sidecarsResult.pluginServices,
startupSettled: Promise.resolve()
};
}
updateCheck.start();
const startupSettled = Promise.all([sidecarsPromise, startupLogSettled.promise]).then(() => void 0);
startupSettled.catch(() => {});
return {
stopGatewayUpdateCheck: updateCheck.stop,
pluginServices: reportedPluginServices,
startupSettled
};
}
const testing = {
providerAuthPrewarmStartDelayMs: PROVIDER_AUTH_PREWARM_START_DELAY_MS,
hasRestartSentinelFast,
prewarmConfiguredPrimaryModel,
hydrateConfiguredExternalCliAuth,
publishConfiguredModelRuntimeSnapshots,
publishStartupModelRuntime,
refreshLatestUpdateRestartSentinelIfPresent,
scheduleProviderAuthStatePrewarm,
scheduleRestartSentinelWakeAfterReady,
shouldSkipStartupModelPrewarm
};
//#endregion
export { startGatewayPostAttachRuntime, startGatewaySidecars, testing };