import { c as normalizeOptionalString } from "./string-coerce-Bje8XVt9.js";
import { t as createLazyImportLoader } from "./lazy-promise-AiZRy56y.js";
import { c as normalizeAgentId, h as toAgentStoreSessionKey } from "./session-key-DWZ1NbQI.js";
import { a as resolveAgentDir, c as resolveDefaultAgentId, o as resolveAgentWorkspaceDir, r as resolveAgentConfig } from "./agent-scope-config-DkpPMLgw.js";
import { t as matchesSkillFilter } from "./filter-mBLiceoD.js";
import { a as logWarn } from "./logger-Bj-Bqkac.js";
import { n as DEFAULT_MODEL, r as DEFAULT_PROVIDER } from "./defaults-DX7iFzkA.js";
import { t as hasAnyAuthProfileStoreSource } from "./source-check-vgrIv1gH.js";
import { t as canonicalizeMainSessionAlias } from "./main-session-BrJBjqz5.js";
import { o as resolveSessionTranscriptPath, u as resolveStorePath } from "./paths-pnYRMSxI.js";
import { h as stringifyRouteThreadId } from "./channel-route-DfV40gQP.js";
import { u as setSessionRuntimeModel } from "./types-BnpwgCKC.js";
import { c as normalizeModelSelection, f as resolveConfiguredModelRef, m as resolveHooksGmailModel } from "./model-selection-shared-CWuhAIsC.js";
import { f as normalizeThinkLevel, n as isThinkingLevelSupported, o as resolveSupportedThinkingLevel } from "./thinking-4Rs8NBoQ.js";
import { i as resolveAllowedModelRef, r as getModelRefStatus, t as isCliProvider } from "./model-selection-cli-SXVRGBF2.js";
import { p as resolveThinkingDefault } from "./model-selection-DVRX-V8s.js";
import { r as loadModelCatalog } from "./model-catalog-BUkIb8c6.js";
import { t as resolveAgentTimeoutMs } from "./timeout-CABcPS9d.js";
import { n as createCronRunDiagnosticsFromError, r as mergeCronRunDiagnostics, t as createCronRunDiagnosticsFromAgentResult } from "./run-diagnostics-jMtu_tdA.js";
import { l as ensureAgentWorkspace } from "./workspace-DtsXm2Tw.js";
import { i as hasNonzeroUsage, r as deriveSessionTotalTokens } from "./usage-BYaWKS-q.js";
import { c as retireSessionMcpRuntime } from "./pi-bundle-mcp-runtime-DnYNq3sc.js";
import "./pi-bundle-mcp-tools-BvAqtvcS.js";
import { t as drainOutputFilesForRun } from "./output-file-run-collector-DcVtud11.js";
import { n as mapHookExternalContentSource, r as resolveHookExternalContentSource, t as isExternalHookSession } from "./external-content-source-DNv_eHW4.js";
import { n as resolveCronStyleNow } from "./current-time-tFPj7Ipc.js";
import { t as expandBundleCommandPromptTemplate } from "./get-reply-inline-actions-CShmRNFl.js";
import { n as resolveSkillCommandInvocation } from "./skill-commands-base-CmWUUoZl.js";
import { n as listSkillCommandsForWorkspace } from "./skill-commands-DiJQwnfx.js";
import { t as resolveCronDeliveryPlan } from "./delivery-plan-CoRprMAf.js";
import { a as resolveCronChannelOutputPolicy, n as markCronSessionPreRun, r as persistCronSkillsSnapshotIfChanged, t as createPersistCronSessionEntry } from "./run-session-state-BYXcE9N8.js";
import { a as resolveHeartbeatAckMaxChars, i as resolveCronPayloadOutcome, t as isHeartbeatOnlyResponse } from "./helpers-BazD02_M.js";
import { t as resolveCronSession } from "./session-CeruMjz6.js";
//#region src/cron/isolated-agent/model-selection.ts
function formatCronPayloadModelRejection(modelOverride, error) {
	if (error.startsWith("model not allowed:")) return `cron payload.model '${modelOverride}' rejected by agents.defaults.models allowlist: ${error.slice(18).trim()}`;
	return `cron payload.model '${modelOverride}' rejected: ${error}`;
}
async function resolveCronModelSelection(params) {
	const resolvedDefault = resolveConfiguredModelRef({
		cfg: params.cfgWithAgentDefaults,
		defaultProvider: DEFAULT_PROVIDER,
		defaultModel: DEFAULT_MODEL
	});
	let provider = resolvedDefault.provider;
	let model = resolvedDefault.model;
	let catalog;
	const loadCatalogOnce = async () => {
		if (!catalog) catalog = await loadModelCatalog({ config: params.cfgWithAgentDefaults });
		return catalog;
	};
	const subagentModelRaw = normalizeModelSelection(params.agentConfigOverride?.subagents?.model) ?? normalizeModelSelection(params.agentConfigOverride?.model) ?? normalizeModelSelection(params.cfg.agents?.defaults?.subagents?.model);
	if (subagentModelRaw) {
		const resolvedSubagent = resolveAllowedModelRef({
			cfg: params.cfgWithAgentDefaults,
			catalog: await loadCatalogOnce(),
			raw: subagentModelRaw,
			defaultProvider: resolvedDefault.provider,
			defaultModel: resolvedDefault.model
		});
		if (!("error" in resolvedSubagent)) {
			provider = resolvedSubagent.ref.provider;
			model = resolvedSubagent.ref.model;
		}
	}
	let hooksGmailModelApplied = false;
	const hooksGmailModelRef = params.isGmailHook ? resolveHooksGmailModel({
		cfg: params.cfg,
		defaultProvider: DEFAULT_PROVIDER
	}) : null;
	if (hooksGmailModelRef) {
		if (getModelRefStatus({
			cfg: params.cfg,
			catalog: await loadCatalogOnce(),
			ref: hooksGmailModelRef,
			defaultProvider: resolvedDefault.provider,
			defaultModel: resolvedDefault.model
		}).allowed) {
			provider = hooksGmailModelRef.provider;
			model = hooksGmailModelRef.model;
			hooksGmailModelApplied = true;
		}
	}
	const modelOverrideRaw = params.payload.kind === "agentTurn" ? params.payload.model : void 0;
	const modelOverride = typeof modelOverrideRaw === "string" ? modelOverrideRaw.trim() : void 0;
	if (modelOverride !== void 0 && modelOverride.length > 0) {
		const resolvedOverride = resolveAllowedModelRef({
			cfg: params.cfgWithAgentDefaults,
			catalog: await loadCatalogOnce(),
			raw: modelOverride,
			defaultProvider: resolvedDefault.provider,
			defaultModel: resolvedDefault.model
		});
		if ("error" in resolvedOverride) return {
			ok: false,
			error: formatCronPayloadModelRejection(modelOverride, resolvedOverride.error)
		};
		provider = resolvedOverride.ref.provider;
		model = resolvedOverride.ref.model;
	}
	if (!modelOverride && !hooksGmailModelApplied) {
		const sessionModelOverride = params.sessionEntry.modelOverride?.trim();
		if (sessionModelOverride) {
			const sessionProviderOverride = params.sessionEntry.providerOverride?.trim() || resolvedDefault.provider;
			const resolvedSessionOverride = resolveAllowedModelRef({
				cfg: params.cfgWithAgentDefaults,
				catalog: await loadCatalogOnce(),
				raw: `${sessionProviderOverride}/${sessionModelOverride}`,
				defaultProvider: resolvedDefault.provider,
				defaultModel: resolvedDefault.model
			});
			if (!("error" in resolvedSessionOverride)) {
				provider = resolvedSessionOverride.ref.provider;
				model = resolvedSessionOverride.ref.model;
			}
		}
	}
	return {
		ok: true,
		provider,
		model
	};
}
//#endregion
//#region src/cron/isolated-agent/run-config.ts
function extractCronAgentDefaultsOverride(agentConfigOverride) {
	const { model: overrideModel, sandbox: _agentSandboxOverride, ...agentOverrideRest } = agentConfigOverride ?? {};
	return {
		overrideModel,
		definedOverrides: Object.fromEntries(Object.entries(agentOverrideRest).filter(([, value]) => value !== void 0))
	};
}
function mergeCronAgentModelOverride(params) {
	const nextDefaults = { ...params.defaults };
	const existingModel = nextDefaults.model && typeof nextDefaults.model === "object" ? nextDefaults.model : {};
	if (typeof params.overrideModel === "string") nextDefaults.model = {
		...existingModel,
		primary: params.overrideModel
	};
	else if (params.overrideModel) nextDefaults.model = {
		...existingModel,
		...params.overrideModel
	};
	return nextDefaults;
}
function buildCronAgentDefaultsConfig(params) {
	const { overrideModel, definedOverrides } = extractCronAgentDefaultsOverride(params.agentConfigOverride);
	return mergeCronAgentModelOverride({
		defaults: Object.assign({}, params.defaults, definedOverrides),
		overrideModel
	});
}
//#endregion
//#region src/cron/isolated-agent/session-key.ts
function resolveCronAgentSessionKey(params) {
	const raw = toAgentStoreSessionKey({
		agentId: params.agentId,
		requestKey: params.sessionKey.trim(),
		mainKey: params.mainKey
	});
	return canonicalizeMainSessionAlias({
		cfg: params.cfg,
		agentId: params.agentId,
		sessionKey: raw
	});
}
//#endregion
//#region src/cron/isolated-agent/skills-snapshot.ts
const skillsSnapshotRuntimeLoader = createLazyImportLoader(() => import("./skills-snapshot.runtime.js"));
async function loadSkillsSnapshotRuntime() {
	return await skillsSnapshotRuntimeLoader.load();
}
async function resolveCronSkillsSnapshot(params) {
	if (params.isFastTestEnv) return params.existingSnapshot ?? {
		prompt: "",
		skills: []
	};
	const runtime = await loadSkillsSnapshotRuntime();
	const snapshotVersion = runtime.getSkillsSnapshotVersion(params.workspaceDir);
	const skillFilter = runtime.resolveAgentSkillsFilter(params.config, params.agentId);
	const existingSnapshot = params.existingSnapshot;
	if (!(!existingSnapshot || existingSnapshot.version !== snapshotVersion || !matchesSkillFilter(existingSnapshot.skillFilter, skillFilter))) return existingSnapshot;
	return runtime.buildWorkspaceSkillSnapshot(params.workspaceDir, {
		config: params.config,
		agentId: params.agentId,
		skillFilter,
		eligibility: { remote: runtime.getRemoteSkillEligibility({ advertiseExecNode: runtime.canExecRequestNode({
			cfg: params.config,
			agentId: params.agentId
		}) }) },
		snapshotVersion
	});
}
//#endregion
//#region src/cron/isolated-agent/skill-command.ts
/**
* 识别 cron payload.message 开头的 /skillname args,改写成技能 prompt。
* 与 chat 的 get-reply-inline-actions 同款规则:有 promptTemplate 用之,
* 否则用默认 `Use the "<skillName>" skill ...`。未匹配则原样返回。
*/
async function expandCronSkillCommand(params) {
	const trimmed = params.message.trim();
	if (!trimmed.startsWith("/")) return { message: params.message };
	const skillCommands = listSkillCommandsForWorkspace({
		workspaceDir: params.workspaceDir,
		cfg: params.cfg,
		agentId: params.agentId
	});
	if (skillCommands.length === 0) return { message: params.message };
	const invocation = resolveSkillCommandInvocation({
		commandBodyNormalized: trimmed,
		skillCommands
	});
	if (!invocation) {
		logWarn(`[cron] skill command not matched, running as plain text: ${trimmed.slice(0, 40)}`);
		return { message: params.message };
	}
	return {
		message: invocation.command.promptTemplate ? expandBundleCommandPromptTemplate(invocation.command.promptTemplate, invocation.args) : [`Use the "${invocation.command.skillName}" skill for this request.`, invocation.args ? `User input:\n${invocation.args}` : null].filter((entry) => Boolean(entry)).join("\n\n"),
		matchedSkill: invocation.command.name
	};
}
//#endregion
//#region src/cron/isolated-agent/run.ts
const sessionStoreRuntimeLoader = createLazyImportLoader(() => import("./store.runtime.js"));
const cronExecutorRuntimeLoader = createLazyImportLoader(() => import("./run-executor.runtime.js"));
const cronExternalContentRuntimeLoader = createLazyImportLoader(() => import("./run-external-content.runtime.js"));
const cronAuthProfileRuntimeLoader = createLazyImportLoader(() => import("./run-auth-profile.runtime.js"));
const cronContextRuntimeLoader = createLazyImportLoader(() => import("./run-context.runtime.js"));
const cronModelCatalogRuntimeLoader = createLazyImportLoader(() => import("./run-model-catalog.runtime.js"));
const cronDeliveryRuntimeLoader = createLazyImportLoader(() => import("./run-delivery.runtime.js"));
const cronModelPreflightRuntimeLoader = createLazyImportLoader(() => import("./model-preflight.runtime.js"));
const cronTranscriptRuntimeLoader = createLazyImportLoader(() => import("./transcript.runtime.js"));
async function loadSessionStoreRuntime() {
	return await sessionStoreRuntimeLoader.load();
}
async function loadCronExecutorRuntime() {
	return await cronExecutorRuntimeLoader.load();
}
async function loadCronExternalContentRuntime() {
	return await cronExternalContentRuntimeLoader.load();
}
async function loadCronAuthProfileRuntime() {
	return await cronAuthProfileRuntimeLoader.load();
}
async function loadCronContextRuntime() {
	return await cronContextRuntimeLoader.load();
}
async function loadCronModelCatalogRuntime() {
	return await cronModelCatalogRuntimeLoader.load();
}
async function loadCronDeliveryRuntime() {
	return await cronDeliveryRuntimeLoader.load();
}
async function loadCronModelPreflightRuntime() {
	return await cronModelPreflightRuntimeLoader.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);
}
function resolveNonNegativeNumber(value) {
	return typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : void 0;
}
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 normalizeCronTraceTarget(target) {
	if (!target) return;
	return {
		...target.channel ? { channel: target.channel } : {},
		...target.to !== void 0 ? { to: target.to } : {},
		...target.accountId ? { accountId: target.accountId } : {},
		...target.threadId !== void 0 ? { threadId: target.threadId } : {},
		...target.source ? { source: target.source } : {}
	};
}
function normalizeMessagingToolTarget(target, resolvedDelivery, matchesMessagingToolDeliveryTarget) {
	const channel = target.provider?.trim();
	if (!channel) return;
	return {
		channel: channel === "message" && resolvedDelivery.ok && matchesMessagingToolDeliveryTarget(target, {
			channel: resolvedDelivery.channel,
			to: resolvedDelivery.to,
			accountId: resolvedDelivery.accountId
		}) ? resolvedDelivery.channel : channel,
		...target.to ? { to: target.to } : {},
		...target.accountId ? { accountId: target.accountId } : {},
		...target.threadId ? { threadId: target.threadId } : {}
	};
}
function buildResolvedCronTraceTarget(resolvedDelivery) {
	if (resolvedDelivery.ok) return {
		ok: true,
		...normalizeCronTraceTarget({
			channel: resolvedDelivery.channel,
			to: resolvedDelivery.to,
			accountId: resolvedDelivery.accountId,
			threadId: resolvedDelivery.threadId,
			source: resolvedDelivery.mode === "implicit" ? "last" : "explicit"
		})
	};
	return {
		ok: false,
		...normalizeCronTraceTarget({
			channel: resolvedDelivery.channel,
			to: resolvedDelivery.to ?? null,
			accountId: resolvedDelivery.accountId,
			threadId: resolvedDelivery.threadId,
			source: resolvedDelivery.mode === "implicit" ? "last" : "explicit"
		}),
		error: resolvedDelivery.error.message
	};
}
function buildCronDeliveryTrace(params) {
	const intended = normalizeCronTraceTarget({
		channel: params.deliveryPlan.channel ?? "last",
		to: params.deliveryPlan.to ?? null,
		accountId: params.deliveryPlan.accountId,
		threadId: params.deliveryPlan.threadId,
		source: params.deliveryPlan.channel === "last" || !params.deliveryPlan.channel ? "last" : "explicit"
	});
	const resolved = params.deliveryPlan.mode !== "none" || hasExplicitCronDeliveryTarget(params.deliveryPlan) ? buildResolvedCronTraceTarget(params.resolvedDelivery) : void 0;
	const messageToolSentTo = params.messagingToolSentTargets.map((target) => normalizeMessagingToolTarget(target, params.resolvedDelivery, params.matchesMessagingToolDeliveryTarget)).filter((target) => Boolean(target));
	return {
		...intended ? { intended } : {},
		...resolved ? { resolved } : {},
		...messageToolSentTo.length > 0 ? { messageToolSentTo } : {},
		fallbackUsed: params.fallbackUsed,
		delivered: params.delivered
	};
}
function resolveMessagingToolSentTargets(params) {
	const explicitTargets = params.runResult.messagingToolSentTargets ?? [];
	if (explicitTargets.length > 0 || params.runResult.didSendViaMessagingTool !== true) return explicitTargets;
	if (!params.resolvedDelivery.ok) return [];
	const threadId = stringifyRouteThreadId(params.resolvedDelivery.threadId);
	return [{
		tool: "message",
		provider: params.resolvedDelivery.channel,
		...params.resolvedDelivery.accountId ? { accountId: params.resolvedDelivery.accountId } : {},
		...params.resolvedDelivery.to ? { to: params.resolvedDelivery.to } : {},
		...threadId ? { threadId } : {}
	}];
}
function resolveCronToolPolicy(params) {
	const enableMessageTool = params.deliveryMode !== "webhook";
	return {
		requireExplicitMessageTarget: false,
		disableMessageTool: !enableMessageTool,
		forceMessageTool: enableMessageTool
	};
}
function canPromptForMessageTool(params) {
	if (params.disableMessageTool) return false;
	return !params.toolsAllow?.length || params.toolsAllow.includes("message");
}
function hasExplicitCronDeliveryTarget(plan) {
	return Boolean(plan.channel && plan.channel !== "last" || plan.to || plan.threadId || plan.accountId);
}
async function resolveCronDeliveryContext(params) {
	const deliveryPlan = resolveCronDeliveryPlan(params.job);
	if (deliveryPlan.mode === "webhook") {
		const resolvedDelivery = {
			ok: false,
			channel: void 0,
			to: void 0,
			accountId: void 0,
			threadId: void 0,
			mode: "implicit",
			error: /* @__PURE__ */ new Error("webhook delivery has no chat target")
		};
		return {
			deliveryPlan,
			deliveryRequested: deliveryPlan.requested,
			resolvedDelivery,
			toolPolicy: resolveCronToolPolicy({ deliveryMode: deliveryPlan.mode })
		};
	}
	if (deliveryPlan.mode === "none" && !hasExplicitCronDeliveryTarget(deliveryPlan)) return {
		deliveryPlan,
		deliveryRequested: false,
		resolvedDelivery: {
			ok: false,
			channel: void 0,
			to: void 0,
			accountId: void 0,
			threadId: void 0,
			mode: "implicit",
			error: /* @__PURE__ */ new Error("delivery is disabled")
		},
		toolPolicy: resolveCronToolPolicy({ deliveryMode: deliveryPlan.mode })
	};
	const { resolveDeliveryTarget } = await loadCronDeliveryRuntime();
	const resolvedDelivery = await resolveDeliveryTarget(params.cfg, params.agentId, {
		channel: deliveryPlan.channel ?? "last",
		to: deliveryPlan.to,
		threadId: deliveryPlan.threadId,
		accountId: deliveryPlan.accountId,
		sessionKey: params.job.sessionKey
	});
	return {
		deliveryPlan,
		deliveryRequested: deliveryPlan.requested,
		resolvedDelivery,
		toolPolicy: resolveCronToolPolicy({ deliveryMode: deliveryPlan.mode })
	};
}
function appendCronDeliveryInstruction(params) {
	if (!params.deliveryRequested) return params.commandBody;
	if (params.messageToolEnabled) {
		const targetHint = params.resolvedDeliveryOk ? "for the current chat" : "with an explicit target";
		return `${params.commandBody}\n\nUse the message tool if you need to notify the user directly ${targetHint}. If you do not send directly, your final plain-text reply will be delivered automatically.`.trim();
	}
	return `${params.commandBody}\n\nReturn your response as plain text; it will be delivered automatically. If the task explicitly calls for messaging a specific external recipient, note who/where it should go instead of sending it yourself.`.trim();
}
function resolvePositiveContextTokens(value) {
	return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : void 0;
}
async function loadCliRunnerRuntime() {
	return await import("./cli-runner.runtime-CwsKnfLB.js");
}
async function loadUsageFormatRuntime() {
	return await import("./usage-format-DFPZ-FEm.js");
}
async function prepareCronRunContext(params) {
	const { input } = params;
	const defaultAgentId = resolveDefaultAgentId(input.cfg);
	const requestedAgentId = typeof input.agentId === "string" && input.agentId.trim() ? input.agentId : typeof input.job.agentId === "string" && input.job.agentId.trim() ? input.job.agentId : void 0;
	const normalizedRequested = requestedAgentId ? normalizeAgentId(requestedAgentId) : void 0;
	const agentConfigOverride = normalizedRequested ? resolveAgentConfig(input.cfg, normalizedRequested) : void 0;
	const agentId = normalizedRequested ?? defaultAgentId;
	const agentCfg = buildCronAgentDefaultsConfig({
		defaults: input.cfg.agents?.defaults,
		agentConfigOverride
	});
	const cfgWithAgentDefaults = {
		...input.cfg,
		agents: Object.assign({}, input.cfg.agents, { defaults: agentCfg })
	};
	let catalog;
	const loadCatalog = async () => {
		if (!catalog) catalog = await (await loadCronModelCatalogRuntime()).loadModelCatalog({ config: cfgWithAgentDefaults });
		return catalog;
	};
	const baseSessionKey = (input.sessionKey?.trim() || `cron:${input.job.id}`).trim();
	const agentSessionKey = resolveCronAgentSessionKey({
		sessionKey: baseSessionKey,
		agentId,
		mainKey: input.cfg.session?.mainKey,
		cfg: input.cfg
	});
	const hookExternalContentSource = (input.job.payload.kind === "agentTurn" ? input.job.payload.externalContentSource : void 0) ?? resolveHookExternalContentSource(baseSessionKey);
	const workspaceDirRaw = resolveAgentWorkspaceDir(input.cfg, agentId);
	const agentDir = resolveAgentDir(input.cfg, agentId);
	const workspaceDir = (await ensureAgentWorkspace({
		dir: workspaceDirRaw,
		ensureBootstrapFiles: !agentCfg?.skipBootstrap && !params.isFastTestEnv,
		skipOptionalBootstrapFiles: agentCfg?.skipOptionalBootstrapFiles
	})).dir;
	const isGmailHook = hookExternalContentSource === "gmail";
	const now = Date.now();
	const cronSession = resolveCronSession({
		cfg: input.cfg,
		sessionKey: agentSessionKey,
		agentId,
		nowMs: now,
		forceNew: input.job.sessionTarget === "isolated"
	});
	const runSessionId = cronSession.sessionEntry.sessionId;
	if (!cronSession.sessionEntry.sessionFile?.trim()) cronSession.sessionEntry.sessionFile = resolveSessionTranscriptPath(runSessionId, agentId);
	const runSessionKey = baseSessionKey.startsWith("cron:") ? `${agentSessionKey}:run:${runSessionId}` : agentSessionKey;
	const persistSessionEntry = createPersistCronSessionEntry({
		isFastTestEnv: params.isFastTestEnv,
		cronSession,
		agentSessionKey,
		updateSessionStore: async (storePath, update) => {
			const { updateSessionStore } = await loadSessionStoreRuntime();
			await updateSessionStore(storePath, update);
		}
	});
	const withRunSession = (result) => ({
		...result,
		sessionId: runSessionId,
		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 = labelSuffix;
	}
	const resolvedModelSelection = await resolveCronModelSelection({
		cfg: input.cfg,
		cfgWithAgentDefaults,
		agentConfigOverride,
		sessionEntry: cronSession.sessionEntry,
		payload: input.job.payload,
		isGmailHook,
		agentId
	});
	if (!resolvedModelSelection.ok) return {
		ok: false,
		agentId,
		result: withRunSession({
			status: "error",
			error: resolvedModelSelection.error,
			diagnostics: createCronRunDiagnosticsFromError("cron-preflight", resolvedModelSelection.error)
		})
	};
	let provider = resolvedModelSelection.provider;
	let model = resolvedModelSelection.model;
	const preflight = await (await loadCronModelPreflightRuntime()).preflightCronModelProvider({
		cfg: cfgWithAgentDefaults,
		provider,
		model
	});
	if (preflight.status === "unavailable") {
		logWarn(`[cron:${input.job.id}] ${preflight.reason}`);
		return {
			ok: false,
			agentId,
			result: withRunSession({
				status: "skipped",
				error: preflight.reason,
				diagnostics: createCronRunDiagnosticsFromError("model-preflight", preflight.reason, { severity: "warn" }),
				provider,
				model
			})
		};
	}
	const hooksGmailThinking = isGmailHook ? normalizeThinkLevel(input.cfg.hooks?.gmail?.thinking) : void 0;
	let thinkLevel = normalizeThinkLevel((input.job.payload.kind === "agentTurn" ? input.job.payload.thinking : void 0) ?? void 0) ?? hooksGmailThinking;
	if (!thinkLevel) thinkLevel = resolveThinkingDefault({
		cfg: cfgWithAgentDefaults,
		provider,
		model,
		catalog: await loadCatalog()
	});
	const thinkingCatalog = await loadCatalog();
	if (!isThinkingLevelSupported({
		provider,
		model,
		level: thinkLevel,
		catalog: thinkingCatalog
	})) {
		const fallbackThinkLevel = resolveSupportedThinkingLevel({
			provider,
			model,
			level: thinkLevel,
			catalog: thinkingCatalog
		});
		if (fallbackThinkLevel !== thinkLevel) {
			logWarn(`[cron:${input.job.id}] Thinking level "${thinkLevel}" is not supported for ${provider}/${model}; downgrading to "${fallbackThinkLevel}".`);
			thinkLevel = fallbackThinkLevel;
		}
	}
	const timeoutMs = resolveAgentTimeoutMs({
		cfg: cfgWithAgentDefaults,
		overrideSeconds: input.job.payload.kind === "agentTurn" ? input.job.payload.timeoutSeconds : void 0
	});
	const agentPayload = input.job.payload.kind === "agentTurn" ? input.job.payload : null;
	const { deliveryPlan, deliveryRequested, resolvedDelivery, toolPolicy } = await resolveCronDeliveryContext({
		cfg: cfgWithAgentDefaults,
		job: input.job,
		agentId
	});
	const { formattedTime, timeLine } = resolveCronStyleNow(input.cfg, now);
	const expanded = await expandCronSkillCommand({
		message: input.message,
		workspaceDir,
		cfg: cfgWithAgentDefaults,
		agentId
	});
	const base = `[cron:${input.job.id} ${input.job.name}] ${expanded.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(input.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();
		const hookType = mapHookExternalContentSource(hookExternalContentSource ?? "webhook");
		commandBody = `${buildSafeExternalPrompt({
			content: input.message,
			source: hookType,
			jobName: input.job.name,
			jobId: input.job.id,
			timestamp: formattedTime
		})}\n\n${timeLine}`.trim();
	} else commandBody = `${base}\n${timeLine}`.trim();
	commandBody = appendCronDeliveryInstruction({
		commandBody,
		deliveryRequested,
		messageToolEnabled: canPromptForMessageTool({
			disableMessageTool: toolPolicy.disableMessageTool,
			toolsAllow: agentPayload?.toolsAllow
		}),
		resolvedDeliveryOk: resolvedDelivery.ok
	});
	const skillsSnapshot = await resolveCronSkillsSnapshot({
		workspaceDir,
		config: cfgWithAgentDefaults,
		agentId,
		existingSnapshot: cronSession.sessionEntry.skillsSnapshot,
		isFastTestEnv: params.isFastTestEnv
	});
	await persistCronSkillsSnapshotIfChanged({
		isFastTestEnv: params.isFastTestEnv,
		cronSession,
		skillsSnapshot,
		nowMs: Date.now(),
		persistSessionEntry
	});
	markCronSessionPreRun({
		entry: cronSession.sessionEntry,
		provider,
		model
	});
	try {
		await persistSessionEntry();
	} catch (err) {
		logWarn(`[cron:${input.job.id}] Failed to persist pre-run session entry: ${String(err)}`);
	}
	await retireRolledCronSessionMcpRuntime({
		job: input.job,
		cronSession
	});
	const authProfileId = !Boolean(cronSession.sessionEntry.authProfileOverride?.trim()) && !hasConfiguredAuthProfiles(cfgWithAgentDefaults) && !hasAnyAuthProfileStoreSource(agentDir) ? void 0 : await (await loadCronAuthProfileRuntime()).resolveSessionAuthProfileOverride({
		cfg: cfgWithAgentDefaults,
		provider,
		agentDir,
		sessionEntry: cronSession.sessionEntry,
		sessionStore: cronSession.store,
		sessionKey: agentSessionKey,
		storePath: cronSession.storePath,
		isNewSession: cronSession.isNewSession && input.job.sessionTarget !== "isolated"
	});
	const liveSelection = {
		provider,
		model,
		authProfileId,
		authProfileIdSource: authProfileId ? cronSession.sessionEntry.authProfileOverrideSource : void 0
	};
	return {
		ok: true,
		context: {
			input,
			cfgWithAgentDefaults,
			agentId,
			agentCfg,
			agentDir,
			agentSessionKey,
			runSessionId,
			runSessionKey,
			workspaceDir,
			commandBody,
			cronSession,
			persistSessionEntry,
			withRunSession,
			agentPayload,
			deliveryPlan,
			resolvedDelivery,
			deliveryRequested,
			suppressExecNotifyOnExit: deliveryPlan.mode === "none",
			toolPolicy,
			skillsSnapshot,
			liveSelection,
			thinkLevel,
			timeoutMs
		}
	};
}
async function finalizeCronRun(params) {
	const { prepared, execution } = params;
	const finalRunResult = execution.runResult;
	const payloads = finalRunResult.payloads ?? [];
	let telemetry;
	if (finalRunResult.meta?.systemPromptReport) prepared.cronSession.sessionEntry.systemPromptReport = finalRunResult.meta.systemPromptReport;
	const usage = finalRunResult.meta?.agentMeta?.usage;
	const promptTokens = finalRunResult.meta?.agentMeta?.promptTokens;
	const modelUsed = finalRunResult.meta?.agentMeta?.model ?? execution.fallbackModel ?? execution.liveSelection.model;
	const providerUsed = finalRunResult.meta?.agentMeta?.provider ?? execution.fallbackProvider ?? execution.liveSelection.provider;
	const contextTokens = resolvePositiveContextTokens(prepared.agentCfg?.contextTokens) ?? (await loadCronContextRuntime()).lookupContextTokens(modelUsed, { allowAsyncLoad: false }) ?? resolvePositiveContextTokens(prepared.cronSession.sessionEntry.contextTokens) ?? 2e5;
	setSessionRuntimeModel(prepared.cronSession.sessionEntry, {
		provider: providerUsed,
		model: modelUsed
	});
	prepared.cronSession.sessionEntry.contextTokens = contextTokens;
	if (isCliProvider(providerUsed, prepared.cfgWithAgentDefaults)) {
		const cliSessionId = finalRunResult.meta?.agentMeta?.sessionId?.trim();
		if (cliSessionId) {
			const { setCliSessionId } = await loadCliRunnerRuntime();
			setCliSessionId(prepared.cronSession.sessionEntry, providerUsed, cliSessionId);
		}
	}
	if (hasNonzeroUsage(usage)) {
		const { estimateUsageCost, resolveModelCostConfig } = await loadUsageFormatRuntime();
		const input = usage.input ?? 0;
		const output = usage.output ?? 0;
		const totalTokens = deriveSessionTotalTokens({
			usage,
			contextTokens,
			promptTokens
		});
		const runEstimatedCostUsd = resolveNonNegativeNumber(estimateUsageCost({
			usage,
			cost: resolveModelCostConfig({
				provider: providerUsed,
				model: modelUsed,
				config: prepared.cfgWithAgentDefaults
			})
		}));
		prepared.cronSession.sessionEntry.inputTokens = input;
		prepared.cronSession.sessionEntry.outputTokens = output;
		const telemetryUsage = {
			input_tokens: input,
			output_tokens: output
		};
		if (typeof totalTokens === "number" && Number.isFinite(totalTokens) && totalTokens > 0) {
			prepared.cronSession.sessionEntry.totalTokens = totalTokens;
			prepared.cronSession.sessionEntry.totalTokensFresh = true;
			telemetryUsage.total_tokens = totalTokens;
		} else {
			prepared.cronSession.sessionEntry.totalTokens = void 0;
			prepared.cronSession.sessionEntry.totalTokensFresh = false;
		}
		prepared.cronSession.sessionEntry.cacheRead = usage.cacheRead ?? 0;
		prepared.cronSession.sessionEntry.cacheWrite = usage.cacheWrite ?? 0;
		if (runEstimatedCostUsd !== void 0) prepared.cronSession.sessionEntry.estimatedCostUsd = runEstimatedCostUsd;
		telemetry = {
			model: modelUsed,
			provider: providerUsed,
			usage: telemetryUsage
		};
	} else telemetry = {
		model: modelUsed,
		provider: providerUsed
	};
	await prepared.persistSessionEntry();
	if (params.isAborted()) return prepared.withRunSession({
		status: "error",
		error: params.abortReason(),
		diagnostics: mergeCronRunDiagnostics(createCronRunDiagnosticsFromAgentResult(finalRunResult, { finalStatus: "error" }), createCronRunDiagnosticsFromError("cron-setup", params.abortReason())),
		...telemetry
	});
	let { summary, outputText, synthesizedText, deliveryPayloads, deliveryPayloadHasStructuredContent, hasFatalErrorPayload, embeddedRunError, pendingPresentationWarningError } = resolveCronPayloadOutcome({
		payloads,
		runLevelError: finalRunResult.meta?.error,
		failureSignal: finalRunResult.meta?.failureSignal,
		finalAssistantVisibleText: finalRunResult.meta?.finalAssistantVisibleText,
		preferFinalAssistantVisibleText: (await resolveCronChannelOutputPolicy(prepared.resolvedDelivery.channel)).preferFinalAssistantVisibleText
	});
	const agentDiagnostics = createCronRunDiagnosticsFromAgentResult(finalRunResult, { finalStatus: hasFatalErrorPayload ? "error" : "ok" });
	const resolveRunOutcome = (result) => prepared.withRunSession({
		status: hasFatalErrorPayload ? "error" : "ok",
		...hasFatalErrorPayload ? { error: embeddedRunError ?? "cron isolated run returned an error payload" } : {},
		summary,
		outputText,
		delivered: result?.delivered,
		deliveryAttempted: result?.deliveryAttempted,
		delivery: result?.delivery,
		diagnostics: hasFatalErrorPayload ? mergeCronRunDiagnostics(agentDiagnostics, createCronRunDiagnosticsFromError("agent-run", embeddedRunError ?? "cron isolated run returned an error payload")) : agentDiagnostics,
		...telemetry
	});
	const failPendingPresentationWarningUnlessDelivered = (delivered) => {
		if (pendingPresentationWarningError && delivered !== true) {
			hasFatalErrorPayload = true;
			embeddedRunError = pendingPresentationWarningError;
		}
	};
	const skipHeartbeatDelivery = prepared.deliveryRequested && !hasFatalErrorPayload && isHeartbeatOnlyResponse(deliveryPayloads, resolveHeartbeatAckMaxChars(prepared.agentCfg));
	const { dispatchCronDelivery, matchesMessagingToolDeliveryTarget, resolveCronDeliveryBestEffort } = await loadCronDeliveryRuntime();
	const messagingToolSentTargets = resolveMessagingToolSentTargets({
		resolvedDelivery: prepared.resolvedDelivery,
		runResult: finalRunResult
	});
	const didSendViaMessagingTool = finalRunResult.didSendViaMessagingTool === true && messagingToolSentTargets.length > 0;
	const skipMessagingToolDelivery = didSendViaMessagingTool && prepared.resolvedDelivery.ok && messagingToolSentTargets.some((target) => matchesMessagingToolDeliveryTarget(target, {
		channel: prepared.resolvedDelivery.channel,
		to: prepared.resolvedDelivery.to,
		accountId: prepared.resolvedDelivery.accountId
	}));
	const deliveryResult = await dispatchCronDelivery({
		cfg: prepared.input.cfg,
		cfgWithAgentDefaults: prepared.cfgWithAgentDefaults,
		deps: prepared.input.deps,
		job: prepared.input.job,
		agentId: prepared.agentId,
		agentSessionKey: prepared.agentSessionKey,
		runSessionKey: prepared.runSessionKey,
		sessionId: prepared.runSessionId,
		runStartedAt: execution.runStartedAt,
		runEndedAt: execution.runEndedAt,
		timeoutMs: prepared.timeoutMs,
		resolvedDelivery: prepared.resolvedDelivery,
		deliveryRequested: prepared.deliveryRequested,
		skipHeartbeatDelivery,
		skipMessagingToolDelivery,
		unverifiedMessagingToolDelivery: didSendViaMessagingTool && !prepared.resolvedDelivery.ok,
		deliveryBestEffort: resolveCronDeliveryBestEffort(prepared.input.job),
		deliveryPayloadHasStructuredContent,
		deliveryPayloads,
		synthesizedText,
		ttsAuto: prepared.cronSession.sessionEntry.ttsAuto,
		summary,
		outputText,
		telemetry,
		abortSignal: prepared.input.abortSignal ?? prepared.input.signal,
		isAborted: params.isAborted,
		abortReason: params.abortReason,
		withRunSession: prepared.withRunSession
	});
	const deliveryTrace = buildCronDeliveryTrace({
		deliveryPlan: prepared.deliveryPlan,
		resolvedDelivery: prepared.resolvedDelivery,
		messagingToolSentTargets,
		matchesMessagingToolDeliveryTarget,
		fallbackUsed: deliveryResult.deliveryAttempted && !skipMessagingToolDelivery,
		delivered: deliveryResult.delivered
	});
	if (deliveryResult.result) {
		const resultWithDeliveryMeta = {
			...deliveryResult.result,
			deliveryAttempted: deliveryResult.result.deliveryAttempted ?? deliveryResult.deliveryAttempted,
			delivery: deliveryTrace,
			diagnostics: mergeCronRunDiagnostics(agentDiagnostics, deliveryResult.result.diagnostics, deliveryResult.result.status === "error" && deliveryResult.result.error ? createCronRunDiagnosticsFromError("delivery", deliveryResult.result.error) : void 0)
		};
		failPendingPresentationWarningUnlessDelivered(resultWithDeliveryMeta.delivered ?? deliveryResult.delivered);
		if (!hasFatalErrorPayload || deliveryResult.result.status !== "ok") return resultWithDeliveryMeta;
		return resolveRunOutcome({
			delivered: deliveryResult.result.delivered,
			deliveryAttempted: resultWithDeliveryMeta.deliveryAttempted,
			delivery: deliveryTrace
		});
	}
	summary = deliveryResult.summary;
	outputText = deliveryResult.outputText;
	failPendingPresentationWarningUnlessDelivered(deliveryResult.delivered);
	return resolveRunOutcome({
		delivered: deliveryResult.delivered,
		deliveryAttempted: deliveryResult.deliveryAttempted,
		delivery: deliveryTrace
	});
}
/**
* Mirror a finished cron run's result into the job's visible result session,
* so users can read each run's output as an appended assistant message.
*
* No-op when the job has no resultSessionKey, when the result has no text, or
* when the target session is missing from the store (append returns an error
* reason, which we swallow — mirroring is best-effort and must never fail a run).
*/
async function mirrorCronResultToSession(params) {
	const resultSessionKey = normalizeOptionalString(params.job.resultSessionKey);
	if (!resultSessionKey) return;
	if (params.result.status === "skipped") return;
	const text = params.result.status === "error" ? `⚠️ 定时任务执行失败：${normalizeOptionalString(params.result.error) ?? "未知错误"}` : normalizeOptionalString(params.result.outputText ?? params.result.summary);
	if (!text) return;
	try {
		const { appendAssistantMessageToSessionTranscript } = await cronTranscriptRuntimeLoader.load();
		const appended = await appendAssistantMessageToSessionTranscript({
			agentId: params.agentId,
			sessionKey: resultSessionKey,
			text,
			idempotencyKey: params.result.sessionId ? `cron:${params.job.id}:run:${params.result.sessionId}` : void 0
		});
		return appended.ok ? normalizeOptionalString(appended.messageId) : void 0;
	} catch (err) {
		logWarn(`[cron:${params.job.id}] failed to mirror result to session: ${String(err)}`);
		return;
	}
}
/**
* Drain a finished run's produced files and copy them into the visible result
* session's outputFileManifest, so the dashboard's "会话文件" panel surfaces them.
*
* Always drains the per-run collector (even when there's nothing to write) so the
* in-memory map can never leak across runs. Best-effort: a missing result session
* entry just no-ops via updateSessionStoreEntry, mirroring the text-mirror's
* never-fail-a-run contract.
*/
async function mirrorCronOutputFilesToSession(params) {
	const files = drainOutputFilesForRun(params.result.sessionId);
	if (files.length === 0) return;
	const resultSessionKey = normalizeOptionalString(params.job.resultSessionKey);
	if (!resultSessionKey) return;
	if (params.result.status === "skipped") return;
	const runSessionId = normalizeOptionalString(params.result.sessionId);
	if (!runSessionId) return;
	try {
		const { updateSessionStoreEntry } = await loadSessionStoreRuntime();
		await updateSessionStoreEntry({
			storePath: resolveStorePath(params.cfg.session?.store, { agentId: params.agentId }),
			sessionKey: resultSessionKey,
			update: async (entry) => {
				const next = { ...entry.outputFileManifest ?? {} };
				for (const file of files) next[file.absPath] = {
					sessionId: runSessionId,
					createdAtMs: file.createdAtMs
				};
				return { outputFileManifest: next };
			}
		});
	} catch (err) {
		logWarn(`[cron:${params.job.id}] failed to mirror output files to session: ${String(err)}`);
	}
}
async function runCronIsolatedAgentTurn(params) {
	const abortSignal = params.abortSignal ?? params.signal;
	const isAborted = () => abortSignal?.aborted === true;
	const abortReason = () => {
		const reason = abortSignal?.reason;
		return typeof reason === "string" && reason.trim() ? reason.trim() : "cron: job execution timed out";
	};
	const prepared = await prepareCronRunContext({
		input: params,
		isFastTestEnv: process.env.OPENCLAW_TEST_FAST === "1"
	});
	const finishWithMirror = async (agentId, result) => {
		const resultMessageId = await mirrorCronResultToSession({
			job: params.job,
			agentId,
			result,
			cfg: params.cfg
		});
		if (resultMessageId) result.resultMessageId = resultMessageId;
		await mirrorCronOutputFilesToSession({
			job: params.job,
			agentId,
			result,
			cfg: params.cfg
		});
		return result;
	};
	if (!prepared.ok) return finishWithMirror(prepared.agentId, prepared.result);
	const notifyExecutionStarted = () => params.onExecutionStarted?.({
		jobId: params.job.id,
		agentId: prepared.context.agentId,
		sessionId: prepared.context.runSessionId,
		sessionKey: prepared.context.runSessionKey
	});
	try {
		const { executeCronRun } = await loadCronExecutorRuntime();
		const execution = await executeCronRun({
			cfg: params.cfg,
			cfgWithAgentDefaults: prepared.context.cfgWithAgentDefaults,
			job: params.job,
			agentId: prepared.context.agentId,
			agentDir: prepared.context.agentDir,
			agentSessionKey: prepared.context.agentSessionKey,
			runSessionKey: prepared.context.runSessionKey,
			workspaceDir: prepared.context.workspaceDir,
			lane: params.lane,
			resolvedDelivery: {
				channel: prepared.context.resolvedDelivery.channel,
				to: prepared.context.resolvedDelivery.to,
				accountId: prepared.context.resolvedDelivery.accountId,
				threadId: prepared.context.resolvedDelivery.threadId
			},
			toolPolicy: prepared.context.toolPolicy,
			skillsSnapshot: prepared.context.skillsSnapshot,
			agentPayload: prepared.context.agentPayload,
			agentVerboseDefault: prepared.context.agentCfg?.verboseDefault,
			liveSelection: prepared.context.liveSelection,
			cronSession: prepared.context.cronSession,
			commandBody: prepared.context.commandBody,
			persistSessionEntry: prepared.context.persistSessionEntry,
			abortSignal,
			onExecutionStarted: notifyExecutionStarted,
			abortReason,
			isAborted,
			thinkLevel: prepared.context.thinkLevel,
			timeoutMs: prepared.context.timeoutMs,
			suppressExecNotifyOnExit: prepared.context.suppressExecNotifyOnExit
		});
		if (isAborted()) return finishWithMirror(prepared.context.agentId, prepared.context.withRunSession({
			status: "error",
			error: abortReason(),
			diagnostics: createCronRunDiagnosticsFromError("cron-setup", abortReason())
		}));
		const finalized = await finalizeCronRun({
			prepared: prepared.context,
			execution,
			abortReason,
			isAborted
		});
		return finishWithMirror(prepared.context.agentId, finalized);
	} catch (err) {
		return finishWithMirror(prepared.context.agentId, prepared.context.withRunSession({
			status: "error",
			error: String(err),
			diagnostics: createCronRunDiagnosticsFromError("agent-run", err)
		}));
	}
}
//#endregion
export { runCronIsolatedAgentTurn as t };
