UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

1,280 lines 59.1 kB
import { I as resolveTimestampMsToIsoString, o as asDateTimestampMs } from "./number-coercion-CLj0HTDM.js";
import { t as parseBoolean } from "./boolean-coercion-1HZNNkFl.js";
import { l as normalizeOptionalString } from "./string-coerce-CIXf7egm.js";
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import { y as resolveDefaultAgentId } from "./agent-scope-config-DcbEhP0R.js";
import { O as parseAgentSessionKey, T as isSubagentSessionKey } from "./session-key-BnWWjqNc.js";
import { n as normalizeAccountId } from "./account-id-CETVCrTz.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { n as GatewayErrorDetailCodes, t as ErrorCodes } from "./gateway-error-details-w0nAGBBp.js";
import { $o as validateWakeParams, _t as validateCronStatusParams, dt as validateCronListParams, ft as validateCronRemoveParams, gt as validateCronScratchSetParams, ht as validateCronScratchGetParams, lt as validateCronAddParams, mt as validateCronRunsParams, pt as validateCronRunParams, ut as validateCronGetParams, vt as validateCronUpdateParams } from "./src-BiL5aQto.js";
import { n as resolveNormalizedAccountEntry } from "./account-lookup-CvFhSs3G.js";
import { n as normalizeMessageChannel } from "./message-channel-core-AaEWic-A.js";
import { t as isDeliverableMessageChannel } from "./message-channel-normalize-B81s0XMO.js";
import "./message-channel-BQrhwUEA.js";
import { d as errorShape } from "./error-codes-Bo8q2D1o.js";
import { a as isAgentHarnessSessionKey, d as resolveAgentHarnessSessionStoreEntryError, n as AGENT_HARNESS_SESSION_ID_LOCKED_MESSAGE, r as AGENT_HARNESS_SESSION_KEY_RESERVED_MESSAGE } from "./agent-harness-session-key-BOz3yx0-.js";
import { a as validateTargetProviderPrefix, t as resolveTargetPrefixedChannel } from "./channel-target-prefix-D7Ut6oKe.js";
import { r as normalizeCronJobPatch, t as normalizeCronJobCreate } from "./normalize-Dd-LvsuI.js";
import { n as createTrustedCronScheduledToolPolicy, t as createAccountCronScheduledToolPolicy } from "./scheduled-tool-policy-WcH3cpLk.js";
import { t as parseAbsoluteTimeMs } from "./parse-tlCSjrTr.js";
import { a as resolveCronDeliverySessionKey, r as isInvalidCronSessionTargetIdError, s as resolveCronSessionTargetSessionKey } from "./session-target-DJsUULzX.js";
import { t as resolveCronJobConfigRevision } from "./config-revision-CExLxl38.js";
import { t as cronStoreKey } from "./key-BBZ40bDq.js";
import { n as cronJobUsesToolRuntime } from "./tools-allow-BSqnFkMT.js";
import { r as bindCronSelfRemovalCommitGuard } from "./active-jobs-C_biqiZG.js";
import { f as resolveOperatorSessionCreation, i as operatorSessionCap, t as authorizeGatewaySessionCreation } from "./operator-role-policy-wsr1DeJv.js";
import { t as CRON_JOB_SCRATCH_MAX_BYTES } from "./scratch-contract-B-Laspel.js";
import { n as resolveRequestedSessionAgentId } from "./session-request-agent-CCRSEGCB.js";
import { i as loadGatewaySessionEntryReadOnly } from "./session-utils-store-CInT2loy.js";
import "./session-utils-Cai0_C6U.js";
import { t as assertCronDeliveryInputNonBlankFields } from "./delivery-target-validation-D5dmr1ev.js";
import { i as getCronManagementAuthority, n as consumeCronCreatorAuthorityGrant, s as withCronManagementGrant } from "./cron-creator-authority-grant-CDgrJQRP.js";
import { n as listConfiguredMessageChannels } from "./channel-selection-DGYiyd5S.js";
import { n as resolveCronDeliveryPlan, t as hasExplicitCronDeliveryTarget } from "./delivery-plan-CjKzhfrM.js";
import { c as readCronTaskRunHistoryPage, f as resolveFailureAlert, o as isInvalidCronTaskRunJobIdError, r as applyJobPatch, t as resolveCronListSnapshotRevision } from "./list-snapshot-revision-DIucIOZa.js";
import { n as resolveCronJobEffectiveAgentId } from "./agent-id-C76WNTsz.js";
import { r as createSessionListEntryFilter } from "./session-sharing-B7MI8hNo.js";
import { t as assertValidParams } from "./validation-pzrlzFvo.js";
import { t as assertActiveAgentRuntimeAuthority } from "./agent-runtime-authority-BkcmfdKo.js";
import { n as cronJobReadView } from "./job-read-view-CpyaDeBp.js";
import { n as resolvedDeliveryTargetsExternalChannel, t as resolveDeliveryTarget } from "./delivery-target-D8sif7VA.js";
import { t as getGatewayProcessInstanceId } from "./process-instance-CwB3RMsz.js";
//#region src/cron/delivery-channel-validation.ts
function hasExplicitChannelConfigEntry(cfg) {
	const channels = cfg.channels;
	if (!channels || typeof channels !== "object" || Array.isArray(channels)) return false;
	return Object.entries(channels).some(([channelId, entry]) => {
		if (channelId === "defaults" || channelId === "modelByChannel") return false;
		return Boolean(entry && typeof entry === "object" && !Array.isArray(entry) && Object.keys(entry).length > 0);
	});
}
async function assertConfiguredAnnounceChannel(params) {
	if (params.channel === "last") return;
	const normalizedChannel = normalizeMessageChannel(params.channel);
	if (!normalizedChannel && params.field === "delivery.channel") return;
	const configuredChannels = (await listConfiguredMessageChannels(params.cfg)).toSorted();
	if (!normalizedChannel) {
		if (configuredChannels.length <= 1) return;
		throw new Error(`${params.field} is required when multiple channels are configured: ${configuredChannels.join(", ")}`);
	}
	if (configuredChannels.length === 0) {
		if (!hasExplicitChannelConfigEntry(params.cfg)) {
			if (!isDeliverableMessageChannel(normalizedChannel)) throw new Error(`${params.field} is not a known channel: ${normalizedChannel}`);
			return;
		}
		throw new Error(`${params.field} is not configured: ${normalizedChannel}`);
	}
	if (!configuredChannels.includes(normalizedChannel)) throw new Error(`${params.field} must be one of: ${configuredChannels.join(", ")}`);
}
/**
* Rejects an announce account the operator has turned off in config, so a job is
* not scheduled against a route its owner already disabled - the same late failure
* this file prevents for `channel` (see `assertValidCronFailureAlert`).
*
* Scoped to a matching `accounts.<id>.enabled: false` entry, and nothing else.
* Cron delivery ids are not always operator-typed (`delivery-context.ts` copies
* the current context account into inferred jobs); channel `isEnabled` adapters
* report unlisted or credential-suppressed accounts as not enabled; and a
* top-level `channels.<id>.enabled: false` is not uniformly channel-wide - twitch
* resolves named accounts from `accounts` alone. Only the account entry itself is
* an unambiguous statement about this route.
*/
function assertEnabledAnnounceAccount(params) {
	if (!params.accountId || !params.channel || params.channel === "last") return;
	const channel = normalizeMessageChannel(params.channel) ?? params.channel;
	const channelConfig = params.cfg.channels?.[channel];
	if (!channelConfig || typeof channelConfig !== "object" || Array.isArray(channelConfig)) return;
	const accounts = channelConfig.accounts;
	if (resolveNormalizedAccountEntry(accounts, params.accountId, normalizeAccountId)?.enabled !== false) return;
	throw new Error(`${params.field}: account "${params.accountId}" is disabled for channel ${channel}`);
}
function resolveAnnounceValidationChannel(params) {
	return params.channel && params.channel !== "last" ? params.channel : resolveTargetPrefixedChannel(params.to) ?? params.channel;
}
function assertCompatibleAnnounceTarget(params) {
	if (!params.channel || params.channel === "last") return;
	const error = validateTargetProviderPrefix({
		channel: params.channel,
		to: params.to
	});
	if (error) throw new Error(`${params.field}: ${error.message}`);
}
async function assertValidCronAnnounceDelivery(params) {
	if (params.delivery && (params.delivery.mode ?? "announce") === "announce") {
		assertCompatibleAnnounceTarget({
			channel: params.delivery.channel,
			to: params.delivery.to,
			field: "delivery.channel"
		});
		await assertConfiguredAnnounceChannel({
			cfg: params.cfg,
			channel: resolveAnnounceValidationChannel(params.delivery),
			field: "delivery.channel"
		});
		assertEnabledAnnounceAccount({
			cfg: params.cfg,
			channel: resolveAnnounceValidationChannel(params.delivery),
			accountId: params.delivery.accountId,
			field: "delivery.accountId"
		});
	}
	const failureDestination = params.delivery?.failureDestination;
	if (failureDestination && (failureDestination.mode ?? "announce") === "announce") {
		if (failureDestination.channel === void 0 && failureDestination.to === void 0 && failureDestination.accountId === void 0 && failureDestination.mode === void 0) return;
		assertCompatibleAnnounceTarget({
			channel: failureDestination.channel,
			to: failureDestination.to,
			field: "delivery.failureDestination.channel"
		});
		await assertConfiguredAnnounceChannel({
			cfg: params.cfg,
			channel: resolveAnnounceValidationChannel(failureDestination),
			field: "delivery.failureDestination.channel"
		});
	}
}
/**
* Validates the per-job `failureAlert` channel the same way announce delivery is
* validated. `failureAlert` is a distinct field from `delivery.failureDestination`
* (its own store columns and delivery path), so it needs its own check - otherwise
* an explicit unknown channel (e.g. a Slack `C0...` id passed to
* `--failure-alert-channel`) is stored and only fails later as `channel_not_found`.
*/
async function assertValidCronFailureAlert(params) {
	const failureAlert = resolveFailureAlert({ deps: { cronConfig: params.cfg.cron } }, {
		delivery: params.delivery,
		failureAlert: params.failureAlert
	});
	if (!failureAlert || failureAlert.mode === "webhook") return;
	assertCompatibleAnnounceTarget({
		channel: failureAlert.channel,
		to: failureAlert.to,
		field: "failureAlert.channel"
	});
	await assertConfiguredAnnounceChannel({
		cfg: params.cfg,
		channel: failureAlert.channel,
		field: "failureAlert.channel"
	});
}
async function assertValidCronCreateDelivery(cfg, job) {
	await assertValidCronAnnounceDelivery({
		cfg,
		delivery: job.delivery
	});
	await assertValidCronFailureAlert({
		cfg,
		failureAlert: job.failureAlert,
		delivery: job.delivery
	});
}
//#endregion
//#region src/cron/delivery-preview.ts
/** Builds dry-run cron delivery labels for CLI/UI list surfaces. */
function formatTarget(channel, to) {
	if (!channel) return "last";
	if (to) return `${channel}:${to}`;
	return channel;
}
function formatDeliveryDetail(params) {
	if (params.requestedChannel === "last" || !params.requestedChannel) {
		if (!params.resolved) return params.error ? `last -> no route, will fail-closed: ${params.error}` : "last -> no route, will fail-closed";
		return params.sessionKey ? `resolved from last, session ${params.sessionKey}` : "resolved from last, main session";
	}
	return params.resolved ? "explicit" : params.error ?? "unresolved";
}
/** Builds the user-visible cron delivery preview for one job without sending anything. */
async function resolveCronDeliveryPreview(params) {
	const plan = resolveCronDeliveryPlan(params.job);
	if (plan.mode === "none" && !hasExplicitCronDeliveryTarget(plan)) return {
		label: "not requested",
		detail: "not requested"
	};
	if (plan.mode === "webhook") return {
		label: plan.to ? `webhook:${plan.to}` : "webhook",
		detail: plan.to ? "webhook" : "webhook target missing"
	};
	const requestedChannel = plan.channel ?? "last";
	const agentId = params.job.agentId?.trim() || params.defaultAgentId || resolveDefaultAgentId(params.cfg);
	const deliverySessionKey = resolveCronDeliverySessionKey(params.job);
	const resolved = await resolveDeliveryTarget(params.cfg, agentId, {
		channel: requestedChannel,
		to: plan.to,
		threadId: plan.threadId,
		accountId: plan.accountId,
		sessionKey: deliverySessionKey
	}, { dryRun: true });
	if (!resolved.ok) {
		if (params.job.sessionTarget === "current" && plan.mode === "announce" && !resolvedDeliveryTargetsExternalChannel(resolved)) return {
			label: "announce -> current session",
			detail: "commits to this conversation (no external channel route)"
		};
		return {
			label: `${plan.mode} -> ${formatTarget(requestedChannel, plan.to ?? null)}`,
			detail: plan.mode === "none" ? `message tool target unresolved: ${resolved.error.message}` : formatDeliveryDetail({
				requestedChannel,
				resolved: false,
				sessionKey: deliverySessionKey,
				error: resolved.error.message
			})
		};
	}
	return {
		label: `${plan.mode} -> ${formatTarget(resolved.channel, resolved.to)}`,
		detail: formatDeliveryDetail({
			requestedChannel,
			resolved: true,
			sessionKey: deliverySessionKey
		})
	};
}
/** Builds cron delivery previews keyed by job id. */
async function resolveCronDeliveryPreviews(params) {
	const entries = await Promise.all(params.jobs.map(async (job) => [job.id, await resolveCronDeliveryPreview({
		cfg: params.cfg,
		defaultAgentId: params.defaultAgentId,
		job
	})]));
	return Object.fromEntries(entries);
}
//#endregion
//#region src/cron/validate-timestamp.ts
/** Validates user-supplied one-shot cron timestamps before scheduling. */
const ONE_MINUTE_MS = 6e4;
const TEN_YEARS_MS = 315576e6;
/**
* Validates one-shot cron timestamps with a small past grace window and far-future cap.
*/
function validateScheduleTimestamp(schedule, nowMs = Date.now()) {
	if (schedule.kind !== "at") return { ok: true };
	const atRaw = normalizeOptionalString(schedule.at) ?? "";
	const atMs = atRaw ? parseAbsoluteTimeMs(atRaw) : null;
	if (atMs === null || !Number.isFinite(atMs)) return {
		ok: false,
		message: `Invalid schedule.at: expected ISO-8601 timestamp (got ${schedule.at})`
	};
	const referenceNowMs = asDateTimestampMs(nowMs) ?? asDateTimestampMs(Date.now()) ?? 0;
	const diffMs = atMs - referenceNowMs;
	if (diffMs < -6e4) {
		const nowDate = resolveTimestampMsToIsoString(referenceNowMs);
		return {
			ok: false,
			message: `schedule.at is in the past: ${resolveTimestampMsToIsoString(atMs)} (${Math.floor(-diffMs / ONE_MINUTE_MS)} minutes ago). Current time: ${nowDate}`
		};
	}
	if (diffMs > TEN_YEARS_MS) return {
		ok: false,
		message: `schedule.at is too far in the future: ${resolveTimestampMsToIsoString(atMs)} (${Math.floor(diffMs / 315576e5)} years ahead). Maximum allowed: 10 years`
	};
	return { ok: true };
}
//#endregion
//#region src/gateway/server-methods/cron-caller-scope.ts
function readCronCallerScope(client) {
	const identity = client?.internal?.agentRuntimeIdentity;
	if (!identity?.agentId) return;
	const cronSelfManagementContext = identity.cronSelfManagementContext;
	const currentJobId = cronSelfManagementContext && Date.now() < cronSelfManagementContext.expiresAtMs ? cronSelfManagementContext.jobId.trim() || void 0 : void 0;
	const sourceChannel = identity.turnSourceChannel?.trim().toLowerCase();
	const manageAll = getCronManagementAuthority(identity);
	const callerOrigin = sourceChannel ? {
		kind: "external",
		channel: sourceChannel
	} : identity.turnSourceLocal === true ? { kind: "local" } : { kind: "unknown" };
	return {
		kind: "agentTool",
		agentId: normalizeAgentId(identity.agentId),
		sessionKey: identity.sessionKey?.trim() || void 0,
		accountId: normalizeAccountId(identity.turnSourceAccountId),
		currentJobId,
		manageAll,
		...!manageAll && identity.cronToolsAllowCapture === "final-executable-surface" ? {
			toolsAllowProvenance: {
				version: 1,
				source: "final-executable-surface",
				callerOrigin
			},
			...identity.cronExecToolTarget?.host === "gateway" ? { toolsAllowExecTarget: {
				version: 1,
				...identity.cronExecToolTarget
			} } : {}
		} : {},
		...!manageAll && identity.cronCreatorAuthorityGrant ? { cronCreatorAuthorityGrant: identity.cronCreatorAuthorityGrant } : {}
	};
}
/** Converts the authenticated gateway caller into server-only scheduled authority provenance. */
function resolveCronScheduledToolPolicyForCaller(callerScope) {
	if (!callerScope) return createTrustedCronScheduledToolPolicy();
	const policy = callerScope.sessionKey ? createAccountCronScheduledToolPolicy({
		ownerSessionKey: callerScope.sessionKey,
		ownerAccountId: callerScope.accountId
	}) : void 0;
	if (!policy) throw new TypeError("agent-runtime cron mutations require an authenticated session identity");
	return policy;
}
function parseAgentIdFromSessionRef(value, fallbackAgentId) {
	const trimmed = value?.trim();
	return trimmed ? parseAgentSessionKey(trimmed)?.agentId ?? fallbackAgentId : void 0;
}
function resolveCronJobOwnerAgentId(job) {
	const ownerAgentId = job.owner?.agentId?.trim() || parseAgentIdFromSessionRef(job.owner?.sessionKey);
	return ownerAgentId ? normalizeAgentId(ownerAgentId) : void 0;
}
function isOperatorCommandCronJob(job) {
	return job.payload.kind === "command" || job.schedule.kind === "on-exit" || job.schedule.kind === "stream";
}
function cronJobMatchesCallerScope(params) {
	if (!params.callerScope) return true;
	if (params.callerScope.manageAll) {
		params.callerScope.manageAll();
		return true;
	}
	if (isOperatorCommandCronJob(params.job)) return false;
	const effectiveAgentId = resolveCronJobEffectiveAgentId(params.job, params.defaultAgentId);
	const policy = params.job.scheduledToolPolicy;
	if (params.allowCurrentJob === true && params.callerScope.currentJobId === params.job.id && effectiveAgentId === params.callerScope.agentId && (policy?.mode !== "account" || normalizeAccountId(policy.ownerAccountId) === params.callerScope.accountId)) return true;
	if (policy && (policy.mode === "trusted" || params.callerScope.sessionKey?.trim() !== policy.ownerSessionKey || params.job.owner?.sessionKey?.trim() !== policy.ownerSessionKey || params.callerScope.accountId !== normalizeAccountId(policy.ownerAccountId))) return false;
	const ownerAccountId = params.job.owner?.accountId;
	if (ownerAccountId && normalizeAccountId(ownerAccountId) !== params.callerScope.accountId) return false;
	const ownerAgentId = resolveCronJobOwnerAgentId(params.job);
	if (ownerAgentId) return ownerAgentId === params.callerScope.agentId;
	if (effectiveAgentId !== params.callerScope.agentId) return false;
	return cronPatchSessionRefsMatchCaller(params.job, params.callerScope);
}
function cronJobMatchesDeclarationScope(params) {
	if (params.callerScope) return cronJobMatchesCallerScope(params);
	if (normalizeAccountId(params.job.owner?.accountId) !== normalizeAccountId(params.input.owner?.accountId)) return false;
	const inputOwnerSessionKey = params.input.owner?.sessionKey;
	const inputOwnerAgentId = resolveCronJobOwnerAgentId(params.input);
	if (inputOwnerSessionKey && !inputOwnerAgentId) return params.job.owner?.sessionKey === inputOwnerSessionKey;
	const inputAgentId = inputOwnerAgentId ?? resolveCronJobEffectiveAgentId(params.input, params.defaultAgentId);
	return (resolveCronJobOwnerAgentId(params.job) ?? resolveCronJobEffectiveAgentId(params.job, params.defaultAgentId)) === inputAgentId;
}
function cronCreateMatchesCallerScope(params) {
	if (!params.callerScope) return true;
	if (resolveCronJobEffectiveAgentId(params.job, params.defaultAgentId) !== params.callerScope.agentId) return false;
	return cronPatchSessionRefsMatchCaller(params.job, params.callerScope);
}
function applyCronCreateCallerScopeDefault(job, callerScope) {
	if (!callerScope) return job;
	return {
		...job,
		agentId: job.agentId?.trim() ? job.agentId : callerScope.agentId,
		owner: {
			agentId: callerScope.agentId,
			...callerScope.sessionKey ? { sessionKey: callerScope.sessionKey } : {},
			accountId: callerScope.accountId
		}
	};
}
function cronPatchSessionRefsMatchCaller(patch, callerScope) {
	if (!callerScope || callerScope.manageAll) {
		callerScope?.manageAll?.();
		return true;
	}
	const target = patch.sessionTarget?.trim();
	return [patch.sessionKey, target?.startsWith("session:") ? target.slice(8) : void 0].every((ref) => {
		const agentId = parseAgentIdFromSessionRef(ref, callerScope.agentId);
		return !agentId || normalizeAgentId(agentId) === callerScope.agentId;
	});
}
//#endregion
//#region src/gateway/server-methods/cron-error-classification.ts
function isCronInvalidRequestError(err) {
	const message = formatErrorMessage(err);
	return message.startsWith("unknown cron job id:") || message.startsWith("cron job already exists:") || message.includes("cron job id must not be blank") || message.includes("cron declarationKey") || message.includes("cron displayName") || message.includes("cron announce delivery requires an explicit channel") || message.includes("cron script payload has a syntax error") || message.includes("cron trigger script has a syntax error") || message.includes("cron script payload must not be empty") || message.includes("cron script payloads cannot be combined") || message.includes("cron script payloads are disabled") || message.includes("cron triggers are disabled") || message.includes("cron triggers require") || message.includes("cron trigger every interval") || message.includes("cron job is missing sessionTarget") || message.includes("invalid cron sessionTarget session id") || message.includes("main cron jobs require payload.kind=\"systemEvent\"") || message.includes("isolated/current/session cron jobs require payload.kind=\"agentTurn\"") || message.includes("has no upcoming run time and would never fire") || message.includes("sessionTarget \"main\" is only valid for the default agent") || message.includes("cron.update payload.kind=\"systemEvent\" requires text") || message.includes("cron.update payload.kind=\"agentTurn\" requires message") || message.includes("cron webhook delivery requires") || message.includes("delivery.channel") || message.includes("delivery.accountId") || message.includes("delivery.failureDestination.channel") || message.includes("failureAlert.channel") || message.includes("cron completion destination webhook requires") || message.includes("cron failure destination webhook requires") || message.includes("cron channel delivery config is only supported") || message.includes("cron delivery.failureDestination is only supported");
}
//#endregion
//#region src/gateway/server-methods/cron-list-caller-scope.ts
const CRON_LIST_SCOPED_SNAPSHOT_MAX_ATTEMPTS = 3;
async function listCronPageWithVisibility({ matchesJob, context, options }) {
	let stableScopedJobs;
	for (let attempt = 0; attempt < CRON_LIST_SCOPED_SNAPSHOT_MAX_ATTEMPTS; attempt += 1) {
		const scopedJobs = [];
		let offset = 0;
		let snapshotRevision;
		let snapshotChanged = false;
		for (;;) {
			const sourcePage = await context.cron.listPage({
				...options,
				limit: 200,
				offset
			});
			if (snapshotRevision && sourcePage.snapshotRevision !== snapshotRevision) {
				snapshotChanged = true;
				break;
			}
			snapshotRevision = sourcePage.snapshotRevision;
			scopedJobs.push(...sourcePage.jobs.filter(matchesJob));
			if (!sourcePage.hasMore || sourcePage.nextOffset === null || sourcePage.nextOffset <= offset) break;
			offset = sourcePage.nextOffset;
		}
		if (!snapshotChanged && snapshotRevision) {
			stableScopedJobs = scopedJobs;
			break;
		}
	}
	if (!stableScopedJobs) throw new Error("cron.list changed repeatedly while applying caller scope");
	const total = stableScopedJobs.length;
	const pageOffset = Math.max(0, Math.min(total, Math.floor(options.offset ?? 0)));
	const defaultLimit = total === 0 ? 50 : total;
	const limit = Math.max(1, Math.min(200, Math.floor(options.limit ?? defaultLimit)));
	const jobs = stableScopedJobs.slice(pageOffset, pageOffset + limit);
	const nextOffset = pageOffset + jobs.length;
	return {
		jobs,
		snapshotRevision: resolveCronListSnapshotRevision(stableScopedJobs),
		total,
		offset: pageOffset,
		limit,
		hasMore: nextOffset < total,
		nextOffset: nextOffset < total ? nextOffset : null
	};
}
//#endregion
//#region src/gateway/server-methods/cron-run-log-filters.ts
function filterCronRunLogJobsByAgent(jobs, agentId, defaultAgentId) {
	if (!agentId) return [...jobs];
	const normalizedAgentId = normalizeAgentId(agentId);
	return jobs.filter((job) => resolveCronJobEffectiveAgentId(job, defaultAgentId) === normalizedAgentId);
}
function cronRunLogPageFilters(params) {
	return {
		limit: params.limit,
		offset: params.offset,
		statuses: params.statuses,
		status: params.status,
		runId: params.runId,
		deliveryStatuses: params.deliveryStatuses,
		deliveryStatus: params.deliveryStatus,
		query: params.query,
		sortDir: params.sortDir
	};
}
//#endregion
//#region src/gateway/server-methods/cron.ts
function resolveCronCreatorAuthorityCapture(callerScope) {
	const grant = callerScope?.cronCreatorAuthorityGrant;
	if (!grant) return;
	if (!callerScope.toolsAllowProvenance) throw new TypeError("cron creator authority grant is missing tool-surface provenance");
	return () => consumeCronCreatorAuthorityGrant(grant);
}
function resolveCronMutationCommitGuard(client, context, jobScope) {
	const validatesAuthority = client?.internal?.agentRuntimeIdentity && context.validateAgentRuntimeApprovalAuthority;
	const identity = client?.internal?.agentRuntimeIdentity;
	const manageAll = identity ? getCronManagementAuthority(identity) : void 0;
	if (!validatesAuthority && !jobScope?.callerScope && !manageAll) return;
	return () => {
		manageAll?.();
		if (validatesAuthority) assertActiveAgentRuntimeAuthority(client, context);
		if (!jobScope?.callerScope) return;
		const callerScope = readCronCallerScope(client);
		const job = context.cron.getJob(jobScope.jobId);
		if (!callerScope || !job || jobScope.expectedConfigRevision !== void 0 && resolveCronJobConfigRevision(job) !== jobScope.expectedConfigRevision || !cronJobMatchesCallerScope({
			job,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId(),
			allowCurrentJob: jobScope.allowCurrentJob
		})) throw new TypeError(`unknown cron job id: ${jobScope.jobId}`);
	};
}
var CronJobConfigRevisionConflictError = class extends Error {
	constructor(expectedConfigRevision, actualConfigRevision) {
		super("cron job definition no longer matches the loaded version");
		this.expectedConfigRevision = expectedConfigRevision;
		this.actualConfigRevision = actualConfigRevision;
	}
};
function publicCronScratch(scratch) {
	if (!scratch) return null;
	return {
		content: scratch.content,
		revision: scratch.revision,
		updatedAtMs: scratch.updatedAtMs
	};
}
function cronAddPayloadWithDeliveryPreview(params) {
	const job = "job" in params.result ? params.result.job : params.result;
	if ("job" in params.result) return {
		created: params.result.created,
		...params.result.updated === void 0 ? {} : { updated: params.result.updated },
		job: cronJobReadView(job),
		deliveryPreview: params.deliveryPreview
	};
	return {
		...cronJobReadView(job),
		deliveryPreview: params.deliveryPreview
	};
}
function compactCronListJob(job) {
	return {
		id: job.id,
		name: job.name,
		...job.declarationKey ? { declarationKey: job.declarationKey } : {},
		...job.displayName ? { displayName: job.displayName } : {},
		...job.owner ? { owner: job.owner } : {},
		enabled: job.enabled,
		nextRunAtMs: job.state.nextRunAtMs ?? null,
		scheduleKind: job.schedule.kind,
		...job.trigger ? { trigger: true } : {},
		lastRunAtMs: job.state.lastRunAtMs ?? null,
		lastRunStatus: job.state.lastRunStatus ?? job.state.lastStatus ?? null,
		lastRunError: job.state.lastError ?? null,
		...job.state.lastDelivered !== void 0 ? { lastDelivered: job.state.lastDelivered } : {},
		...job.state.lastDeliveryStatus !== void 0 ? { lastDeliveryStatus: job.state.lastDeliveryStatus } : {},
		...job.state.lastDeliveryError !== void 0 ? { lastDeliveryError: job.state.lastDeliveryError } : {},
		...job.state.deliverySuppressionReason !== void 0 ? { deliverySuppressionReason: job.state.deliverySuppressionReason } : {},
		...job.state.lastFailureNotificationDelivered !== void 0 ? { lastFailureNotificationDelivered: job.state.lastFailureNotificationDelivered } : {},
		...job.state.lastFailureNotificationDeliveryStatus !== void 0 ? { lastFailureNotificationDeliveryStatus: job.state.lastFailureNotificationDeliveryStatus } : {},
		...job.state.lastFailureNotificationDeliveryError !== void 0 ? { lastFailureNotificationDeliveryError: job.state.lastFailureNotificationDeliveryError } : {}
	};
}
async function assertValidCronUpdatePatch(params) {
	const nextJob = structuredClone(params.currentJob);
	applyJobPatch(nextJob, params.patch, {
		defaultAgentId: params.defaultAgentId,
		cronConfig: params.cfg.cron
	});
	if ("agentId" in params.patch || "sessionTarget" in params.patch || "sessionKey" in params.patch) assertCronDoesNotTargetAgentHarness(nextJob);
	const effectiveDelivery = params.patch.delivery?.channel === null && nextJob.delivery && (nextJob.delivery.mode ?? "announce") === "announce" && nextJob.delivery.channel === void 0 && resolveTargetPrefixedChannel(nextJob.delivery.to) === void 0 ? {
		...nextJob.delivery,
		channel: "last"
	} : nextJob.delivery;
	if ("delivery" in params.patch) await assertValidCronAnnounceDelivery({
		cfg: params.cfg,
		delivery: effectiveDelivery
	});
	const failureAlertPatch = params.patch.failureAlert;
	const failureAlertRoutingPatched = failureAlertPatch && ("channel" in failureAlertPatch || "to" in failureAlertPatch || "mode" in failureAlertPatch);
	const currentAlert = resolveFailureAlert({ deps: { cronConfig: params.cfg.cron } }, params.currentJob);
	const nextAlert = resolveFailureAlert({ deps: { cronConfig: params.cfg.cron } }, {
		...nextJob,
		delivery: effectiveDelivery
	});
	const alertNewlyEnabled = currentAlert === null && nextAlert !== null;
	const alertRouteChanged = currentAlert?.mode !== nextAlert?.mode || currentAlert?.channel !== nextAlert?.channel || currentAlert?.to !== nextAlert?.to || currentAlert?.accountId !== nextAlert?.accountId || currentAlert?.threadId !== nextAlert?.threadId;
	if (failureAlertRoutingPatched || alertNewlyEnabled || alertRouteChanged && (params.patch.delivery !== void 0 || failureAlertPatch === null)) await assertValidCronFailureAlert({
		cfg: params.cfg,
		failureAlert: nextJob.failureAlert,
		delivery: effectiveDelivery
	});
	return nextJob;
}
function requiresExplicitAgentRuntimeToolsAllow(params) {
	return params.callerScope !== void 0 && !params.callerScope.manageAll && cronJobUsesToolRuntime(params.job) && params.job.payload.toolsAllow === void 0;
}
function cronPatchTouchesToolRuntime(patch) {
	return patch.payload !== void 0 || Object.hasOwn(patch, "trigger");
}
function assertCronDoesNotTargetAgentHarness(input) {
	const targetSessionKey = resolveCronSessionTargetSessionKey(input.sessionTarget) ?? (input.sessionTarget === "current" ? input.sessionKey?.trim() : void 0);
	if (!targetSessionKey) return;
	const loaded = loadGatewaySessionEntryReadOnly(targetSessionKey, input.agentId?.trim() ? { agentId: input.agentId.trim() } : {});
	const reservedKey = isAgentHarnessSessionKey(targetSessionKey) || isAgentHarnessSessionKey(loaded.canonicalKey);
	if (loaded.entry?.modelSelectionLocked === true) throw new Error(reservedKey ? AGENT_HARNESS_SESSION_KEY_RESERVED_MESSAGE : AGENT_HARNESS_SESSION_ID_LOCKED_MESSAGE);
	if (!reservedKey || loaded.entry) return;
	throw new Error(AGENT_HARNESS_SESSION_KEY_RESERVED_MESSAGE);
}
function resolveCronJobId(params) {
	return normalizeOptionalString(params.id ?? params.jobId);
}
function respondInvalidCronParams(respond, method, reason) {
	respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid ${method} params: ${reason}`));
}
function respondMissingCronJobId(respond, method) {
	respondInvalidCronParams(respond, method, "missing id");
}
function respondCronJobNotFound(respond, jobId, options = {}) {
	const message = options.preserveCronGetWireMessage ? `cron job not found: ${jobId}. List automations and retry with a current job id.` : `Automation not found: ${jobId}. List automations and retry with a current job id.`;
	respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `${message} For cross-session management, use a fresh authenticated Control UI administrator turn or the Automations page.`, { details: {
		code: GatewayErrorDetailCodes.CRON_JOB_NOT_FOUND,
		jobId
	} }));
}
function resolveCronSessionVisibility(client, cfg) {
	const identity = client?.internal?.agentRuntimeIdentity;
	if (identity && getCronManagementAuthority(identity)) return;
	if (operatorSessionCap(client, cfg) !== "none") return;
	const entryFilter = createSessionListEntryFilter({
		client,
		cfg
	});
	if (!entryFilter) return;
	return (sessionKey, agentId) => {
		const loaded = loadGatewaySessionEntryReadOnly(sessionKey, agentId ? { agentId } : void 0);
		return loaded.entry !== void 0 && entryFilter(loaded.canonicalKey, loaded.entry);
	};
}
function cronJobIsVisible(job, visibility, defaultAgentId) {
	if (!visibility) return true;
	const sessionKey = job.owner?.sessionKey ?? resolveCronSessionTargetSessionKey(job.sessionTarget) ?? job.sessionKey;
	return Boolean(sessionKey && visibility(sessionKey, job.owner?.agentId ?? job.agentId ?? defaultAgentId));
}
/** Gateway request handlers for cron jobs and cron run-log access. */
const cronHandlers = {
	wake: async ({ params, respond, context, client }) => {
		if (!assertValidParams(params, validateWakeParams, "wake", respond)) return;
		const p = params;
		const sessionKey = p.sessionKey?.trim() || void 0;
		const agentId = p.agentId?.trim() || void 0;
		const callerScope = readCronCallerScope(client);
		const requestedOwner = sessionKey ? resolveRequestedSessionAgentId(context.getRuntimeConfig(), sessionKey, agentId ?? callerScope?.agentId) : void 0;
		if (requestedOwner && !requestedOwner.ok) {
			respond(false, void 0, requestedOwner.error);
			return;
		}
		const resolvedAgentId = requestedOwner?.agentId ?? callerScope?.agentId ?? agentId;
		if (sessionKey && isAgentHarnessSessionKey(sessionKey)) {
			const loaded = loadGatewaySessionEntryReadOnly(sessionKey, resolvedAgentId ? { agentId: resolvedAgentId } : {});
			const harnessSessionError = loaded.entry ? resolveAgentHarnessSessionStoreEntryError(loaded.canonicalKey, loaded.entry) : AGENT_HARNESS_SESSION_KEY_RESERVED_MESSAGE;
			if (harnessSessionError) {
				respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, harnessSessionError));
				return;
			}
		}
		if (sessionKey && isSubagentSessionKey(sessionKey)) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "wake sessionKey cannot target a subagent session"));
			return;
		}
		const sessionKeyAgentId = sessionKey ? parseAgentSessionKey(sessionKey)?.agentId?.trim().toLowerCase() : void 0;
		if (callerScope && agentId && normalizeAgentId(agentId) !== callerScope.agentId) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "wake agentId outside caller scope"));
			return;
		}
		if (agentId && sessionKeyAgentId && agentId.toLowerCase() !== sessionKeyAgentId) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "wake agentId contradicts the agent that owns sessionKey; pass a single canonical wake target"));
			return;
		}
		const wakeConfig = context.getRuntimeConfig();
		if (wakeConfig.gateway?.roles) {
			const knownWakeAgentId = resolvedAgentId ?? context.cron.getDefaultAgentId();
			const wakeAgent = knownWakeAgentId ? {
				ok: true,
				agentId: knownWakeAgentId
			} : resolveRequestedSessionAgentId(wakeConfig, sessionKey ?? "main");
			if (!wakeAgent.ok) {
				respond(false, void 0, wakeAgent.error);
				return;
			}
			const wakeAccessError = authorizeGatewaySessionCreation({
				cfg: wakeConfig,
				client,
				agentId: wakeAgent.agentId
			});
			if (wakeAccessError) {
				respond(false, void 0, wakeAccessError);
				return;
			}
		}
		await context.cron.prepareWake?.();
		assertActiveAgentRuntimeAuthority(client, context);
		respond(true, context.cron.wake({
			mode: p.mode,
			text: p.text,
			...sessionKey ? { sessionKey } : {},
			...resolvedAgentId ? { agentId: resolvedAgentId } : {}
		}), void 0);
	},
	"cron.list": async ({ params, respond, context, client }) => {
		if (!assertValidParams(params, validateCronListParams, "cron.list", respond)) return;
		const p = params;
		const admittedScope = readCronCallerScope(client);
		const callerScope = admittedScope?.manageAll ? void 0 : admittedScope;
		const requestedAgentId = p.agentId ? normalizeAgentId(p.agentId) : void 0;
		if (callerScope && requestedAgentId && requestedAgentId !== callerScope.agentId) {
			respondInvalidCronParams(respond, "cron.list", "agentId outside caller scope");
			return;
		}
		const listOptions = {
			includeDisabled: p.includeDisabled,
			limit: p.limit,
			offset: p.offset,
			query: p.query,
			enabled: p.enabled,
			scheduleKind: p.scheduleKind,
			lastRunStatus: p.lastRunStatus,
			trigger: p.trigger,
			sortBy: p.sortBy,
			sortDir: p.sortDir,
			agentId: callerScope ? void 0 : p.agentId
		};
		const cronVisibility = resolveCronSessionVisibility(client, context.getRuntimeConfig());
		const defaultAgentId = context.cron.getDefaultAgentId();
		const page = callerScope || cronVisibility ? await listCronPageWithVisibility({
			context,
			options: listOptions,
			matchesJob: (job) => cronJobMatchesCallerScope({
				job,
				callerScope,
				defaultAgentId,
				allowCurrentJob: true
			}) && cronJobIsVisible(job, cronVisibility, defaultAgentId)
		}) : await context.cron.listPage(listOptions);
		if (p.compact === true) {
			respond(true, {
				...page,
				jobs: page.jobs.map(compactCronListJob)
			}, void 0);
			return;
		}
		const jobs = page.jobs.map(cronJobReadView);
		if (p.includeDeliveryPreviews === false) {
			respond(true, {
				...page,
				jobs
			}, void 0);
			return;
		}
		const deliveryPreviews = await resolveCronDeliveryPreviews({
			cfg: context.getRuntimeConfig(),
			defaultAgentId: context.cron.getDefaultAgentId(),
			jobs: page.jobs
		});
		respond(true, {
			...page,
			jobs,
			deliveryPreviews
		}, void 0);
	},
	"cron.status": async ({ params, respond, context }) => {
		if (!assertValidParams(params, validateCronStatusParams, "cron.status", respond)) return;
		respond(true, await context.cron.status(), void 0);
	},
	"cron.get": async ({ params, respond, context, client }) => {
		if (!assertValidParams(params, validateCronGetParams, "cron.get", respond)) return;
		const jobId = resolveCronJobId(params);
		if (!jobId) {
			respondMissingCronJobId(respond, "cron.get");
			return;
		}
		const callerScope = readCronCallerScope(client);
		const job = await context.cron.readJob(jobId);
		const cronVisibility = resolveCronSessionVisibility(client, context.getRuntimeConfig());
		if (!job || !cronJobIsVisible(job, cronVisibility, context.cron.getDefaultAgentId()) || !cronJobMatchesCallerScope({
			job,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId(),
			allowCurrentJob: true
		})) {
			respondCronJobNotFound(respond, jobId, { preserveCronGetWireMessage: true });
			return;
		}
		respond(true, cronJobReadView(job), void 0);
	},
	"cron.scratch.get": async ({ params, respond, context, client }) => {
		if (!assertValidParams(params, validateCronScratchGetParams, "cron.scratch.get", respond)) return;
		const jobId = resolveCronJobId(params);
		if (!jobId) {
			respondMissingCronJobId(respond, "cron.scratch.get");
			return;
		}
		const callerScope = readCronCallerScope(client);
		const job = await context.cron.readJob(jobId);
		if (!job || !cronJobMatchesCallerScope({
			job,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId()
		})) {
			respondCronJobNotFound(respond, jobId);
			return;
		}
		const state = await context.cron.readScratch(jobId);
		respond(true, {
			scratch: publicCronScratch(state.scratch),
			currentRevision: state.currentRevision,
			maxBytes: CRON_JOB_SCRATCH_MAX_BYTES
		}, void 0);
	},
	"cron.scratch.set": async ({ params, respond, context, client }) => {
		if (!assertValidParams(params, validateCronScratchSetParams, "cron.scratch.set", respond)) return;
		const p = params;
		const jobId = resolveCronJobId(p);
		if (!jobId) {
			respondMissingCronJobId(respond, "cron.scratch.set");
			return;
		}
		const callerScope = readCronCallerScope(client);
		const job = await context.cron.readJob(jobId);
		if (!job || !cronJobMatchesCallerScope({
			job,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId()
		})) {
			respondCronJobNotFound(respond, jobId);
			return;
		}
		try {
			const commitGuard = resolveCronMutationCommitGuard(client, context, {
				callerScope,
				jobId
			});
			const result = await context.cron.writeScratch(jobId, {
				content: p.content,
				expectedRevision: p.expectedRevision,
				...commitGuard ? { commitGuard } : {}
			});
			if (!result.ok) {
				respond(true, result, void 0);
				return;
			}
			respond(true, {
				ok: true,
				scratch: publicCronScratch(result.scratch),
				currentRevision: result.currentRevision,
				maxBytes: CRON_JOB_SCRATCH_MAX_BYTES
			}, void 0);
		} catch (error) {
			respondInvalidCronParams(respond, "cron.scratch.set", formatErrorMessage(error));
		}
	},
	"cron.add": async ({ params, respond, context, client }) => {
		const rawParams = params;
		if (typeof rawParams?.declarationKey === "string" && rawParams.declarationKey.trim().length === 0) {
			respondInvalidCronParams(respond, "cron.add", "declarationKey must not be blank");
			return;
		}
		if (typeof rawParams?.displayName === "string" && rawParams.displayName.trim().length === 0) {
			respondInvalidCronParams(respond, "cron.add", "displayName must not be blank");
			return;
		}
		const hasEnabled = Boolean(rawParams && Object.hasOwn(rawParams, "enabled"));
		const parsedEnabled = hasEnabled ? parseBoolean(rawParams?.enabled) : void 0;
		if (hasEnabled && parsedEnabled === void 0) {
			respondInvalidCronParams(respond, "cron.add", "enabled must be a boolean");
			return;
		}
		const enabledExplicit = parsedEnabled !== void 0;
		const sessionKey = typeof params?.sessionKey === "string" ? params.sessionKey : void 0;
		let normalized;
		try {
			assertCronDeliveryInputNonBlankFields(params?.delivery);
			normalized = normalizeCronJobCreate(params, { sessionContext: { sessionKey } }) ?? params;
		} catch (err) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid cron.add params: ${formatErrorMessage(err)}`));
			return;
		}
		const candidate = normalized;
		if (!assertValidParams(candidate, validateCronAddParams, "cron.add", respond)) return;
		const callerScope = readCronCallerScope(client);
		const operatorActor = callerScope ? void 0 : resolveOperatorSessionCreation(client).actor;
		const creatorSession = callerScope?.sessionKey ? loadGatewaySessionEntryReadOnly(callerScope.sessionKey, { agentId: callerScope.agentId }).entry : void 0;
		const actor = operatorActor ?? creatorSession?.createdActor;
		const actorId = normalizeOptionalString(actor?.id);
		const createdActor = actor ? {
			...actor,
			...actorId ? { id: actorId } : {}
		} : void 0;
		let captureRuntimeAuthority;
		try {
			captureRuntimeAuthority = resolveCronCreatorAuthorityCapture(callerScope);
		} catch (err) {
			respondInvalidCronParams(respond, "cron.add", formatErrorMessage(err));
			return;
		}
		const assertMutationCurrent = resolveCronMutationCommitGuard(client, context);
		const selectionIdentity = JSON.stringify(creatorSession?.skillLibrarySelections);
		const commitGuard = () => {
			assertMutationCurrent?.();
			if (creatorSession && callerScope?.sessionKey) {
				const latest = loadGatewaySessionEntryReadOnly(callerScope.sessionKey, { agentId: callerScope.agentId }).entry;
				if (latest?.sessionId !== creatorSession.sessionId || latest.lifecycleRevision !== creatorSession.lifecycleRevision || JSON.stringify(latest.skillLibrarySelections) !== selectionIdentity) throw new Error("Creator session changed before scheduling; retry from the current turn.");
			}
		};
		const jobCreate = applyCronCreateCallerScopeDefault(candidate, callerScope);
		const cfg = context.getRuntimeConfig();
		try {
			assertCronDoesNotTargetAgentHarness(jobCreate);
		} catch (err) {
			respondInvalidCronParams(respond, "cron.add", formatErrorMessage(err));
			return;
		}
		if (!cronCreateMatchesCallerScope({
			job: jobCreate,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId()
		})) {
			respondInvalidCronParams(respond, "cron.add", "job agentId outside caller scope");
			return;
		}
		if (requiresExplicitAgentRuntimeToolsAllow({
			job: jobCreate,
			callerScope
		})) {
			respondInvalidCronParams(respond, "cron.add", "agent-runtime tool jobs require an explicit payload.toolsAllow cap");
			return;
		}
		const timestampValidation = validateScheduleTimestamp(jobCreate.schedule);
		if (!timestampValidation.ok) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, timestampValidation.message));
			return;
		}
		try {
			await assertValidCronCreateDelivery(cfg, jobCreate);
		} catch (err) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid cron.add params: ${formatErrorMessage(err)}`));
			return;
		}
		const deliveryPreview = await resolveCronDeliveryPreview({
			cfg,
			defaultAgentId: context.cron.getDefaultAgentId(),
			job: jobCreate
		});
		let result;
		try {
			result = await context.cron.add(jobCreate, {
				enabledExplicit,
				...createdActor ? { createdActor } : {},
				...creatorSession?.skillLibrarySelections ? { skillLibrarySelections: creatorSession.skillLibrarySelections } : {},
				commitGuard,
				...captureRuntimeAuthority ? { captureRuntimeAuthority } : {},
				matchesExisting: (job) => cronJobMatchesDeclarationScope({
					job,
					input: jobCreate,
					callerScope,
					defaultAgentId: context.cron.getDefaultAgentId()
				}),
				...cronJobUsesToolRuntime(jobCreate) ? {
					scheduledToolPolicy: resolveCronScheduledToolPolicyForCaller(callerScope),
					...callerScope?.toolsAllowProvenance ? { toolsAllowProvenance: callerScope.toolsAllowProvenance } : {},
					...callerScope?.toolsAllowExecTarget ? { toolsAllowExecTarget: callerScope.toolsAllowExecTarget } : {}
				} : {}
			});
		} catch (err) {
			if (!(err instanceof TypeError) && !(err instanceof RangeError) && !isCronInvalidRequestError(err)) throw err;
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid cron.add params: ${formatErrorMessage(err)}`));
			return;
		}
		const job = "job" in result ? result.job : result;
		context.logGateway.info("cron: job added", {
			jobId: job.id,
			declarationKey: job.declarationKey,
			schedule: jobCreate.schedule
		});
		respond(true, cronAddPayloadWithDeliveryPreview({
			result,
			deliveryPreview
		}), void 0);
	},
	"cron.update": async ({ params, respond, context, client }) => {
		let normalizedPatch;
		try {
			const rawPatch = params?.patch;
			const rawDisplayName = rawPatch && typeof rawPatch === "object" ? rawPatch.displayName : void 0;
			if (typeof rawDisplayName === "string" && rawDisplayName.trim().length === 0) throw new Error("displayName must not be blank");
			assertCronDeliveryInputNonBlankFields(rawPatch && typeof rawPatch === "object" ? rawPatch.delivery : void 0);
			normalizedPatch = normalizeCronJobPatch(rawPatch);
		} catch (err) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid cron.update params: ${formatErrorMessage(err)}`));
			return;
		}
		const candidate = normalizedPatch && typeof params === "object" && params !== null ? {
			...params,
			patch: normalizedPatch
		} : params;
		if (!assertValidParams(candidate, validateCronUpdateParams, "cron.update", respond)) return;
		if (!normalizedPatch) {
			respondInvalidCronParams(respond, "cron.update", "patch did not normalize");
			return;
		}
		const p = candidate;
		const callerScope = readCronCallerScope(client);
		let captureRuntimeAuthority;
		try {
			captureRuntimeAuthority = resolveCronCreatorAuthorityCapture(callerScope);
		} catch (err) {
			respondInvalidCronParams(respond, "cron.update", formatErrorMessage(err));
			return;
		}
		const commitGuard = resolveCronMutationCommitGuard(client, context);
		const jobId = resolveCronJobId(p);
		if (!jobId) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid cron.update params: missing id"));
			return;
		}
		const patch = normalizedPatch;
		const cfg = context.getRuntimeConfig();
		const currentJob = await context.cron.readJob(jobId);
		if (!currentJob || !cronJobMatchesCallerScope({
			job: currentJob,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId()
		})) {
			respondCronJobNotFound(respond, jobId);
			return;
		}
		if (callerScope && !callerScope.manageAll && "agentId" in patch) {
			respondInvalidCronParams(respond, "cron.update", "agentId cannot be changed by caller scope");
			return;
		}
		if (!cronPatchSessionRefsMatchCaller(patch, callerScope)) {
			respondInvalidCronParams(respond, "cron.update", "session target outside caller scope");
			return;
		}
		if (patch.schedule) {
			const timestampValidation = validateScheduleTimestamp(patch.schedule);
			if (!timestampValidation.ok) {
				respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, timestampValidation.message));
				return;
			}
		}
		const touchesToolRuntime = cronPatchTouchesToolRuntime(patch);
		const validateUpdate = async (jobToUpdate) => {
			const nextJob = await assertValidCronUpdatePatch({
				cfg,
				defaultAgentId: context.cron.getDefaultAgentId(),
				currentJob: jobToUpdate,
				patch
			});
			if (touchesToolRuntime && requiresExplicitAgentRuntimeToolsAllow({
				job: nextJob,
				callerScope
			})) throw new TypeError("agent-runtime tool jobs require an explicit payload.toolsAllow cap");
		};
		try {
			await validateUpdate(currentJob);
		} catch (err) {
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid cron.update params: ${formatErrorMessage(err)}`));
			return;
		}
		let job;
		try {
			job = await context.cron.updateWithPrecondition(jobId, patch, async (lockedJob) => {
				if (!cronJobMatchesCallerScope({
					job: lockedJob,
					callerScope,
					defaultAgentId: context.cron.getDefaultAgentId()
				})) throw new Error(`unknown cron job id: ${jobId}`);
				if (p.expectedConfigRevision !== void 0) {
					const actualConfigRevision = resolveCronJobConfigRevision(lockedJob);
					if (actualConfigRevision !== p.expectedConfigRevision) throw new CronJobConfigRevisionConflictError(p.expectedConfigRevision, actualConfigRevision);
				}
				await validateUpdate(lockedJob);
			}, touchesToolRuntime || commitGuard || captureRuntimeAuthority ? {
				...touchesToolRuntime ? {
					scheduledToolPolicy: callerScope?.manageAll ? void 0 : resolveCronScheduledToolPolicyForCaller(callerScope),
					toolsAllowProvenance: callerScope?.toolsAllowProvenance,
					toolsAllowExecTarget: callerScope?.toolsAllowExecTarget
				} : {},
				...commitGuard ? { commitGuard } : {},
				...captureRuntimeAuthority ? { captureRuntimeAuthority } : {}
			} : void 0);
		} catch (err) {
			if (err instanceof CronJobConfigRevisionConflictError) {
				respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "cron job definition no longer matches the loaded version; review the latest version before retrying", { details: {
					code: "CRON_JOB_CHANGED",
					expectedConfigRevision: err.expectedConfigRevision,
					actualConfigRevision: err.actualConfigRevision
				} }));
				return;
			}
			if (!(err instanceof TypeError) && !(err instanceof RangeError) && !isCronInvalidRequestError(err)) throw err;
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, `invalid cron.update params: ${formatErrorMessage(err)}`));
			return;
		}
		context.logGateway.info("cron: job updated", { jobId });
		respond(true, cronJobReadView(job), void 0);
	},
	"cron.remove": async ({ params, respond, context, client }) => {
		if (!assertValidParams(params, validateCronRemoveParams, "cron.remove", respond)) return;
		const jobId = resolveCronJobId(params);
		if (!jobId) {
			respondMissingCronJobId(respond, "cron.remove");
			return;
		}
		const callerScope = readCronCallerScope(client);
		const job = await context.cron.readJob(jobId);
		if (!job || !cronJobMatchesCallerScope({
			job,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId(),
			allowCurrentJob: true
		})) {
			respondCronJobNotFound(respond, jobId);
			return;
		}
		const usesCurrentJobCapability = !cronJobMatchesCallerScope({
			job,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId()
		});
		const expectedConfigRevision = usesCurrentJobCapability ? resolveCronJobConfigRevision(job) : void 0;
		let result;
		try {
			const commitGuard = resolveCronMutationCommitGuard(client, context, {
				callerScope,
				jobId,
				allowCurrentJob: usesCurrentJobCapability,
				expectedConfigRevision
			});
			const identity = client?.internal?.agentRuntimeIdentity;
			const validateAuthority = context.validateAgentRuntimeApprovalAuthority;
			if (identity && validateAuthority && commitGuard && callerScope?.currentJobId === jobId) bindCronSelfRemovalCommitGuard(jobId, identity.operationalRunInstance, commitGuard, () => {
				if (!validateAuthority(identity) || readCronCallerScope(client)?.currentJobId !== jobId) throw new TypeError("cron self-removal authority is no longer active");
			});
			result = commitGuard ? await context.cron.remove(jobId, { commitGuard }) : await context.cron.remove(jobId);
		} catch (error) {
			if (error instanceof TypeError) {
				respondInvalidCronParams(respond, "cron.remove", formatErrorMessage(error));
				return;
			}
			throw error;
		}
		if (!result.removed) {
			respondCronJobNotFound(respond, jobId);
			return;
		}
		context.logGateway.info("cron: job removed", { jobId });
		respond(true, result, void 0);
	},
	"cron.run": async ({ params, respond, context, client }) => {
		if (!assertValidParams(params, validateCronRunParams, "cron.run", respond)) return;
		const p = params;
		const callerScope = readCronCallerScope(client);
		const jobId = resolveCronJobId(p);
		if (!jobId) {
			respondMissingCronJobId(respond, "cron.run");
			return;
		}
		const job = await context.cron.readJob(jobId);
		if (!job || !cronJobMatchesCallerScope({
			job,
			callerScope,
			defaultAgentId: context.cron.getDefaultAgentId()
		})) {
			respondCronJobNotFound(respond, jobId);
			return;
		}
		if (p.expectedProcessInstanceId && p.expectedProcessInstanceId !== getGatewayProcessInstanceId()) {
			respondInvalidCronParams(respond, "cron.run", "Gateway process changed after preflight");
			return;
		}
		let result;
		try {
			const commitGuard = resolveCronMutationCommitGuard(client, context, {
				callerScope,
				jobId
			});
			result = commitGuard ? await context.cron.enqueueRun(jobId, p.mode ?? "force", { commitGuard }) : await context.cron.enqueueRun(jobId, p.mode ?? "force");
		} catch (error) {
			if (error instanceof TypeError) {
				respondInvalidCronParams(respond, "cron.run", formatErrorMessage(error));
				return;
			}
			if (isInvalidCronSessionTargetIdError(error)) {
				respond(true, {
					ok: true,
					ran: false,
					reason: "invalid-spec"
				}, void 0);
				return;
			}
			if (isCronInvalidRequestError(error)) {
				respondInvalidCronParams(respond, "cron.run", formatErrorMessage(error));
				return;
			}
			throw error;
		}
		respond(true, {
			...result,
			processInstanceId: getGatewayProcessInstanceId()
		}, void 0);
	},
	"cron.runs": async ({ params, respond, context, client }) => {
		if (!assertValidParams(params, validateCronRunsParams, "cron.runs", respond)) return;
		const p = params;
		const callerScope = readCronCallerScope(client);
		const explicitScope = p.scope;
		const hasJobSelector = p.id !== void 0 || p.jobId !== void 0;
		const jobId = resolveCronJobId(p);
		const scope = explicitScope ?? (hasJobSelector ? "job" : "all");
		const cronVisibility = resolveCronSessionVisibility(client, context.getRuntimeConfig());
		if (scope === "all") {
			if (callerScope) {
				respondInvalidCronParams(respond, "cron.runs", "scope all is not allowed by caller scope");
				return;
			}
			const jobs = filterCronRunLogJobsByAgent(await context.cron.list({ includeDisabled: true }), p.agentId, context.cron.getDefaultAgentId()).filter((job) => cronJobIsVisible(job, cronVisibility, context.cron.getDefaultAgentId()));
			const jobNameById = Object.fromEntries(jobs.filter((job) => typeof job.id === "string" && typeof job.name === "string").map((job) => [job.id, job.name]));
			const visibleJobIds = new Set(jobs.map((job) => job.id));
			respond(true, readCronTaskRunHistoryPage({
				storeKey: cronStoreKey(context.cronStorePath),
				...cronRunLogPageFilters(p),
				agentId: p.agentId,
				jobNameById,
				entryFilter: cronVisibility ? (entry) => visibleJobIds.has(entry.jobId) && (!entry.sessionKey || cronVisibility(entry.sessionKey, p.agentId)) : void 0
			}), void 0);
			return;
		}
		if (!jobId) {
			respondMissingCronJobId(respond, "cron.runs");
			return;
		}
		try {
			const job = await context.cron.readJob(jobId);
			const defaultAgentId = context.cron.getDefaultAgentId();
			const matchedJob = job && filterCronRunLogJobsByAgent([job], p.agentId, defaultAgentId).length > 0 && cronJobIsVisible(job, cronVisibility, defaultAgentId) && cronJobMatchesCallerScope({
				job,
				callerScope,
				defaultAgentId,
				allowCurrentJob: true
			}) ? job : void 0;
			const storeKey = cronStoreKey(context.cronStorePath);
			if ((callerScope || p.agentId || cronVisibility) && !matchedJob || !job && readCronTaskRunHistoryPage({
				storeKey,
				jobId,
				limit: 1
			}).total === 0) {
				respondCronJobNotFound(respond, jobId);
				return;
			}
			const jobNameById = matchedJob && typeof matchedJob.name === "string" ? { [jobId]: matchedJob.name } : void 0;
			respond(true, readCronTaskRunHistoryPage({
				storeKey,
				jobId,
				...cronRunLogPageFilters(p),
				jobNameById,
				entryFilter: cronVisibility ? (entry) => !entry.sessionKey || cronVisibility(entry.sessionKey, matchedJob?.agentId) : void 0
			}), void 0);
		} catch (err) {
			if (!isInvalidCronTaskRunJobIdError(err)) throw err;
			respond(false, void 0, errorShape(ErrorCodes.INVALID_REQUEST, "invalid cron.runs params: invalid id"));
		}
	}
};
for (const [method, handler] of Object.entries(cronHandlers)) cronHandlers[method] = async (args) => {
	const identity = args.client?.internal?.agentRuntimeIdentity;
	if (!identity) return await handler(args);
	const grant = identity.cronManagementGrant;
	let succeeded = false;
	const run = async () => {
		assertActiveAgentRuntimeAuthority(args.client, args.context);
		await handler({
			...args,
			respond: (...response) => {
				if (method === "cron.list" || method === "cron.get") {
					assertActiveAgentRuntimeAuthority(args.client, args.context);
					getCronManagementAuthority(identity)?.();
				}
				succeeded = response[0];
				args.respond(...response);
			}
		});
	};
	try {
		await (grant ? withCronManagementGrant(grant, identity, method, run) : run());
	} catch (error) {
		if (!(error instanceof TypeError)) throw error;
		respondInvalidCronParams(args.respond, method, error.message);
	} finally {
		if (grant) args.context.logGateway.info("cron: admin management", {
			method,
			runId: identity.operationalRunInstance.runId,
			instanceId: identity.operationalRunInstance.instanceId,
			ok: succeeded
		});
	}
};
//#endregion
export { cronHandlers as t };