openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
1,687 lines • 79.6 kB
JavaScript
import { _ as resolvePrimaryStringValue, l as normalizeOptionalString } from "./string-coerce-1VuiLaaR.mjs";
import { l as asNonNegativeFiniteNumber, n as MAX_TIMER_TIMEOUT_MS, u as asPositiveFiniteNumber } from "./number-coercion-CuVhdCBs.mjs";
import { o as resolveUserPath } from "./home-dir-Dc6nN6HB.mjs";
import { t as createLazyImportLoader } from "./lazy-promise-DGqyc4Y4.mjs";
import { t as isFastTestRuntimeEnv } from "./test-runtime-env-mVzBKwSd.mjs";
import "./env-BBJTTIuE.mjs";
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.mjs";
import "./utils-DgZn2dLu.mjs";
import { s as raceWithTimeout } from "./src-D1BHGNTj.mjs";
import { E as createDiagnosticTraceContextFromActiveScope, O as freezeDiagnosticTraceContext, P as runWithDiagnosticTraceContext, p as isDiagnosticsEnabled, s as emitTrustedDiagnosticEvent, w as createChildDiagnosticTraceContext } from "./diagnostic-events-BADdmgZf.mjs";
import { t as _usingCtx } from "./usingCtx-DwOBYOJf.mjs";
import { n as normalizeAgentId } from "./agent-id-D1-Q6vR4.mjs";
import { C as tryResolveAmbientOwnerAgentId, a as resolveAgentDir, d as resolveAgentWorkspaceDir, r as resolveAgentConfig } from "./agent-scope-config-CDYpzA0r.mjs";
import { M as parseAgentSessionKey, S as isCronSessionKey } from "./session-key-C09ow4h0.mjs";
import { s as resolveAgentModelPrimaryValue } from "./model-input-C9K4a_6k.mjs";
import { n as DEFAULT_MODEL, r as DEFAULT_PROVIDER } from "./defaults-Jag53zwP.mjs";
import { t as splitTrailingAuthProfile } from "./model-ref-profile-BNjB2hED.mjs";
import { b as resolveSubagentModelConfigSelectionResult } from "./agent-scope-CG17uXFn.mjs";
import { S as releaseAgentRunContext, o as consumeCronNextCheckProposal, r as claimAgentRunContext } from "./agent-run-registry-BaxMDLZF.mjs";
import { l as getAgentEventLifecycleGeneration, t as assertAgentRunLifecycleGenerationCurrent, y as withAgentRunLifecycleGeneration } from "./agent-events-GvNTkbe3.mjs";
import { n as withPluginRuntimeGenerationScope } from "./generation-scope-tH9UXyLm.mjs";
import { _ as resolveHooksGmailModel, h as resolveConfiguredModelRef, m as resolveConfiguredModelPolicyAllow, s as getModelRefStatus } from "./model-selection-shared-DcM6hdAC.mjs";
import { n as findModelInCatalog } from "./model-catalog-lookup-4OgPvT6V.mjs";
import { c as normalizeThinkLevel } from "./thinking.shared-Jy32Vp-O.mjs";
import { i as logWarn } from "./logger-DskmrSVa.mjs";
import { l as retireSessionMcpRuntime } from "./agent-bundle-mcp-manager-api-IlYicdWC.mjs";
import { t as hasAnyAuthProfileStoreSource } from "./source-check-BwAkkwWI.mjs";
import { t as resolveAllowedModelRefCore } from "./model-selection-resolve-PdD2eskm.mjs";
import { a as isSilentReplyPayloadText, r as SILENT_REPLY_TOKEN, t as AUTOMATION_FAILED_TOKEN } from "./tokens-D4ghQG_g.mjs";
import { l as resolveCreatorSandbox } from "./operator-role-policy-Bsa4SHqC.mjs";
import { n as publishedModelCatalogOwnerMatchesAgent } from "./prepared-model-catalog-owner-lSB2_Cf6.mjs";
import { a as withPreparedModelRuntimePluginGenerationScope, i as scopePreparedModelRuntimeLease } from "./prepared-model-runtime-generation-scope-B18pp3-E.mjs";
import "./thinking-DacLfQjp.mjs";
import { i as resolveThinkingSelectionCore, n as resolveConfiguredThinkingDefaultCore } from "./model-thinking-default-twlQ6xyp.mjs";
import { a as isAgentHarnessSessionKey, n as AGENT_HARNESS_SESSION_ID_LOCKED_MESSAGE, r as AGENT_HARNESS_SESSION_KEY_RESERVED_MESSAGE } from "./agent-harness-session-key-D8EQIzja.mjs";
import { i as resolveSessionRuntimeOverrideForProvider } from "./session-runtime-compat-zlHVO23h.mjs";
import { i as normalizeThinkingCatalogProviders, r as needsThinkHydration, s as resolveEffectiveAgentRuntime } from "./thinking-runtime--sshEpUp.mjs";
import { t as ensureAgentWorkspace } from "./workspace-BHjQqjjT.mjs";
import { m as loadPublishedGatewayReplyDispatchRuntime, t as acquireAgentRunPreparedModelRuntime } from "./prepared-model-runtime-BbAS5OtQ.mjs";
import { l as loadResolvedPublishedModelCatalogOwner, o as loadProviderScopedThinkingCatalog } from "./prepared-model-catalog-CntkE0sm.mjs";
import { n as mapHookExternalContentSource, r as resolveHookExternalContentSource, t as isExternalHookSession } from "./external-content-source-BKc9v74i.mjs";
import { s as resolveCronScheduledToolPolicy } from "./scheduled-tool-policy-CYthJ0r4.mjs";
import { n as isDetachedCronSessionTarget, s as resolveCronSessionTargetSessionKey } from "./session-target-DJsUULzX.mjs";
import { r as resolveCronRunToolsAllow } from "./tools-allow-BpmRg2Op.mjs";
import { H as extractTextFromChatContent } from "./user-facing-text-DC1sCo4O.mjs";
import { Q as normalizeAgentRunTerminalReplySnapshot, X as buildAgentRunTerminalReplySnapshot } from "./subagent-registry.store.sqlite-DIYyRjhZ.mjs";
import { a as hasBillableUsage, i as deriveSessionTotalTokens, n as deriveContextPromptTokens, o as hasNonzeroUsage } from "./usage-XmAH1cfG.mjs";
import { n as resolveAgentTimeoutMs } from "./timeout-Q2p-0uCf.mjs";
import { m as resolveAgentRunErrorLifecycleFields, o as createAgentRunRestartAbortError } from "./run-termination-DGv2EKii.mjs";
import { n as logMessageDispatchStarted, t as logMessageDispatchCompleted } from "./diagnostic-B-cJgPjo.mjs";
import "./lanes-D7a4ntDu.mjs";
import { u as isCommandLaneTaskTimeoutError } from "./command-queue-CFE60Avj.mjs";
import { t as resolveSourceDeliveryOutcome } from "./source-delivery-plan-B_VDfdVc.mjs";
import "./sessions-DQtCC-5j.mjs";
import { n as resolveProjectedSessionContextTokens, r as resolveTrustedSessionContextTokens } from "./context-token-provenance-Djv3AdiI.mjs";
import { i as normalizeCronRunErrorText, o as resolveCronAbortReasonText } from "./execution-errors-3rfzndmK.mjs";
import { r as clearBootstrapSnapshotOnSessionRollover } from "./bootstrap-cache-Dnyzgktr.mjs";
import { t as createDiagnosticMessageLifecycle } from "./message-lifecycle-Cpxvqvx0.mjs";
import "./agent-bundle-mcp-tools-CsG5nW12.mjs";
import { s as readSessionMessagesAsync } from "./session-transcript-readers-B2XS9yB_.mjs";
import { n as resolveCronJobEffectiveAgentId } from "./agent-id-uYw1RFJQ.mjs";
import { t as resolveCronAgentSessionKey } from "./session-key-C18RZBZJ.mjs";
import { t as removeCronRunContinuationSessionIfIdle } from "./run-continuation-cleanup-CvKWzHo6.mjs";
import { t as hasAcceptedSessionSpawn } from "./accepted-session-spawn-DpD0J38H.mjs";
import { r as resolveIngressWorkspaceOverrideForSessionRun } from "./spawned-context-BjFFoRwI.mjs";
import { i as toNormalizedUsage, r as mergeUsageIntoAccumulator, t as createUsageAccumulator } from "./usage-accumulator-CzWfyqDH.mjs";
import { n as resolveCronStyleNow } from "./current-time-CZi1S4GF.mjs";
import { t as createAgentLifecycleTerminalBackstop } from "./agent-lifecycle-terminal-COwG2fZv.mjs";
import { o as projectChatDisplayMessages } from "./chat-display-projection.core-BRO3Pr6M.mjs";
import "./chat-display-projection-CkWUSNcY.mjs";
import { n as isEmbeddedRunTerminalToolFailure, t as CODE_MODE_MCP_CATALOG_MISS_MESSAGE } from "./terminal-tool-failure-C7cE3h6q.mjs";
import { t as cleanupBrowserSessionsForLifecycleEnd } from "./browser-lifecycle-cleanup-BvQA8BG4.mjs";
import { i as mergeCronRunDiagnostics, n as createCronRunDiagnosticsFromError, t as createCronRunDiagnosticsFromAgentResult } from "./run-diagnostics-D1nl-AS_.mjs";
import { t as resolveCronRunErrorReason } from "./run-error-reason-CFlWjwpd.mjs";
import { t as cleanupCronRunSessionAfterRun } from "./session-cleanup-ByFhAVX8.mjs";
import { t as classifyCronAgentTurnShellPrompt } from "./agent-turn-command-prompt-DqrePHZg.mjs";
import { t as CronExecutionRootRuntimeError } from "./execution-root-runtime-BEhlVxn3.mjs";
import { a as buildCronDeliveryTrace, c as loadCronDeliveryRuntime, l as resolveCronDeliveryContext, n as resolveCronPreflight, o as createCronToolsAllowPreflightDiagnostics, u as resolveCronChannelOutputPolicy } from "./run-fallback-policy-DIphkTp9.mjs";
import { a as createPersistCronSessionEntry, c as setCronSessionAgentHarnessId, i as createCronRunContinuationSession, l as setCronSessionRuntimeModel, m as resolveCronPayloadOutcome, n as adoptCronRunSessionMetadata, o as persistCronSkillsSnapshotIfChanged, r as beginCronSessionWorkAdmission, t as CronSessionLifecycleClaimError } from "./run-session-state-BqGurr63.mjs";
import { n as prepareCronSession } from "./session-BPb5keTa.mjs";
import { n as resolveCronAgentConfig, t as resolveCronActiveRuntimeConfig } from "./run-config-B3KSDx-q.mjs";
import path from "node:path";
import fs from "node:fs/promises";
import { randomUUID } from "node:crypto";
import { isPathInside } from "@openclaw/fs-safe/path";
//#region src/cron/isolated-agent/run-usage.ts
const cronContextRuntimeLoader$1 = createLazyImportLoader(() => import("./run-context.runtime.js"));
function resolveCronRunUsage(runs) {
if (runs.length === 1) return runs[0]?.runResult.meta?.agentMeta?.usage;
const accumulated = createUsageAccumulator();
for (const { runResult } of runs) {
const usage = runResult.meta?.agentMeta?.usage;
if (!usage) continue;
const bucketTotal = (usage.input ?? 0) + (usage.output ?? 0) + (usage.cacheRead ?? 0) + (usage.cacheWrite ?? 0);
mergeUsageIntoAccumulator(accumulated, {
...usage,
total: Math.max(bucketTotal, asNonNegativeFiniteNumber(usage.total) ?? 0)
});
}
return toNormalizedUsage(accumulated);
}
function applyCronRunUsage(prepared, runs) {
const usage = resolveCronRunUsage(runs);
if (!hasNonzeroUsage(usage) && !hasNonzeroUsage(runs.at(-1)?.runResult.meta?.agentMeta?.usage)) return;
const input = usage?.input ?? 0;
const output = usage?.output ?? 0;
const cacheRead = usage?.cacheRead ?? 0;
const cacheWrite = usage?.cacheWrite ?? 0;
prepared.cronSession.sessionEntry.inputTokens = input;
prepared.cronSession.sessionEntry.outputTokens = output;
prepared.cronSession.sessionEntry.cacheRead = cacheRead;
prepared.cronSession.sessionEntry.cacheWrite = cacheWrite;
const bucketTotalTokens = input + output + cacheRead + cacheWrite;
const totalTokens = typeof usage?.total === "number" && Number.isFinite(usage.total) ? Math.max(bucketTotalTokens, usage.total) : bucketTotalTokens;
return {
input_tokens: input,
output_tokens: output,
...totalTokens > 0 ? { total_tokens: totalTokens } : {},
...cacheRead > 0 ? { cache_read_tokens: cacheRead } : {},
...cacheWrite > 0 ? { cache_write_tokens: cacheWrite } : {}
};
}
/** Preserve each completed prompt's prices and diagnostics across a continuation. */
async function recordCronRunUsage(params) {
const { prepared, runs } = params;
const billableRuns = runs.filter(({ runResult }) => {
const meta = runResult.meta?.agentMeta;
return hasBillableUsage(meta?.usage) || hasBillableUsage(meta?.diagnosticUsage);
});
if (billableRuns.length === 0) return;
const { estimateAggregateUsageCost, resolveModelCostConfig } = await import("./usage-format-CK--rFqi.mjs");
let estimatedCostUsd = 0;
let hasSessionUsage = false;
for (const run of billableRuns) {
const result = run.runResult;
const meta = result.meta?.agentMeta;
const usage = meta?.usage;
const diagnosticUsage = meta?.diagnosticUsage ?? usage;
const provider = meta?.provider ?? run.fallbackProvider;
const model = meta?.model ?? run.fallbackModel;
const costConfig = resolveModelCostConfig({
provider,
model,
config: prepared.cfgWithAgentDefaults,
agentDir: prepared.agentDir
});
if (hasBillableUsage(usage)) {
hasSessionUsage = true;
const cost = asNonNegativeFiniteNumber(estimateAggregateUsageCost({
usage,
cost: costConfig
}));
estimatedCostUsd = estimatedCostUsd !== void 0 && cost !== void 0 ? estimatedCostUsd + cost : void 0;
}
if (!isDiagnosticsEnabled(prepared.cfgWithAgentDefaults) || !hasBillableUsage(diagnosticUsage)) continue;
const input = diagnosticUsage.input ?? 0;
const output = diagnosticUsage.output ?? 0;
const cacheRead = diagnosticUsage.cacheRead ?? 0;
const cacheWrite = diagnosticUsage.cacheWrite ?? 0;
const promptTokens = input + cacheRead + cacheWrite;
const bucketTotalTokens = promptTokens + output;
const total = typeof diagnosticUsage.total === "number" && Number.isFinite(diagnosticUsage.total) ? Math.max(bucketTotalTokens, diagnosticUsage.total) : bucketTotalTokens;
const costUsd = asNonNegativeFiniteNumber(estimateAggregateUsageCost({
usage: diagnosticUsage,
cost: costConfig
}));
const contextUsedTokens = deriveContextPromptTokens({
lastCallUsage: meta?.lastCallUsage,
promptTokens: meta?.promptTokens,
usage
});
const contextTokens = (result === runs.at(-1)?.runResult ? params.contextTokens : void 0) ?? asPositiveFiniteNumber(meta?.contextTokens) ?? (await cronContextRuntimeLoader$1.load()).resolveModelContextTokenProjection({
cfg: prepared.cfgWithAgentDefaults,
provider,
model,
allowAsyncLoad: false
}).contextTokens ?? 2e5;
emitTrustedDiagnosticEvent({
type: "model.usage",
...result.diagnosticTrace ? { trace: freezeDiagnosticTraceContext(createChildDiagnosticTraceContext(result.diagnosticTrace)) } : {},
sessionKey: prepared.runSessionKey,
sessionId: prepared.currentRunSessionId(),
channel: "cron",
agentId: prepared.agentId,
provider,
model,
usage: {
input,
output,
cacheRead,
cacheWrite,
promptTokens,
total
},
lastCallUsage: meta?.lastCallUsage,
context: {
limit: contextTokens,
...contextUsedTokens !== void 0 ? { used: contextUsedTokens } : {}
},
...costUsd !== void 0 ? { costUsd } : {},
durationMs: run.runEndedAt - run.runStartedAt
});
}
if (hasSessionUsage) prepared.cronSession.sessionEntry.estimatedCostUsd = estimatedCostUsd;
}
//#endregion
//#region src/cron/isolated-agent/run-finalize.ts
/** Final persistence, telemetry, and delivery for an isolated cron run. */
const cronContextRuntimeLoader = createLazyImportLoader(() => import("./run-context.runtime.js"));
async function finalizeCronRun(params) {
const { prepared, execution } = params;
const finalRunResult = execution.runResult;
const replyDisposition = (normalizeAgentRunTerminalReplySnapshot(finalRunResult.meta?.terminalReply) ?? buildAgentRunTerminalReplySnapshot({
visibleText: finalRunResult.meta?.finalAssistantVisibleText,
rawText: finalRunResult.meta?.finalAssistantRawText,
terminalReplyKind: finalRunResult.meta?.terminalReplyKind
})).disposition;
const payloads = finalRunResult.payloads ?? [];
const cleanupRunSession = async (reason) => {
await cleanupCronRunSessionAfterRun({
job: prepared.input.job,
agentSessionKey: prepared.agentSessionKey,
sessionId: prepared.currentRunSessionId(),
lifecycleRevision: prepared.cronSession.lifecycleRevision,
sessionUpdatedAt: prepared.cronSession.sessionEntry.updatedAt,
beforeDelete: params.beforeSessionDelete,
reason
});
params.markCronRunSessionCleanupHandled();
};
if (!params.isAborted()) {
if (finalRunResult.meta?.systemPromptReport) prepared.cronSession.sessionEntry.systemPromptReport = finalRunResult.meta.systemPromptReport;
if (finalRunResult.meta?.executionTrace?.runner !== "cli") adoptCronRunSessionMetadata({
entry: prepared.cronSession.sessionEntry,
sessionKey: prepared.agentSessionKey,
runMeta: finalRunResult.meta?.agentMeta
});
}
const usage = resolveCronRunUsage(execution.completedPromptRuns);
const lastCallUsage = finalRunResult.meta?.agentMeta?.lastCallUsage;
const promptTokens = finalRunResult.meta?.agentMeta?.promptTokens;
const modelUsed = finalRunResult.meta?.agentMeta?.model ?? execution.fallbackModel;
const providerUsed = finalRunResult.meta?.agentMeta?.provider ?? execution.fallbackProvider;
const runtimeContextTokens = asPositiveFiniteNumber(finalRunResult.meta?.agentMeta?.contextTokens);
const { contextTokens: modelContextTokens, authoredContextTokens } = (await cronContextRuntimeLoader.load()).resolveModelContextTokenProjection({
cfg: prepared.cfgWithAgentDefaults,
provider: providerUsed,
model: modelUsed,
allowAsyncLoad: false
});
const agentHarnessId = normalizeOptionalString(finalRunResult.meta?.agentMeta?.agentHarnessId);
const retainedRuntimeContextTokens = resolveTrustedSessionContextTokens({
entry: prepared.cronSession.sessionEntry,
provider: providerUsed,
model: modelUsed,
agentHarnessId
});
const projectedContextTokens = resolveProjectedSessionContextTokens({
entry: prepared.cronSession.sessionEntry,
provider: providerUsed,
model: modelUsed,
agentHarnessId,
resolvedContextTokens: modelContextTokens,
authoredContextTokens
});
const contextTokens = runtimeContextTokens ?? projectedContextTokens ?? 2e5;
const projectedUsesPersistedContext = retainedRuntimeContextTokens !== void 0 && (prepared.cronSession.sessionEntry.modelSelectionLocked === true || authoredContextTokens === void 0 && projectedContextTokens === retainedRuntimeContextTokens);
const contextTokensSource = runtimeContextTokens !== void 0 ? finalRunResult.meta?.agentMeta?.contextTokensSource ?? "resolved" : projectedUsesPersistedContext ? prepared.cronSession.sessionEntry.contextTokensSource : "resolved";
if (!params.isAborted()) {
setCronSessionRuntimeModel({
entry: prepared.cronSession.sessionEntry,
provider: providerUsed,
model: modelUsed
});
setCronSessionAgentHarnessId({
entry: prepared.cronSession.sessionEntry,
agentHarnessId
});
prepared.cronSession.sessionEntry.contextTokens = contextTokens;
prepared.cronSession.sessionEntry.contextTokensSource = contextTokensSource;
}
if (hasNonzeroUsage(usage) || hasNonzeroUsage(finalRunResult.meta?.agentMeta?.usage)) {
const totalTokens = deriveSessionTotalTokens({
usage: lastCallUsage,
contextTokens,
promptTokens
});
if (typeof totalTokens === "number" && Number.isFinite(totalTokens) && totalTokens > 0) {
prepared.cronSession.sessionEntry.totalTokens = totalTokens;
prepared.cronSession.sessionEntry.totalTokensFresh = true;
prepared.cronSession.sessionEntry.totalTokensVersion = 1;
} else {
prepared.cronSession.sessionEntry.totalTokens = void 0;
prepared.cronSession.sessionEntry.totalTokensFresh = false;
prepared.cronSession.sessionEntry.totalTokensVersion = void 0;
}
}
const telemetry = {
model: modelUsed,
provider: providerUsed,
usage: await params.settleUsage(contextTokens)
};
if (params.isAborted()) return prepared.withRunSession({
status: "error",
error: params.abortReason(),
replyDisposition,
diagnostics: mergeCronRunDiagnostics(prepared.preflightDiagnostics, createCronRunDiagnosticsFromAgentResult(finalRunResult, { finalStatus: "error" }), createCronRunDiagnosticsFromError("cron-setup", params.abortReason())),
...telemetry
});
const cronPayloadOutcome = resolveCronPayloadOutcome({
payloads,
runLevelError: finalRunResult.meta?.error,
failureSignal: finalRunResult.meta?.failureSignal,
finalAssistantVisibleText: finalRunResult.meta?.finalAssistantVisibleText,
preferFinalAssistantVisibleText: (await resolveCronChannelOutputPolicy(prepared.resolvedDelivery.channel, { deliveryRequested: prepared.deliveryRequested })).preferFinalAssistantVisibleText
});
if (finalRunResult.meta?.aborted === true && !cronPayloadOutcome.hasFatalErrorPayload) {
const error = normalizeOptionalString(finalRunResult.meta.error?.message) ?? "cron isolated agent run aborted";
await cleanupRunSession("cron-delete-after-run-aborted");
return prepared.withRunSession({
status: "error",
error,
replyDisposition,
diagnostics: mergeCronRunDiagnostics(prepared.preflightDiagnostics, createCronRunDiagnosticsFromAgentResult(finalRunResult, { finalStatus: "error" }), createCronRunDiagnosticsFromError("agent-run", error)),
...telemetry
});
}
const { deliveryDisposition, deliveryPayloadHasStructuredContent, hasFatalStructuredErrorPayload, pendingPresentationWarningError } = cronPayloadOutcome;
let { synthesizedText, deliveryPayloads, summary, outputText, hasFatalErrorPayload, embeddedRunError, agentReportedFailure } = cronPayloadOutcome;
const terminalToolFailure = finalRunResult.meta?.terminalToolFailure;
const hasTerminalToolFailure = isEmbeddedRunTerminalToolFailure(terminalToolFailure);
if (hasFatalErrorPayload && hasTerminalToolFailure) summary = CODE_MODE_MCP_CATALOG_MISS_MESSAGE;
const agentDiagnostics = createCronRunDiagnosticsFromAgentResult(finalRunResult, { finalStatus: hasFatalErrorPayload ? "error" : "ok" });
const runDiagnostics = mergeCronRunDiagnostics(prepared.preflightDiagnostics, agentDiagnostics);
const resolveRunOutcome = (result) => {
const disposition = result?.disposition;
const failure = disposition?.kind === "error" ? disposition : void 0;
const useRunFailure = hasFatalErrorPayload && !failure;
const runError = embeddedRunError ?? "cron isolated run returned an error payload";
const deliveryError = disposition && useRunFailure ? void 0 : result?.deliveryError;
const deliveryDiagnosticError = deliveryError ?? failure?.error;
const output = failure && failure.errorKind !== "delivery-target" ? {} : disposition && !useRunFailure ? {
summary: result?.summary,
outputText: result?.outputText
} : {
summary,
outputText
};
return prepared.withRunSession({
status: failure || hasFatalErrorPayload ? "error" : "ok",
...failure ? {
error: failure.error,
...failure.errorKind ? { errorKind: failure.errorKind } : {}
} : hasFatalErrorPayload ? {
error: runError,
...agentReportedFailure ? { errorClassification: {
kind: "permanent",
reportedByAgent: true
} } : {}
} : {},
...output,
replyDisposition,
deliveryState: result?.deliveryState,
delivered: useRunFailure && disposition?.kind === "pending" ? void 0 : failure?.delivered ?? result?.delivered,
deliveryAttempted: result?.deliveryAttempted,
deliveryError,
deliverySuppressionReason: result?.deliverySuppressionReason,
delivery: result?.delivery,
diagnostics: mergeCronRunDiagnostics(runDiagnostics, useRunFailure && !hasTerminalToolFailure ? createCronRunDiagnosticsFromError("agent-run", runError) : void 0, deliveryDiagnosticError ? createCronRunDiagnosticsFromError("delivery", deliveryDiagnosticError) : void 0),
...telemetry
});
};
const acceptedSessionSpawn = hasAcceptedSessionSpawn(finalRunResult.acceptedSessionSpawns);
const heartbeatOnlyResponse = prepared.deliveryRequested && !hasFatalErrorPayload && deliveryDisposition.kind !== "visible";
const heartbeatControlOnlyResponse = heartbeatOnlyResponse && (deliveryDisposition.kind === "empty" || deliveryDisposition.kind === "heartbeat" && deliveryDisposition.controlOnly);
const spawnOnlyHandoff = acceptedSessionSpawn && (heartbeatControlOnlyResponse || deliveryPayloads.length === 0 && normalizeOptionalString(synthesizedText) === void 0);
if (spawnOnlyHandoff && heartbeatControlOnlyResponse) {
deliveryPayloads = [];
synthesizedText = void 0;
summary = void 0;
outputText = void 0;
}
const skipHeartbeatDelivery = heartbeatOnlyResponse && !spawnOnlyHandoff;
const sourceDeliveryOutcome = resolveSourceDeliveryOutcome(prepared.sourceDelivery, {
didSendViaMessageTool: finalRunResult.didSendViaMessagingTool,
messageToolSentTargets: finalRunResult.messagingToolSentTargets
});
let queueSourceSessionMessageToolAwareness;
if (sourceDeliveryOutcome.visibleDeliveries.length > 0) {
const { queueCronMessageToolDeliveryAwareness } = await loadCronDeliveryRuntime();
queueSourceSessionMessageToolAwareness = await queueCronMessageToolDeliveryAwareness({
cfg: prepared.cfgWithAgentDefaults,
runSessionKey: prepared.runSessionKey,
job: prepared.input.job,
agentId: prepared.agentId,
agentSessionKey: prepared.agentSessionKey,
deferredTargetSessionKey: prepared.input.job.sessionTarget === "current" ? prepared.sourceSessionKey : void 0,
runStartedAt: execution.runStartedAt,
resolvedDelivery: prepared.resolvedDelivery,
sourceDeliveryOutcome
});
}
const hasIntentionalSilentReply = finalRunResult.meta?.terminalReplyKind === "silent-empty" || isSilentReplyPayloadText(finalRunResult.meta?.finalAssistantRawText) || isSilentReplyPayloadText(finalRunResult.meta?.finalAssistantVisibleText);
if (hasFatalStructuredErrorPayload && prepared.deliveryRequested) {
await cleanupRunSession("cron-delete-after-run-fatal-error");
const deliveryTrace = buildCronDeliveryTrace({
deliveryPlan: prepared.deliveryPlan,
resolvedDelivery: prepared.resolvedDelivery,
sourceDeliveryOutcome,
fallbackUsed: false,
delivered: sourceDeliveryOutcome.verifiedMessageToolDelivery
});
await queueSourceSessionMessageToolAwareness?.();
return resolveRunOutcome({
delivered: sourceDeliveryOutcome.verifiedMessageToolDelivery,
deliveryAttempted: sourceDeliveryOutcome.verifiedMessageToolDelivery,
delivery: deliveryTrace
});
}
params.markCronRunSessionCleanupHandled();
const { dispatchCronDelivery } = await loadCronDeliveryRuntime();
const deliveryResult = await dispatchCronDelivery({
cfgWithAgentDefaults: prepared.cfgWithAgentDefaults,
deps: prepared.input.deps,
job: prepared.input.job,
deliveryAttemptFence: prepared.input.deliveryAttemptFence,
agentId: prepared.agentId,
agentSessionKey: prepared.agentSessionKey,
sourceSessionKey: prepared.sourceSessionKey,
sourceSessionGeneration: prepared.sourceSessionGeneration,
runSessionKey: prepared.runSessionKey,
sessionId: prepared.currentRunSessionId(),
lifecycleRevision: prepared.cronSession.lifecycleRevision,
sessionUpdatedAt: prepared.cronSession.sessionEntry.updatedAt,
beforeSessionDelete: params.beforeSessionDelete,
runStartedAt: execution.runStartedAt,
timeoutMs: prepared.timeoutMs,
resolvedDelivery: prepared.resolvedDelivery,
deliveryPlan: prepared.deliveryPlan,
deliveryRequested: prepared.deliveryRequested,
undeliveredRunStatus: hasFatalErrorPayload || pendingPresentationWarningError ? "error" : "ok",
skipDelivery: skipHeartbeatDelivery ? hasIntentionalSilentReply ? "silent" : deliveryDisposition.kind : void 0,
spawnOnlyHandoff,
sourceDeliveryOutcome,
queueSourceSessionMessageToolAwareness,
deliveryBestEffort: prepared.input.job.delivery?.bestEffort === true,
deliveryPayloadHasStructuredContent,
deliveryPayloads,
synthesizedText,
ttsAuto: prepared.cronSession.sessionEntry.ttsAuto,
summary,
outputText,
abortSignal: prepared.input.abortSignal ?? prepared.input.signal,
isAborted: params.isAborted,
abortReason: params.abortReason
});
const deliveryTrace = buildCronDeliveryTrace({
deliveryPlan: prepared.deliveryPlan,
resolvedDelivery: prepared.resolvedDelivery,
sourceDeliveryOutcome,
fallbackUsed: prepared.deliveryRequested && deliveryResult.deliveryAttempted && !sourceDeliveryOutcome.satisfiesSourceDelivery,
delivered: deliveryResult.delivered
});
if (!deliveryResult.disposition) {
summary = deliveryResult.summary;
outputText = deliveryResult.outputText;
}
if (pendingPresentationWarningError && deliveryResult.delivered !== true) {
hasFatalErrorPayload = true;
embeddedRunError = pendingPresentationWarningError;
}
if (deliveryResult.agentReportedFailure && !hasFatalErrorPayload) {
hasFatalErrorPayload = true;
embeddedRunError = deliveryResult.agentReportedFailure;
agentReportedFailure = true;
}
return resolveRunOutcome({
...deliveryResult,
delivery: deliveryTrace
});
}
//#endregion
//#region src/skills/runtime/cron-snapshot.ts
const skillsSnapshotRuntimeLoader = createLazyImportLoader(() => import("./cron-snapshot.runtime.js"));
async function resolveCronSkillsSnapshot(params) {
if (params.isFastTestEnv) return params.existingSnapshot ?? {
prompt: "",
skills: []
};
const runtime = await skillsSnapshotRuntimeLoader.load();
const skillFilter = runtime.resolveEffectiveAgentSkillFilter(params.config, params.agentId);
const nodeSkills = runtime.resolveNodeExecEligibility({
cfg: params.config,
agentId: params.agentId
});
return (await runtime.resolveReusableWorkspaceSkillSnapshot({
workspaceDir: params.workspaceDir,
config: params.config,
agentId: params.agentId,
existingSnapshot: params.existingSnapshot,
librarySelections: params.librarySelections,
skillFilter,
resolveEligibility: () => ({
nodeSkills,
remote: runtime.getRemoteSkillEligibility({ advertiseExecNode: nodeSkills.canExec })
}),
watch: false,
hydrateExisting: false
})).snapshot;
}
//#endregion
//#region src/cron/isolated-agent/model-selection.ts
const CRON_THINKING_HYDRATION_WAIT_MS = 5e3;
function formatCronPayloadModelRejection(params) {
const { modelOverride, error } = params;
if (error.startsWith("model not allowed:")) {
const modelRef = error.slice(18).trim();
const policy = resolveConfiguredModelPolicyAllow(params);
return `automation model override '${modelOverride}' rejected by ${policy.configPath ?? "agents.defaults.modelPolicy.allow"}: ${modelRef} is not in [${policy.refs.length ? policy.refs.toSorted().join(", ") : "(none configured)"}]`;
}
return `automation model override '${modelOverride}' rejected: ${error}`;
}
async function resolveCronModelSelectionOwner(params) {
const owner = params.publishedRuntime ? Object.freeze({
...params.publishedRuntime,
metadataSnapshot: params.publishedRuntime.pluginGeneration.pluginMetadataSnapshot,
modelCatalog: params.publishedRuntime.readFullModelCatalog?.() ?? params.publishedRuntime.modelCatalog
}) : await loadResolvedPublishedModelCatalogOwner({
config: params.cfg,
...params.agentId ? { agentId: params.agentId } : {},
...params.agentDir ? { agentDir: params.agentDir } : {},
...params.workspaceDir ? { workspaceDir: params.workspaceDir } : {},
readOnly: true,
allowGatewaySubagentBinding: true
});
if (params.requiredAgentId && !publishedModelCatalogOwnerMatchesAgent(owner, params.requiredAgentId)) throw new Error(`cron model catalog owner changed from ${params.requiredAgentId} to ${owner.agentId}`);
return owner;
}
async function resolveCronThinkingCatalog(params) {
const catalog = normalizeThinkingCatalogProviders(params.owner.modelCatalog.entries);
if (!needsThinkHydration(catalog, params.provider, params.model, params.agentRuntime)) return catalog;
const hydration = loadProviderScopedThinkingCatalog({
config: params.owner.config,
provider: params.provider,
model: params.model,
agentRuntime: params.agentRuntime,
agentId: params.owner.agentId,
agentDir: params.owner.agentDir,
workspaceDir: params.owner.workspaceDir
});
const refreshed = await raceWithTimeout(hydration.then(normalizeThinkingCatalogProviders), CRON_THINKING_HYDRATION_WAIT_MS, () => void 0, { ref: false });
return refreshed && findModelInCatalog(refreshed, params.provider, params.model) ? refreshed : catalog;
}
async function resolveCronThinkingSelection(params) {
const immutableThinkLevel = normalizeThinkLevel(params.jobThinking) ?? normalizeThinkLevel(params.hookThinking) ?? normalizeThinkLevel(params.sessionThinking);
const requestedThinkLevel = immutableThinkLevel ?? resolveConfiguredThinkingDefaultCore({
cfg: params.cfg,
agentId: params.owner.agentId,
provider: params.provider,
model: params.model
});
return {
catalog: requestedThinkLevel === "off" && params.agentRuntime === "openclaw" ? params.owner.modelCatalog.entries : await resolveCronThinkingCatalog(params),
immutableThinkLevel,
loadThinkingCatalog: async (provider, model, agentRuntime) => await resolveCronThinkingCatalog({
owner: params.owner,
provider,
model,
agentRuntime
}),
requestedThinkLevel
};
}
/** Resolves the effective model for an isolated cron run across defaults, agents, hooks, payload, and session state. */
async function resolveCronModelSelection(params) {
const owner = params.owner ?? await resolveCronModelSelectionOwner({
cfg: params.cfg,
...params.agentId ? {
agentId: params.agentId,
requiredAgentId: params.agentId,
agentDir: params.agentDir,
workspaceDir: params.workspaceDir
} : {}
});
const ownerAgentId = owner.agentId;
const ownerAgentConfigOverride = params.agentConfigOverride ? owner.config === params.cfg && (!params.agentId || ownerAgentId === params.agentId) ? params.agentConfigOverride : resolveAgentConfig(owner.config, ownerAgentId) : void 0;
const { cfgWithAgentDefaults } = resolveCronAgentConfig({
config: owner.config,
agentConfigOverride: ownerAgentConfigOverride
});
const resolvedDefault = resolveConfiguredModelRef({
cfg: cfgWithAgentDefaults,
agentId: ownerAgentId,
defaultProvider: DEFAULT_PROVIDER,
defaultModel: DEFAULT_MODEL,
manifestPlugins: owner.metadataSnapshot
});
const selectionParams = {
cfg: owner.config,
catalog: owner.modelCatalog.entries,
defaultProvider: resolvedDefault.provider,
defaultModel: resolvedDefault,
agentId: ownerAgentId,
manifestPlugins: owner.metadataSnapshot
};
const selection = (ref, modelSource, profileModel) => {
const configuredProfileId = splitTrailingAuthProfile(profileModel ?? "").profile;
return {
ok: true,
provider: ref.provider,
model: ref.model,
modelSource,
...configuredProfileId ? { configuredProfileId } : {},
cfgWithAgentDefaults,
owner
};
};
const override = (raw, source) => {
if (!raw) return;
const resolved = resolveAllowedModelRefCore({
...selectionParams,
raw
});
if (!("error" in resolved)) return selection(resolved.ref, source, source === "session" ? void 0 : raw);
return source === "payload" ? {
ok: false,
error: formatCronPayloadModelRejection({
cfg: owner.config,
agentId: ownerAgentId,
modelOverride: raw,
error: resolved.error
})
} : void 0;
};
const modelOverrideRaw = params.payload.kind === "agentTurn" ? params.payload.model : void 0;
const payloadSelection = override(typeof modelOverrideRaw === "string" ? modelOverrideRaw.trim() : void 0, "payload");
if (payloadSelection) return payloadSelection;
const hooksGmailModelRef = params.isGmailHook ? resolveHooksGmailModel({
cfg: owner.config,
defaultProvider: DEFAULT_PROVIDER,
manifestPlugins: owner.metadataSnapshot
}) : null;
if (hooksGmailModelRef && getModelRefStatus({
...selectionParams,
ref: hooksGmailModelRef
}).allowed) return selection(hooksGmailModelRef, "hook", owner.config.hooks?.gmail?.model);
const sessionModelOverride = params.sessionEntry.modelOverride?.trim();
const sessionSelection = override(sessionModelOverride ? `${params.sessionEntry.providerOverride?.trim() || resolvedDefault.provider}/${sessionModelOverride}` : void 0, "session");
if (sessionSelection) return sessionSelection;
const subagentSelection = resolveSubagentModelConfigSelectionResult({
cfg: owner.config,
agentId: ownerAgentId,
agentConfigOverride: ownerAgentConfigOverride
});
return override(resolvePrimaryStringValue(subagentSelection?.raw), subagentSelection?.source === "agent" ? "agent" : "subagent") ?? selection(resolvedDefault, "default", resolveAgentModelPrimaryValue(cfgWithAgentDefaults.agents?.defaults?.model));
}
//#endregion
//#region src/cron/isolated-agent/run-command-preflight.ts
/** Rejects deterministic command prompts whose stored cap cannot execute them. */
function resolveCronCommandPromptPreflight(job) {
if (job.payload.kind !== "agentTurn" || classifyCronAgentTurnShellPrompt({
...job.payload,
toolsAllow: resolveCronRunToolsAllow(job)
}) !== "commandPromptWithoutShellAccess") return;
const error = `Automation ${job.id} cannot run its requested command because the agent prompt contains a "Command to run:" block but toolsAllow does not grant shell/process access. No command was executed. Recreate it as a command automation with \`openclaw automations add ... --command "<shell>"\`, or explicitly reauthorize this job from a trusted operator shell with \`openclaw automations edit ${job.id} --tools exec,process\`.`;
return {
status: "error",
error,
diagnostics: createCronRunDiagnosticsFromError("cron-preflight", error)
};
}
//#endregion
//#region src/cron/isolated-agent/run-current-context.ts
const CURRENT_CONTEXT_RAW_MESSAGES_MAX = 220;
const CURRENT_CONTEXT_READ_MAX_BYTES = 262144;
const CURRENT_CONTEXT_MAX_LINE_CHARS = 220;
const CURRENT_CONTEXT_MAX_BLOCK_CHARS = 1400;
const CURRENT_CONTEXT_HEADER = "Recent conversation:";
function truncateContextLine(role, text) {
const prefix = `- ${role === "user" ? "User" : "Assistant"}: `;
const textLimit = CURRENT_CONTEXT_MAX_LINE_CHARS - prefix.length;
if (text.length <= textLimit) return `${prefix}${text}`;
return `${prefix}${truncateUtf16Safe(text, textLimit - 3).trimEnd()}...`;
}
function formatCurrentConversationContext(messages) {
const lines = projectChatDisplayMessages(messages, {
maxChars: CURRENT_CONTEXT_MAX_LINE_CHARS,
resolveCronJobName: () => void 0
}).flatMap((message) => {
if (!message || typeof message !== "object" || Array.isArray(message)) return [];
const record = message;
if (record.role !== "user" && record.role !== "assistant") return [];
const text = extractTextFromChatContent(record.content);
return text ? [truncateContextLine(record.role, text)] : [];
}).slice(-10);
while (lines.length > 0 && `${CURRENT_CONTEXT_HEADER}\n${lines.join("\n")}`.length > CURRENT_CONTEXT_MAX_BLOCK_CHARS) lines.shift();
return lines.length > 0 ? `${CURRENT_CONTEXT_HEADER}\n${lines.join("\n")}` : void 0;
}
async function buildCurrentConversationContextBlock(params, deps = {}) {
const sessionId = params.sourceSessionEntry.sessionId?.trim();
if (!sessionId) return;
try {
const messages = await (deps.readSessionMessages ?? readSessionMessagesAsync)({
agentId: params.agentId,
sessionEntry: params.sourceSessionEntry,
sessionId,
sessionKey: params.sourceSessionKey,
storePath: params.storePath
}, {
mode: "recent",
maxBytes: CURRENT_CONTEXT_READ_MAX_BYTES,
maxLines: CURRENT_CONTEXT_RAW_MESSAGES_MAX,
maxMessages: CURRENT_CONTEXT_RAW_MESSAGES_MAX
});
return formatCurrentConversationContext(Array.isArray(messages) ? messages : []);
} catch {
return;
}
}
//#endregion
//#region src/cron/isolated-agent/run-prepare-runtime.ts
/** Lazy preparation runtimes and session lifecycle helpers for cron runs. */
const sessionAccessorRuntimeLoader = createLazyImportLoader(() => import("./session-accessor-C2axcAVZ.mjs"));
const cronExternalContentRuntimeLoader = createLazyImportLoader(() => import("./run-external-content.runtime.js"));
const cronAuthProfileRuntimeLoader = createLazyImportLoader(() => import("./run-auth-profile.runtime.js"));
async function loadSessionAccessorRuntime() {
return await sessionAccessorRuntimeLoader.load();
}
async function loadCronExternalContentRuntime() {
return await cronExternalContentRuntimeLoader.load();
}
function hasConfiguredAuthProfiles(cfg) {
return Boolean(cfg.auth?.profiles && Object.keys(cfg.auth.profiles).length > 0) || Boolean(cfg.auth?.order && Object.keys(cfg.auth.order).length > 0);
}
/**
* Resolves the run's auth profile, skipping the lazy runtime entirely when no
* override, configured profile, or store source exists for it to find. Auth
* resolution may mutate session state, so it uses the store and key that
* persistence will write.
*/
async function resolveCronAuthSelection(params) {
if (!Boolean(params.cronSession.sessionEntry.authProfileOverride?.trim()) && !hasConfiguredAuthProfiles(params.cfg) && !hasAnyAuthProfileStoreSource(params.agentDir)) return;
return await (await cronAuthProfileRuntimeLoader.load()).resolveSessionAuthSelection({
agentId: params.agentId,
cfg: params.cfg,
provider: params.provider,
modelId: params.modelId,
...params.configuredProfileId ? { configuredProfileId: params.configuredProfileId } : {},
harnessRuntime: params.harnessRuntime,
agentDir: params.agentDir,
sessionEntry: params.cronSession.sessionEntry,
sessionStore: params.cronSession.store,
sessionKey: params.sessionKey,
storePath: params.cronSession.storePath,
isNewSession: params.isNewSession
});
}
async function retireRolledCronSessionMcpRuntime(params) {
if (params.job.sessionTarget === "isolated") return;
const previousSessionId = normalizeOptionalString(params.cronSession.previousSessionId);
const currentSessionId = normalizeOptionalString(params.cronSession.sessionEntry.sessionId);
if (!previousSessionId || previousSessionId === currentSessionId) return;
await retireSessionMcpRuntime({
sessionId: previousSessionId,
reason: "cron-session-rollover",
onError: (error, sessionId) => {
logWarn(`[cron:${params.job.id}] Failed to dispose retired bundle MCP runtime for session ${sessionId}: ${String(error)}`);
}
});
}
function appendCronUnattendedRunPreamble(commandBody, opts) {
return `${commandBody}\n\n${`This is an unattended scheduled run. Nobody is present to clarify or approve, so complete the task with what you have. Your final reply is the deliverable — not a plan, an acknowledgement, or a request for input. If nothing needs doing, reply exactly ${SILENT_REPLY_TOKEN}. If something failed, start with ${AUTOMATION_FAILED_TOKEN} on its own line, then state what failed and what you tried — the scheduler owns retries and failure alerts.`}${opts.externalHook ? "" : " Where the job's own instructions conflict with this preamble, the job's instructions win (a question or plan the job explicitly requests is a valid deliverable). If this job is no longer needed, remove it if your available tools allow."}`;
}
//#endregion
//#region src/cron/isolated-agent/run-timeout.ts
/** Converts cron payload timeout overrides into embedded-runner timeout signals. */
/** Converts explicit cron payload timeoutSeconds into a timer-safe millisecond override signal. */
function resolveCronRunTimeoutOverrideMs(timeoutSeconds) {
const timeoutMs = Math.floor((timeoutSeconds ?? 0) * 1e3);
return Number.isFinite(timeoutSeconds) && timeoutMs > 0 ? Math.min(timeoutMs, MAX_TIMER_TIMEOUT_MS) : void 0;
}
//#endregion
//#region src/cron/isolated-agent/run-workspace.ts
/** Keep workspace selection, provisioning, and failed-preparation lease cleanup together. */
async function prepareCronSessionWorkspace(params) {
const { sessionWorkAdmission, sessionKey } = params;
const abortSignal = params.input.abortSignal ?? params.input.signal;
const assertCurrent = () => {
abortSignal?.throwIfAborted();
if (!sessionWorkAdmission.isActive()) throw new CronSessionLifecycleClaimError(sessionKey);
};
const selected = await resolveCronSessionWorkspace({
cfg: params.cfg,
agentId: params.agentId,
sessionTarget: params.input.job.sessionTarget,
admissionSource: params.input.admissionSource,
ownerSessionKey: params.input.job.owner?.sessionKey,
sessionKey: params.sessionKey,
entry: params.cronSession.initialSessionEntry,
defaultWorkspaceDir: params.defaultWorkspaceDir,
executionRoot: params.input.executionRoot,
assertCurrent
});
try {
const provisioning = await (await import("./acp-workspace-provisioning-BfXOvM5y.mjs")).resolveAcpAgentWorkspaceProvisioningForTurn({
cfg: params.cfg,
agentId: params.agentId,
workspaceDir: params.defaultWorkspaceDir,
cwd: selected.cwd,
sessionKey: params.sessionKey,
sessionEntry: params.cronSession.sessionEntry
});
assertCurrent();
await ensureAgentWorkspace({
dir: params.defaultWorkspaceDir,
ensureBootstrapFiles: !params.agentCfg.skipBootstrap && !params.isFastTestEnv,
skipOptionalBootstrapFiles: params.agentCfg.skipOptionalBootstrapFiles,
provisioning,
guard: { assertHost: assertCurrent }
});
assertCurrent();
return selected;
} catch (error) {
await selected.lease?.release();
throw error;
}
}
/** Resolve only persisted session bindings; the job prompt never supplies filesystem authority. */
async function resolveCronSessionWorkspace(params) {
const target = resolveCronSessionTargetSessionKey(params.sessionTarget);
if (!target) return { workspaceDir: params.defaultWorkspaceDir };
if (resolveCronAgentSessionKey({
sessionKey: target,
agentId: params.agentId,
cfg: params.cfg,
mainKey: params.cfg.session?.mainKey
}) !== params.sessionKey) throw new CronSessionLifecycleClaimError(params.sessionKey, "Bound automation session is mismatched.");
const entry = params.entry;
if (!entry) return { workspaceDir: params.defaultWorkspaceDir };
const override = resolveIngressWorkspaceOverrideForSessionRun({
spawnedBy: entry.spawnedBy,
workspaceDir: entry.spawnedWorkspaceDir,
cwd: entry.spawnedCwd
});
const requestedCwd = normalizeOptionalString(entry.spawnedCwd);
if (params.admissionSource === "requester-schedule" && (override || requestedCwd || entry.worktree)) {
const ownerSessionKey = normalizeOptionalString(params.ownerSessionKey);
if (!ownerSessionKey || resolveCronAgentSessionKey({
sessionKey: ownerSessionKey,
agentId: params.agentId,
cfg: params.cfg,
mainKey: params.cfg.session?.mainKey
}) !== params.sessionKey) throw new CronSessionLifecycleClaimError(params.sessionKey, "Requester-scoped automation can only use its owning conversation’s workspace.");
}
const workspaceDir = override ? await fs.realpath(resolveUserPath(override)) : params.defaultWorkspaceDir;
const cwd = requestedCwd ? await fs.realpath(resolveUserPath(requestedCwd)) : void 0;
params.assertCurrent();
if (params.executionRoot && (override || requestedCwd || entry.worktree) && path.resolve(params.executionRoot) !== path.resolve(workspaceDir)) throw new CronSessionLifecycleClaimError(params.sessionKey, "Bound automation workspace conflicts with its execution root.");
const binding = entry.worktree;
if (!binding) return {
workspaceDir,
cwd
};
const { captureOpenClawStateWorkerContext } = await import("./openclaw-state-worker-context-Cbmnm5EG.mjs");
const { readRegistryWorktree } = await import("./registry-read-C_xLl4VE.mjs");
const { acquireWorktreeRunLease } = await import("./run-lease-DHLKQYUM.mjs");
params.assertCurrent();
const state = captureOpenClawStateWorkerContext();
const validateWorktree = async () => {
const record = await readRegistryWorktree(state, binding.id);
params.assertCurrent();
if (!record || record.id !== binding.id || record.ownerKind !== "session" || record.ownerId !== params.sessionKey || record.removedAt !== void 0 || record.branch !== binding.branch || path.resolve(record.repoRoot) !== path.resolve(binding.repoRoot)) throw new CronSessionLifecycleClaimError(params.sessionKey, "Bound automation worktree is missing, retired, or mismatched.");
const root = await fs.realpath(record.path);
params.assertCurrent();
if (!isPathInside(root, workspaceDir) || !isPathInside(root, cwd ?? workspaceDir)) throw new CronSessionLifecycleClaimError(params.sessionKey, "Bound automation workspace does not match its managed worktree.");
};
await validateWorktree();
const lease = await acquireWorktreeRunLease(binding.id);
try {
await validateWorktree();
return {
workspaceDir,
cwd,
lease
};
} catch (error) {
await lease.release();
throw error;
}
}
//#endregion
//#region src/cron/isolated-agent/run-prepare.ts
/** Session identity and context preparation for isolated cron runs. */
async function prepareCronRunContext(params) {
try {
var _usingCtx$2 = _usingCtx();
const { input } = params;
const commandPromptPreflight = resolveCronCommandPromptPreflight(input.job);
if (commandPromptPreflight) return {
ok: false,
result: commandPromptPreflight
};
const requestedRuntimeCfg = resolveCronActiveRuntimeConfig(input.cfg);
const requestedAgentId = input.agentId?.trim() || input.job.agentId?.trim();
const requiredAgentId = (requestedAgentId ? normalizeAgentId(requestedAgentId) : void 0) ?? parseAgentSessionKey(input.job.sessionKey ?? input.sessionKey)?.agentId;
const initialAgentId = resolveCronJobEffectiveAgentId({ agentId: requiredAgentId }, tryResolveAmbientOwnerAgentId(requestedRuntimeCfg));
const runtimeResources = _usingCtx$2.a(new AsyncDisposableStack());
let runtimeLease;
const publishedRuntime = await loadPublishedGatewayReplyDispatchRuntime({
agentId: initialAgentId,
onRuntimeLease: (lease) => {
runtimeLease = runtimeResources.use(scopePreparedModelRuntimeLease(lease));
},
abortSignal: input.abortSignal ?? input.signal
});
const modelOwner = await resolveCronModelSelectionOwner({
cfg: requestedRuntimeCfg,
publishedRuntime,
...requiredAgentId ? {
agentId: initialAgentId,
requiredAgentId,
agentDir: resolveAgentDir(requestedRuntimeCfg, initialAgentId),
workspaceDir: resolveAgentWorkspaceDir(requestedRuntimeCfg, initialAgentId)
} : {}
});
const { agentId, agentDir } = modelOwner;
const agentConfigOverride = requiredAgentId ? resolveAgentConfig(modelOwner.config, agentId) : void 0;
const { runtimeConfig: runtimeCfg, agentDefaults: agentCfg } = resolveCronAgentConfig({
config: modelOwner.config,
agentConfigOverride
});
const baseSessionKey = (input.sessionKey?.trim() || `cron:${input.job.id}`).trim();
const currentBoundSourceKey = input.job.sessionTarget === "current" ? input.job.sessionKey?.trim() : void 0;
const usesDetachedRunSession = isDetachedCronSessionTarget(input.job.sessionTarget) || Boolean(currentBoundSourceKey);
const baseSessionKeyIsCron = baseSessionKey.startsWith("cron:") || isCronSessionKey(baseSessionKey);
const cronExecutionSessionKey = usesDetachedRunSession && !baseSessionKeyIsCron ? `cron:${input.job.id}` : baseSessionKey;
const agentSessionKey = resolveCronAgentSessionKey({
sessionKey: cronExecutionSessionKey,
agentId,
mainKey: runtimeCfg.session?.mainKey,
cfg: runtimeCfg
});
const resolvedBaseSessionKey = resolveCronAgentSessionKey({
sessionKey: currentBoundSourceKey ?? baseSessionKey,
agentId,
mainKey: runtimeCfg.session?.mainKey,
cfg: runtimeCfg
});
const sourceSessionKey = currentBoundSourceKey && resolvedBaseSessionKey !== agentSessionKey ? resolvedBaseSessionKey : void 0;
const hookExternalContentSource = (input.job.payload.kind === "agentTurn" ? input.job.payload.externalContentSource : void 0) ?? resolveHookExternalContentSource(baseSessionKey);
const isGmailHook = hookExternalContentSource === "gmail";
return runtimeLease ? await runtimeLease.run(prepareAdmittedSession) : await prepareAdmittedSession();
async function prepareAdmittedSession() {
const now = Date.now();
const sandbox = resolveCreatorSandbox(runtimeCfg, { actor: input.job.createdActor });
const cronSession = await prepareCronSession({
cfg: runtimeCfg,
sessionKey: agentSessionKey,
sourceSessionKey,
skillLibrarySelections: input.job.skillLibrarySelections,
agentId,
nowMs: now,
forceNew: usesDetachedRunSession,
hookExternalContentSource
});
const sourceEntry = sourceSessionKey ? cronSession.store[sourceSessionKey] : void 0;
const sourceSessionGeneration = sourceEntry ? {
sessionId: sourceEntry.sessionId,
lifecycleRevision: sourceEntry.lifecycleRevision
} : void 0;
const reservedKey = isAgentHarnessSessionKey(agentSessionKey);
if (cronSession.initialSessionEntry?.modelSelectionLocked === true) throw new Error(reservedKey ? AGENT_HARNESS_SESSION_KEY_RESERVED_MESSAGE : AGENT_HARNESS_SESSION_ID_LOCKED_MESSAGE);
if (reservedKey && !cronSession.initialSessionEntry) throw new Error(AGENT_HARNESS_SESSION_KEY_RESERVED_MESSAGE);
const runSessionId = cronSession.sessionEntry.sessionId;
const currentRunSessionId = () => cronSession.sessionEntry.sessionId ?? runSessionId;
const usesExactRunSession = usesDetachedRunSession || baseSessionKey.startsWith("cron:");
const runSessionKey = usesExactRunSession ? `${agentSessionKey}:run:${runSessionId}` : agentSessionKey;
const sessionWorkAdmission = await beginCronSessionWorkAdmission({
cronSession,
agentSessionKey,
runSessionKey,
signal: input.abortSignal ?? input.signal,
onInterrupt: params.onLifecycleInterrupt
});
clearBootstrapSnapshotOnSessionRollover({
sessionKey: agentSessionKey,
previousSessionId: cronSession.previousSessionId
});
let preparedModelRuntimeLease;
let workspaceLease;
let workspaceLeaseTransferred = false;
try {
const selectedWorkspace = await prepareCronSessionWorkspace({
cfg: runtimeCfg,
agentId,
input,
agentCfg,
sessionKey: agentSessionKey,
cronSession,
defaultWorkspaceDir: modelOwner.workspaceDir,
sessionWorkAdmission,
isFastTestEnv: params.isFastTestEnv
});
workspaceLease = selectedWorkspace.lease;
const workspaceDir = selectedWorkspace.workspaceDir;
const executionWorkspaceDir = input.executionRoot ?? workspaceDir;
const persistCronSessionRow = async ({ storePath, sessionKey, fallbackEntry, resetBoundary, update, assertCommitAllowed }) => {
const { applySessionEntryLifecycleMutation, patchSessionEntryCore } = await loadSessionAccessorRuntime();
if (resetBoundary) {
await applySessionEntryLifecycleMutation({
activeSessionKey: sessionKey,
agentId,
storePath,
upserts: [{
sessionKey,
resetBoundary,
buildEntry: ({ currentEntry }) => update(currentEntry)
}],
skipMaintenance: true
});
return;
}
await patchSessionEntryCore({
storePath,
sessionKey,
agentId
}, (_entry, context) => update(context.existingEntry), {
fallbackEntry,
replaceEntry: true,
assertCommitAllowed
});
};
const persistSessionEntry = createPersistCronSessionEntry({
cronSession,
agentSessionKey,
createdActor: input.job.createdActor,
sandbox,
workspaceDir,
persistSessionEntry: persistCronSessionRow
});
const withRunSession = (result) => ({
...result,
sessionId: currentRunSessionId(),
sessionKey: runSessionKey
});
if (!cronSession.sessionEntry.label?.trim() && baseSessionKey.startsWith("cron:")) {
const labelSuffix = typeof input.job.name === "string" && input.job.name.trim() ? input.job.name.trim() : input.job.id;
cronSession.sessionEntry.label = `Automation: ${labelSuffix}`;
}
const resolvedModelSelection = await resolveCronModelSelection({
cfg: runtimeCfg,
owner: modelOwner,
agentConfigOverride,
sessionEntry: cronSession.sessionEntry,
payload: input.job.payload,
isGmailHook,
agentId,
agentDir,
workspaceDir: executionWorkspaceDir
});
if (!resolvedModelSelection.ok) {
sessionWorkAdmission.release();
return {
ok: false,
result: withRunSession({
status: "error",
error: resolvedModelSelection.error,
diagnostics: createCronRunDiagnosticsFromError("cron-preflight", resolvedModelSelection.error)
})
};
}
const cfgWithAgentDefaults = resolvedModelSelection.cfgWithAgentDefaults;
const ownerAgentConfig = resolveAgentConfig(modelOwner.config, modelOwner.agentId);
const matchesDefaultFallbackAgentStringModel = typeof ownerAgentConfig?.model === "string" && resolveAgentModelPrimaryValue(ownerAgentConfig.model) === resolveAgentModelPrimaryValue(modelOwner.config.agents?.defaults?.model);
const useSubagentFallbacks = resolvedModelSelection.modelSource === "subagent";
const inheritDefaultFallbacksForAgentStringModel = matchesDefaultFallbackAgentStringModel && (resolvedModelSelection.modelSource === "default" || resolvedModelSelection.modelSource === "agent");
const preflight = await resolveCronPreflight({
cfg: cfgWithAgentDefaults,
job: input.job,
agentId: modelOwner.agentId,
provider: resolvedModelSelection.provider,
model: resolvedModelSelection.model,
useSubagentFallbacks,
inheritDefaultFallbacksForAgentStringModel
});
if (!preflight.ok) {
logWarn(`[cron:${input.job.id}] ${preflight.reason}`);
sessionWorkAdmission.release();
return {
ok: false,
result: withRunSession({
status: "skipped",
error: preflight.reason,
diagnostics: createCronRunDiagnosticsFromError("model-preflight", preflight.reason, { severity: "warn" }),
provider: resolvedModelSelection.provider,
model: resolvedModelSelection.model
})
};
}
const { provider, model, modelFallbacksOverride, runtimePluginCandidates } = preflight;
const effectiveAgentRuntime = resolveEffectiveAgentRuntime({
cfg: cfgWithAgentDefaults,
provider,
modelId: model,
agentId: modelOwner.agentId,
sessionKey: agentSessionKey,
sessionEntry: cronSession.sessionEntry
});
const thinkingSelection = await resolveCronThinkingSelection({
cfg: cfgWithAgentDefaults,
owner: modelOwner,
provider,
model,
agentRuntime: effectiveAgentRuntime,
jobThinking: input.job.payload.kind === "agentTurn" ? input.job.payload.thinking : void 0,
hookThinking: isGmailHook ? runtimeCfg.hooks?.gmail?.thinking : void 0,
sessionThinking: cronSession.sessionEntry.thinkingLevel
});
const { requestedLevel: requestedThinkLevel, level: fallbackThinkLevel, supported: thinkingLevelSupported } = resolveThinkingSelectionCore({
cfg: cfgWithAgentDefaults,
agentId: modelOwner.agentId,
provider,
model,
level: thinkingSelection.requestedThinkLevel,
catalog: thinkingSelection.catalog,
agentRuntime: effectiveAgentRuntime
});
if (!thinkingLevelSupported && fallbackThinkLevel !== requestedThinkLevel) logWarn(`[cron:${input.job.id}] Thinking level "${requestedThinkLevel}" is not supported for ${provider}/${model}; using "${fallbackThinkLevel}" for this candidate.`);
preparedModelRuntimeLease = await acquireAgentRunPreparedModelRuntime({
config: modelOwner.config,
agentId,
agentDir,
workspaceDir,
allowGatewaySubagentBinding: true,
runtimePluginSelections: runtimePluginCandidates.map((candidate) => {
const runtime = resolveSessionRuntimeOverrideForProvider({
provider: candidate.provider,
entry: cronSession.sessionEntry,
cfg: cfgWithAgentDefaults
});
return runtime ? {
provider: candidate.provider,
modelId: candidate.model,
runtime,
agentId
} : {
provider: candidate.provider,
modelId: candidate.model,
agentId
};
})
}, {
catalogMode: "static",
...publishedRuntime ? { pluginGeneration: publishedRuntime.pluginGeneration } : { pluginMetadataSnapshot: modelOwner.metadataSnapshot },
abortSignal: input.abortSignal ?? input.signal
});
const admittedConfig = preparedModelRuntimeLease.snapshot.config;
const explicitTimeoutSeconds = input.job.payload.kind === "agentTurn" ? input.job.payload.timeoutSeconds : void 0;
const timeoutMs = resolveAgentTimeoutMs({
cfg: cfgWithAgentDefaults,
overrideSeconds: explicitTimeoutSeconds
});
const runTimeoutOverrideMs = resolveCronRunTimeoutOverrideMs(explicitTimeoutSeconds);
const agentPayload = input.job.payload.kind === "agentTurn" ? {
...input.job.payload,
toolsAllow: resolveCronRunToolsAllow(input.job)
} : null;
const configuredProvider = admittedConfig.models?.providers?.[provider];
const modelApi = findModelInCatalog(thinkingSelection.catalog, provider, model)?.api ?? configuredProvider?.models?.find((candidate) => candidate.id === model)?.api ?? configuredProvider?.api;
const preflightDiagnostics = await createCronToolsAllowPreflightDiagnostics({
cfg: admittedConfig,
jobId: input.job.id,
provider,
model,
modelApi,
agentId: modelOwner.agentId,
agentDir: modelOwner.agentDir,
sessionKey: agentSessionKey,
agentPayload
});
const { deliveryPlan, deliveryRequested, resolvedDelivery, sourceDelivery, deliverySystemPrompt, messageToolFormatPrompt } = await resolveCronDeliveryContext({
cfg: admittedConfig,
job: input.job,
agentId
});
const { formattedTime, timeLine } = resolveCronStyleNow(runtimeCfg, now);
const currentConversationContext = input.job.sessionTarget === "current" && agentPayload && sourceSessionKey && sourceEntry ? await buildCurrentConversationContextBlock({
agentId,
sourceSessionEntry: sourceEntry,
sourceSessionKey,
storePath: cronSession.storePath
}) : void 0;
const turnMessage = input.job.payload.kind === "agentTurn" ? input.job.payload.message : input.message;
const message = currentConversationContext ? `${currentConversationContext}\n\n${turnMessage}` : turnMessage;
const sourcePromptPrefix = `[cron:${input.job.id} ${input.job.name}]`;
const base = `${sourcePromptPrefix} ${message}`.trim();
const isExternalHook = hookExternalContentSource !== void 0 || isExternalHookSession(baseSessionKey);
const allowUnsafeExternalContent = agentPayload?.allowUnsafeExternalContent === true || isGmailHook && input.cfg.hooks?.gmail?.allowUnsafeExternalContent === true;
const shouldWrapExternal = isExternalHook && !allowUnsafeExternalContent;
let commandBody;
if (isExternalHook) {
const { detectSuspiciousPatterns } = await loadCronExternalContentRuntime();
const suspiciousPatterns = detectSuspiciousPatterns(message);
if (suspiciousPatterns.length > 0) logWarn(`[security] Suspicious patterns detected in external hook content (session=${baseSessionKey}, patterns=${suspiciousPatterns.length}): ${suspiciousPatterns.slice(0, 3).join(", ")}`);
}
if (shouldWrapExternal) {
const { buildSafeExternalPrompt } = await loadCronExternalContentRuntime();
commandBody = `${buildSafeExternalPrompt({
content: message,
source: mapHookExternalContentSource(hookExternalContentSource ?? "webhook"),
jobName: input.job.name,
jobId: input.job.id,
timestamp: formattedTime
})}\n\n${timeLine}`.trim();
} else commandBody = `${base}\n${timeLine}`.trim();
commandBody = appendCronUnattendedRunPreamble(commandBody, { externalHook: isExternalHook });
const skillsSnapshot = input.skillsSnapshot ?? await resolveCronSkillsSnapshot({
workspaceDir: executionWorkspaceDir,
config: admittedConfig,
agentId,
existingSnapshot: cronSession.sessionEntry.skillsSnapshot,
librarySelections: cronSession.sessionEntry.skillLibrarySelections,
isFastTestEnv: params.isFastTestEnv
});
await persistCronSkillsSnapshotIfChanged({
isFastTestEnv: params.isFastTestEnv,
cronSession,
skillsSnapshot,
nowMs: Date.now(),
persistSessionEntry
});
setCronSessionRuntimeModel({
entry: cronSession.sessionEntry,
provider,
model
});
cronSession.sessionEntry.systemSent = true;
try {
await persistSessionEntry();
} catch (err) {
if (err instanceof CronSessionLifecycleClaimError) throw err;
logWarn(`[cron:${input.job.id}] Failed to persist pre-run session entry: ${String(err)}`);
if (sandbox === "required" || cronSession.sessionEntry.sandbox === "required") throw err;
}
await retireRolledCronSessionMcpRuntime({
job: input.job,
cronSession
});
const authSelection = await resolveCronAuthSelection({
agentId,
cfg: admittedConfig,
provider,
modelId: model,
...provider === resolvedModelSelection.provider && resolvedModelSelection.configuredProfileId ? { configuredProfileId: resolvedModelSelection.configuredProfileId } : {},
harnessRuntime: effectiveAgentRuntime,
agentDir,
cronSession,
sessionKey: agentSessionKey,
isNewSession: cronSession.isNewSession && input.job.sessionTarget !== "isolated"
});
const authProfileId = authSelection?.profileId;
const liveSelection = {
provider,
model,
agentRuntimeOverride: resolveSessionRuntimeOverrideForProvider({
provider,
entry: cronSession.sessionEntry,
cfg: admittedConfig
}),
authProfileId,
authProfileIdSource: authSelection?.source
};
const runContinuationSession = usesExactRunSession ? createCronRunContinuationSession({
cronSession,
runSessionKey,
createdActor: input.job.createdActor,
sandbox,
thinkingLevel: requestedThinkLevel,
toolsAllow: agentPayload?.toolsAllow,
toolsAllowIsDefault: agentPayload?.toolsAllowIsDefault,
scheduledToolPolicy: resolveCronScheduledToolPolicy({
toolsAllow: agentPayload?.toolsAllow,
scheduledToolPolicy: input.job.scheduledToolPolicy,
owner: input.job.owner
}),
scheduledToolCallerOrigin: input.job.toolsAllowProvenance?.callerOrigin,
toolsAllowExecTarget: input.job.toolsAllowExecTarget,
toolsAllowExecTargetRequirement: input.job.toolsAllowExecTargetRequirement,
cliSessionBindingFacts: {
extraSystemPromptStatic: deliverySystemPrompt,
sourceReplyDeliveryMode: sourceDelivery.sourceReplyDeliveryMode,
requireExplicitMessageTarget: sourceDelivery.messageTool.requireExplicitTarget
},
persistSessionEntry: persistCronSessionRow
}) : void 0;
await runContinuationSession?.initialize();
workspaceLeaseTransferred = true;
return {
ok: true,
context: {
input,
cfgWithAgentDefaults: admittedConfig,
agentId,
agentCfg,
agentDir,
agentSessionKey,
sourceSessionKey,
sourceSessionGeneration,
runSessionId,
currentRunSessionId,
runSessionKey,
usesDetachedRunSession,
workspaceDir,
cwd: selectedWorkspace.cwd,
workspaceLease,
executionRoot: input.executionRoot,
commandBody,
inputProvenance: agentPayload && !isExternalHook ? {
kind: "internal_system",
sourceTool: "cron",
sourcePromptPrefix,
jobId: input.job.id,
runId: runSessionId,
sourceSessionKey: runSessionKey
} : void 0,
cronSession,
sessionWorkAdmission,
persistSessionEntry,
runContinuationSession,
withRunSession,
agentPayload,
deliveryPlan,
resolvedDelivery,
deliveryRequested,
deliverySystemPrompt,
messageToolFormatPrompt,
sourceDelivery,
suppressExecNotifyOnExit: deliveryPlan.mode === "none",
skillsSnapshot,
liveSelection,
useSubagentFallbacks,
inheritDefaultFallbacksForAgentStringModel,
modelFallbacksOverride,
thinkingSelection,
timeoutMs,
preflightDiagnostics,
runTimeoutOverrideMs,
preparedModelRuntimeLease
}
};
} catch (error) {
try {
try {
var _usingCtx3 = _usingCtx();
_usingCtx3.a(preparedModelRuntimeLease);
throw error;
} catch (_) {
_usingCtx3.e = _;
} finally {
await _usingCtx3.d();
}
} finally {
sessionWorkAdmission.release();
}
} finally {
if (!workspaceLeaseTransferred) await workspaceLease?.release();
}
}
} catch (_) {
_usingCtx$2.e = _;
} finally {
await _usingCtx$2.d();
}
}
//#endregion
//#region src/cron/isolated-agent/run.ts
const cronExecutorRuntimeLoader = createLazyImportLoader(() => import("./run-executor.runtime.js"));
async function disposeCronRunContext(params) {
releaseAgentRunContext(params.runId, params.runContextOwnerToken);
if (params.ownsSessionRuntime) await retireSessionMcpRuntime({
sessionId: params.sessionId,
reason: "isolated-cron-dispose",
onError: (error, sid) => {
logWarn(`[cron] Failed to retire MCP runtime during isolated cron dispose ${sid}: ${String(error)}`);
}
}).catch(() => {});
params.cronSession.store = {};
}
/** Runs one isolated cron agent turn, including setup, execution, delivery, and persistence. */
async function runCronIsolatedAgentTurn(params) {
return await runWithDiagnosticTraceContext(createDiagnosticTraceContextFromActiveScope(), () => runCronIsolatedAgentTurnInTrace(params));
}
async function runCronIsolatedAgentTurnInTrace(params) {
try {
var _usingCtx$1 = _usingCtx();
const admittedLifecycleGeneration = getAgentEventLifecycleGeneration();
const upstreamAbortSignal = params.abortSignal ?? params.signal;
const lifecycleAbortController = new AbortController();
const abortSignal = upstreamAbortSignal ? AbortSignal.any([upstreamAbortSignal, lifecycleAbortController.signal]) : lifecycleAbortController.signal;
const isAborted = () => abortSignal?.aborted ?? false;
const abortReason = () => resolveCronAbortReasonText(abortSignal?.reason) ?? "cron: job execution timed out";
const isFastTestEnv = isFastTestRuntimeEnv();
let prepared;
try {
prepared = await prepareCronRunContext({
input: {
...params,
abortSignal
},
isFastTestEnv,
onLifecycleInterrupt: () => lifecycleAbortController.abort(createAgentRunRestartAbortError())
});
} catch (err) {
if (err instanceof CronExecutionRootRuntimeError) return {
status: "error",
error: err.message,
admissionDisposition: "rejected"
};
if (err instanceof CronSessionLifecycleClaimError) return {
status: "error",
error: err.message,
admissionDisposition: err.admissionDisposition
};
throw err;
}
if (!prepared.ok) return {
...prepared.result,
admissionDisposition: "rejected"
};
_usingCtx$1.a({ [Symbol.asyncDispose]: async () => {
await prepared.context.workspaceLease?.release();
} });
const preparedRuntimeLease = _usingCtx$1.a(prepared.context.preparedModelRuntimeLease);
let leaseActive = true;
try {
return await withPreparedModelRuntimePluginGenerationScope(preparedRuntimeLease.pluginGeneration, () => withPluginRuntimeGenerationScope(preparedRuntimeLease.snapshot, async () => {
const runId = randomUUID();
const initialSessionId = prepared.context.cronSession.sessionEntry.sessionId;
const ownsSessionRuntime = params.job.sessionTarget === "isolated";
let runContextOwnerToken;
let runLifecycleGeneration = admittedLifecycleGeneration;
let executionStarted = false;
const notifyExecutionStarted = (info) => {
executionStarted = true;
if (info?.lifecycleGeneration) runLifecycleGeneration = info.lifecycleGeneration;
params.onExecutionStarted?.({
jobId: params.job.id,
agentId: prepared.context.agentId,
sessionId: prepared.context.currentRunSessionId(),
sessionKey: prepared.context.runSessionKey,
runId,
...info?.isFallback === true ? { isFallback: true } : {},
phase: "runner_entered",
provider: info?.provider ?? prepared.context.liveSelection.provider,
model: info?.model ?? prepared.context.liveSelection.model
});
};
const notifyExecutionPhase = (info) => {
params.onExecutionPhase?.({
jobId: params.job.id,
agentId: prepared.context.agentId,
sessionId: prepared.context.currentRunSessionId(),
sessionKey: prepared.context.runSessionKey,
provider: prepared.context.liveSelection.provider,
model: prepared.context.liveSelection.model,
...info
});
};
const turnStartedAtMs = Date.now();
const diagnosticsEnabled = isDiagnosticsEnabled(params.cfg);
const messageLifecycle = (() => {
try {
const lifecycle = createDiagnosticMessageLifecycle({
enabled: diagnosticsEnabled,
sessionId: prepared.context.runSessionId,
sessionKey: prepared.context.runSessionKey,
agentId: prepared.context.agentId,
channel: "cron",
source: "cron-isolated",
startedAtMs: turnStartedAtMs,
trackSessionState: true
});
lifecycle.markProcessing();
if (diagnosticsEnabled) logMessageDispatchStarted({
sessionId: prepared.context.runSessionId,
sessionKey: prepared.context.runSessionKey,
channel: "cron",
source: "cron-isolated"
});
return lifecycle;
} catch (error) {
prepared.context.sessionWorkAdmission.release();
throw error;
}
})();
let outcome = "completed";
let outcomeError;
let cronRunSessionCleanupHandled = false;
let completedPromptRuns = [];
let usage;
let usageSettlement;
const settleUsage = async (contextTokens) => {
usageSettlement ??= (async () => {
usage = applyCronRunUsage(prepared.context, completedPromptRuns);
await recordCronRunUsage({
prepared: prepared.context,
runs: completedPromptRuns,
contextTokens
});
await prepared.context.persistSessionEntry();
await prepared.context.runContinuationSession?.seal({ basePersisted: true });
})();
await usageSettlement;
return usage;
};
const lifecycle = createAgentLifecycleTerminalBackstop({
runId,
sessionKey: prepared.context.runSessionKey,
startedAt: turnStartedAtMs,
getLifecycleGeneration: () => runLifecycleGeneration,
resolveTerminationFields: (error) => resolveAgentRunErrorLifecycleFields(error, abortSignal)
});
try {
assertAgentRunLifecycleGenerationCurrent(runLifecycleGeneration);
runContextOwnerToken = claimAgentRunContext(runId, {
sessionKey: prepared.context.runSessionKey,
sessionId: initialSessionId,
agentId: prepared.context.agentId,
lifecycleGeneration: runLifecycleGeneration,
cronRunsByJobId: /* @__PURE__ */ new Map([[params.job.id, { pacingEnabled: params.job.pacing !== void 0 }]])
}, {
trackOwner: true,
ownsContext: true
});
const { executeCronRun } = await cronExecutorRuntimeLoader.load();
const executionParams = {
...prepared.context,
runId,
cfg: params.cfg,
job: params.job,
deliveryAttemptFence: params.deliveryAttemptFence,
lane: params.lane,
agentVerboseDefault: prepared.context.agentCfg?.verboseDefault,
persistRunContinuationSession: prepared.context.runContinuationSession?.sync,
setRunContinuationCliExecutionProvider: prepared.context.runContinuationSession?.setCliExecutionProvider,
abortSignal,
lifecycle,
onExecutionStarted: notifyExecutionStarted,
onExecutionPhase: notifyExecutionPhase,
onLaneWait: params.onLaneWait,
onPromptCompleted: (runs) => {
completedPromptRuns = runs;
},
abortReason,
isAborted,
immutableThinkLevel: prepared.context.thinkingSelection.immutableThinkLevel,
thinkingCatalog: prepared.context.thinkingSelection.catalog,
loadThinkingCatalog: prepared.context.thinkingSelection.loadThinkingCatalog,
executionIdentity: params.executionIdentity,
admissionSource: params.admissionSource
};
const execution = await prepared.context.sessionWorkAdmission.run(() => withAgentRunLifecycleGeneration(runLifecycleGeneration, () => executeCronRun(executionParams)));
lifecycle.emit("end", execution.runResult);
const finalized = await finalizeCronRun({
prepared: prepared.context,
execution,
abortReason,
isAborted,
settleUsage,
markCronRunSessionCleanupHandled: () => {
cronRunSessionCleanupHandled = true;
},
beforeSessionDelete: prepared.context.sessionWorkAdmission.release
});
if (finalized.status === "error") {
outcome = "error";
outcomeError = finalized.error;
}
const delayMs = consumeCronNextCheckProposal(runId, params.job.id);
return finalized.status !== "ok" || delayMs === void 0 ? finalized : {
...finalized,
nextCheck: { delayMs }
};
} catch (err) {
lifecycle.emit("error", err);
consumeCronNextCheckProposal(runId, params.job.id);
const isCronLaneTimeout = isAborted() || isCommandLaneTaskTimeoutError(err, "cron-nested");
const error = isCronLaneTimeout ? abortReason() : normalizeCronRunErrorText(err);
const errorReason = resolveCronRunErrorReason(isCronLaneTimeout ? error : err, prepared.context.liveSelection.provider);
outcome = "error";
outcomeError = error;
const admissionDisposition = err instanceof CronSessionLifecycleClaimError ? err.admissionDisposition : err instanceof CronExecutionRootRuntimeError || !executionStarted ? "rejected" : void 0;
if (completedPromptRuns.length > 0) try {
await settleUsage();
} catch (usageError) {
if (usageError !== err) logWarn(`[cron:${params.job.id}] Failed to settle completed prompt usage: ${String(usageError)}`);
}
return prepared.context.withRunSession({
status: "error",
error,
errorClassification: errorReason ? {
kind: "reason",
reason: errorReason
} : void 0,
executionStarted,
usage,
...admissionDisposition ? { admissionDisposition } : {},
provider: prepared.context.liveSelection.provider,
model: prepared.context.liveSelection.model,
diagnostics: mergeCronRunDiagnostics(prepared.context.preflightDiagnostics, createCronRunDiagnosticsFromError(isCronLaneTimeout ? "cron-setup" : "agent-run", isCronLaneTimeout ? error : err))
});
} finally {
try {
await prepared.context.runContinuationSession?.seal();
} catch (sealError) {
logWarn(`[cron:${params.job.id}] Failed to seal run continuation during cleanup: ${String(sealError)}`);
}
const finalSessionRef = {
sessionId: prepared.context.currentRunSessionId(),
sessionKey: prepared.context.runSessionKey
};
try {
if (diagnosticsEnabled) logMessageDispatchCompleted({
...finalSessionRef,
channel: "cron",
source: "cron-isolated",
durationMs: Date.now() - turnStartedAtMs,
outcome,
error: outcomeError
});
messageLifecycle.markIdle(void 0, finalSessionRef);
messageLifecycle.markProcessed(outcome, {
...finalSessionRef,
error: outcomeError
});
} finally {
try {
if (!cronRunSessionCleanupHandled) await cleanupCronRunSessionAfterRun({
job: params.job,
agentSessionKey: prepared.context.agentSessionKey,
sessionId: prepared.context.currentRunSessionId(),
lifecycleRevision: prepared.context.cronSession.lifecycleRevision,
sessionUpdatedAt: prepared.context.cronSession.sessionEntry.updatedAt,
beforeDelete: prepared.context.sessionWorkAdmission.release,
reason: "cron-delete-after-run-finally"
});
} finally {
try {
try {
await disposeCronRunContext({
runId,
sessionId: initialSessionId,
cronSession: prepared.context.cronSession,
ownsSessionRuntime,
runContextOwnerToken
});
} finally {
prepared.context.sessionWorkAdmission.release();
}
if (prepared.context.runContinuationSession) try {
await removeCronRunContinuationSessionIfIdle(prepared.context.runSessionKey);
} catch (error) {
logWarn(`[cron:${params.job.id}] Failed to remove unused run continuation: ${String(error)}`);
}
} finally {
if (prepared.context.runSessionKey !== prepared.context.agentSessionKey) await cleanupBrowserSessionsForLifecycleEnd({
cfg: prepared.context.cfgWithAgentDefaults,
sessionKeys: [prepared.context.runSessionKey],
onWarn: (message) => logWarn(`[cron:${params.job.id}] ${message}`)
});
}
}
}
}
}), () => leaseActive ? preparedRuntimeLease.snapshot : void 0);
} finally {
leaseActive = false;
}
} catch (_) {
_usingCtx$1.e = _;
} finally {
await _usingCtx$1.d();
}
}
//#endregion
export { runCronIsolatedAgentTurn as t };