UNPKG

openclaw

Version:

Multi-channel AI gateway with extensible messaging integrations

2,457 lines 96.3 kB
import { i as resolveGlobalSingleton } from "./global-singleton-Dc_stLtU.js";
import "./src-vebZIeLe.js";
import { l as toErrorObject } from "./error-coercion-D_-xJ90S.js";
import { t as expectDefined } from "./expect-CyE8FADM.js";
import { r as asNullableRecord } from "./record-coerce-DItp3I4t.js";
import { l as normalizeOptionalString, o as normalizeLowercaseStringOrEmpty } from "./string-coerce-CIXf7egm.js";
import { r as truncateUtf16Safe } from "./utf16-slice-D_ngcYKd.js";
import { n as normalizeAgentId } from "./agent-id-CeT3w4ap.js";
import { t as AgentSelectionRequiredError } from "./agent-scope-config-DcbEhP0R.js";
import { O as parseAgentSessionKey, S as isAcpSessionKey } from "./session-key-BnWWjqNc.js";
import { t as formatErrorMessage } from "./errors-Db3Ymjlb.js";
import { S as resolveRuntimeConfigCacheKey } from "./runtime-snapshot-BaQikjTR.js";
import { t as KeyedAsyncQueue } from "./keyed-async-queue-CTreGrmR.js";
import { r as logVerbose } from "./globals-CTaGxEqj.js";
import { n as deliveryContextFromSession } from "./delivery-context.shared-CXmRgetN.js";
import { a as toAcpRuntimeError, n as AcpRuntimeError, o as withAcpRuntimeErrorBoundary, r as formatAcpErrorChain } from "./errors-CY1OFR4J.js";
import { a as identityHasStableSessionId, c as resolveRuntimeHandleIdentifiersFromIdentity, i as identityEquals, l as resolveRuntimeResumeSessionId, n as createIdentityFromHandleEvent, o as isSessionIdentityPending, r as createIdentityFromStatus, s as mergeSessionIdentity, t as createIdentityFromEnsure, u as resolveSessionIdentityFromMeta } from "./session-identity-CY5KdnMP.js";
import "./errors-Bnilofa2.js";
import { a as requireReadySessionMeta, c as resolveAcpSessionTarget, i as normalizeAcpErrorCode, l as resolveMissingMetaError, n as createUnsupportedControlError, o as resolveAcpAgentFromSessionKey, r as hasLegacyAcpIdentityProjection, s as resolveAcpSessionResolutionError, t as acpSessionActorKey } from "./manager.utils-sCXNDTX4.js";
import { a as buildRuntimeConfigOptionPairs, b as validateRuntimeOptionPatch, c as isThinkingConfigKey, f as reconcileAcceptedRuntimeOptions, g as validateRuntimeConfigOptionInput, h as runtimeOptionsEqual, i as resolveTurnTimeoutMs, l as mergeRuntimeOptions, m as resolveRuntimeOptionsFromMeta, n as awaitTurnWithTimeout, o as buildRuntimeControlSignature, p as resolveRuntimeConfigOptionKey, r as cleanupTimedOutTurn, s as inferRuntimeOptionPatchFromConfigOption, u as normalizeRuntimeOptions, v as validateRuntimeModeInput } from "./manager.turn-timeout-DfQgYwqb.js";
import { f as recordSessionHumanDirectMessage, h as recordSubagentTerminalState } from "./session-state-events-DNCKmH78.js";
import { i as isRetainedExecutionOwnerBinding } from "./execution-owner-binding-DvQsTSTS.js";
import { n as bindTaskRunExecution } from "./task-registry.store.sqlite-BK18rNh0.js";
import { t as bindTaskFlowExecution } from "./task-flow-registry.store.sqlite-Dt5miOil.js";
import { B as createNextAcpTaskBackingDetail, j as listTasksForRelatedSessionKey } from "./task-registry-BP9SCk1B.js";
import { i as failTaskRunByRunId, r as createRunningTaskRun, t as completeTaskRunByRunId, u as startTaskRunByRunId } from "./detached-task-runtime-ZA7N6H20.js";
import { n as resolveRequiredCompletionTerminalResult } from "./task-completion-contract-BJW3TUQJ.js";
import { r as requireAcpRuntimeBackend, t as getAcpRuntimeBackend } from "./registry-DXcNog2m.js";
import { c as upsertAcpSessionMeta, n as readAcpSessionEntry, t as listAcpSessionEntries } from "./session-meta-dzG4VkYo.js";
//#region src/acp/control-plane/manager.cancel-session.ts
/** Cancels either the active ACP turn or the idle runtime handle for a session. */
async function runManagerCancelSession(params) {
	const actorKey = acpSessionActorKey(params);
	const activeTurn = params.activeTurnBySession.get(actorKey);
	const expectedRunId = params.expectedRunId?.trim();
	const expectedInstanceId = params.expectedInstanceId?.trim();
	const expectedOwnerKey = params.expectedOwnerKey?.trim();
	const requireExpectedTurn = (current) => {
		if (expectedRunId && current?.requestId !== expectedRunId || expectedInstanceId && current?.instanceId !== expectedInstanceId) throw new AcpRuntimeError("ACP_TURN_FAILED", "ACP task is no longer the active run.");
		return current;
	};
	const requireExpectedOwner = () => {
		if (!expectedOwnerKey) return;
		const resolution = params.resolveSession(params);
		const entry = resolution.kind === "ready" ? resolution.entry : void 0;
		if ((entry?.spawnedBy?.trim() || entry?.parentSessionKey?.trim()) !== expectedOwnerKey) throw new AcpRuntimeError("ACP_TURN_FAILED", "ACP task owner could not be verified.");
	};
	requireExpectedTurn(activeTurn);
	if (activeTurn) {
		await cancelManagerActiveTurn({
			activeTurn,
			reason: params.reason,
			revalidate: () => {
				requireExpectedTurn(params.activeTurnBySession.get(actorKey));
				requireExpectedOwner();
			}
		});
		return;
	}
	await params.withSessionActor(params, async () => {
		requireExpectedTurn(params.activeTurnBySession.get(actorKey));
		requireExpectedOwner();
		const resolution = params.resolveSession({
			cfg: params.cfg,
			sessionKey: params.sessionKey,
			agentId: params.agentId
		});
		const resolvedMeta = requireReadySessionMeta(resolution);
		const { runtime, handle } = await params.ensureRuntimeHandle({
			cfg: params.cfg,
			sessionKey: params.sessionKey,
			agentId: params.agentId,
			meta: resolvedMeta
		});
		try {
			await cancelRuntimeHandle({
				runtime,
				handle,
				reason: params.reason
			});
			await params.setSessionState({
				cfg: params.cfg,
				sessionKey: params.sessionKey,
				agentId: params.agentId,
				state: "idle",
				clearLastError: true
			});
		} catch (error) {
			const acpError = normalizeCancelError(error);
			await params.setSessionState({
				cfg: params.cfg,
				sessionKey: params.sessionKey,
				agentId: params.agentId,
				state: "error",
				lastError: acpError.message
			});
			throw acpError;
		}
	});
}
/** Aborts and deduplicates runtime cancellation for one active manager turn. */
async function cancelManagerActiveTurn(params) {
	params.revalidate?.();
	params.activeTurn.abortController.abort();
	if (!params.activeTurn.cancelPromise) params.activeTurn.cancelPromise = params.activeTurn.runtime.cancel({
		handle: params.activeTurn.handle,
		reason: params.reason
	});
	await withAcpRuntimeErrorBoundary({
		run: async () => await params.activeTurn.cancelPromise,
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "ACP cancel failed before completion."
	});
}
async function cancelRuntimeHandle(params) {
	await withAcpRuntimeErrorBoundary({
		run: async () => await params.runtime.cancel({
			handle: params.handle,
			reason: params.reason
		}),
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "ACP cancel failed before completion."
	});
}
function normalizeCancelError(error) {
	return toAcpRuntimeError({
		error,
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "ACP cancel failed before completion."
	});
}
//#endregion
//#region src/acp/control-plane/manager.runtime-owner.ts
/** Old backends can isolate qualified keys, but silently ignore an added owner field. */
function assertAcpRuntimeOwnerSupport(runtime, target) {
	if (!parseAgentSessionKey(target.sessionKey) && runtime.ownerAwareSessions !== 1) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "This ACP backend cannot isolate bare session keys by owner. Upgrade the backend to support ownerAwareSessions version 1.", { detailCode: "SESSION_OWNER_UNSUPPORTED" });
}
/** These operator repairs must never become resume recovery, failover, or a successful reset. */
function isAcpOwnerRepairRequired(error) {
	let current = error;
	for (let depth = 0; current instanceof Error && depth < 8; depth++) {
		const detail = "detailCode" in current ? current.detailCode : void 0;
		if (detail === "SESSION_OWNER_MIGRATION_REQUIRED" || detail === "SESSION_OWNER_UNSUPPORTED") return true;
		current = current.cause;
	}
	return false;
}
function persistedAcpRuntimeHandle(target, meta) {
	const identity = resolveSessionIdentityFromMeta(meta);
	return {
		sessionKey: target.sessionKey,
		agentId: target.agentId,
		backend: meta.backend,
		runtimeSessionName: meta.runtimeSessionName,
		cwd: meta.cwd,
		acpxRecordId: identity?.acpxRecordId,
		...resolveRuntimeHandleIdentifiersFromIdentity(identity)
	};
}
//#endregion
//#region src/acp/control-plane/manager.runtime-resume-state.ts
/** Recovery helpers for stale ACP persistent session ids and early runtime exits. */
/** Detects acpx exits that are safe to retry with a fresh runtime handle. */
function isRecoverableManagerAcpxExitError(message) {
	return /^acpx exited with (code \d+|signal [a-z0-9]+)/i.test(message.trim());
}
/** acpx detail code for a persistent session that can no longer be resumed and must be re-created. */
const SESSION_RESUME_REQUIRED_DETAIL_CODE = "SESSION_RESUME_REQUIRED";
/**
* Detects a "persistent session can no longer be resumed" failure by acpx's
* structured detail code, on the error itself or anywhere in its cause chain.
* Keying on the structured code rather than the human reason text is what makes
* recovery independent of the backend's wording — Claude reports "Resource not
* found", Kiro reports "Internal error" (RequestError -32603), but both wrap a
* SessionResumeRequiredError; matching the reason text missed Kiro and left the
* thread permanently stuck (#87830).
*/
function isRecoverableMissingManagerPersistentSessionError(error) {
	let current = error;
	for (let depth = 0; current && depth < 8; depth += 1) {
		if (current.detailCode === SESSION_RESUME_REQUIRED_DETAIL_CODE) return true;
		current = current.cause;
	}
	return false;
}
/** Prepares a one-time fresh-handle retry only before authoritative prompt submission. */
async function prepareFreshManagerRuntimeHandleRetry(params) {
	if (isAcpOwnerRepairRequired(params.error) || params.attempt > 0 || params.promptStarted || params.sawTurnOutput) return false;
	if (isRecoverableManagerAcpxExitError(params.error.message)) {
		params.runtimeHandles.clear(params);
		logVerbose(`acp-manager: retrying ${params.sessionKey} with a fresh runtime handle after early turn failure: ${params.error.message}`);
		return true;
	}
	if (!params.runtime || !params.meta || params.meta.mode !== "persistent" || !isRecoverableMissingManagerPersistentSessionError(params.error)) return false;
	if (params.runtime.prepareFreshSession) try {
		await params.runtime.prepareFreshSession({
			persistedHandle: persistedAcpRuntimeHandle(params, params.meta),
			sessionKey: params.sessionKey,
			agentId: params.agentId
		});
	} catch (error) {
		if (isAcpOwnerRepairRequired(error)) throw error;
		logVerbose(`acp-manager: failed preparing a fresh persistent session for ${params.sessionKey}: ${formatErrorMessage(error)}`);
		return false;
	}
	if (!await clearPersistedRuntimeResumeState({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		writeSessionMeta: params.writeSessionMeta
	})) return false;
	params.runtimeHandles.clear(params);
	logVerbose(`acp-manager: retrying ${params.sessionKey} with a fresh persistent session after missing backend resume target: ${params.error.message}`);
	return true;
}
async function clearPersistedRuntimeResumeState(params) {
	const now = Date.now();
	if (!await params.writeSessionMeta({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		mutate: (current, entry) => {
			if (!entry) return null;
			const base = current;
			if (!base) return null;
			const currentIdentity = resolveSessionIdentityFromMeta(base);
			if (!currentIdentity?.acpxSessionId && !currentIdentity?.agentSessionId) return base;
			const nextIdentity = {
				state: "pending",
				...currentIdentity.acpxRecordId ? { acpxRecordId: currentIdentity.acpxRecordId } : {},
				source: currentIdentity.source,
				lastUpdatedAt: now
			};
			return {
				backend: base.backend,
				agent: base.agent,
				runtimeSessionName: base.runtimeSessionName,
				identity: nextIdentity,
				mode: base.mode,
				...base.runtimeOptions ? { runtimeOptions: base.runtimeOptions } : {},
				...base.cwd ? { cwd: base.cwd } : {},
				state: base.state,
				lastActivityAt: now,
				...base.lastError ? { lastError: base.lastError } : {}
			};
		}
	})) {
		logVerbose(`acp-manager: unable to clear persisted runtime resume state for ${params.sessionKey}`);
		return false;
	}
	return true;
}
/** Clears persisted runtime resume identifiers while preserving the manager session shell. */
async function discardPersistedManagerRuntimeState(params) {
	const now = Date.now();
	await params.writeSessionMeta({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		mutate: (current, entry) => {
			if (!entry) return null;
			const base = current;
			if (!base) return null;
			const currentIdentity = resolveSessionIdentityFromMeta(base);
			const nextIdentity = currentIdentity ? {
				state: "pending",
				...currentIdentity.acpxRecordId ? { acpxRecordId: currentIdentity.acpxRecordId } : {},
				source: currentIdentity.source,
				lastUpdatedAt: now
			} : void 0;
			return {
				backend: base.backend,
				agent: base.agent,
				runtimeSessionName: base.runtimeSessionName,
				...nextIdentity ? { identity: nextIdentity } : {},
				mode: base.mode,
				...base.runtimeOptions ? { runtimeOptions: base.runtimeOptions } : {},
				...base.cwd ? { cwd: base.cwd } : {},
				state: "idle",
				lastActivityAt: now
			};
		},
		failOnError: true
	});
}
/**
* Best-effort fresh-session preparation against a maybe-missing backend.
* Every non-applied path records why it was skipped: a reset that silently
* skips this step looks successful while the backend keeps resuming the old
* conversation, which is the worst failure mode for session resets.
*/
async function tryPrepareFreshManagerRuntimeSession(params) {
	const configuredBackend = (params.meta.backend || params.cfg.acp?.backend || "").trim();
	try {
		const backend = params.deps.getRuntimeBackend(configuredBackend || void 0);
		if (!backend) {
			if (params.missingBackendError) throw toErrorObject(params.missingBackendError, "Non-Error thrown");
			logVerbose(`${params.logPrefix}: fresh-session preparation skipped for ${params.sessionKey}: ACP backend "${configuredBackend || "(default)"}" is not registered`);
			return;
		}
		assertAcpRuntimeOwnerSupport(backend.runtime, params);
		if (!backend.runtime.prepareFreshSession) {
			logVerbose(`${params.logPrefix}: fresh-session preparation skipped for ${params.sessionKey}: ACP backend "${backend.id}" does not support prepareFreshSession`);
			return;
		}
		await backend.runtime.prepareFreshSession({
			persistedHandle: persistedAcpRuntimeHandle(params, params.meta),
			sessionKey: params.sessionKey,
			agentId: params.agentId
		});
	} catch (error) {
		if (isAcpOwnerRepairRequired(error)) throw error;
		logVerbose(`${params.logPrefix}: unable to prepare fresh session for ${params.sessionKey}: ${formatErrorMessage(error)}`);
	}
}
//#endregion
//#region src/acp/control-plane/manager.close-session.ts
/** Close/reset path for ACP runtime sessions and persisted manager metadata. */
/** Closes an ACP session runtime handle and optionally discards persistent state/meta. */
async function runManagerCloseSession(params) {
	const { input, sessionKey, agentId } = params;
	const resolution = params.resolveSession({
		cfg: input.cfg,
		sessionKey,
		agentId
	});
	const resolutionError = resolveAcpSessionResolutionError(resolution);
	if (resolutionError) {
		if (input.requireAcpSession ?? true) throw resolutionError;
		return {
			runtimeClosed: false,
			metaCleared: false
		};
	}
	const meta = requireReadySessionMeta(resolution);
	const currentIdentity = resolveSessionIdentityFromMeta(meta);
	const shouldSkipRuntimeClose = input.discardPersistentState && currentIdentity != null && !identityHasStableSessionId(currentIdentity);
	let runtimeClosed = false;
	let runtimeNotice;
	if (shouldSkipRuntimeClose) {
		await tryPrepareFreshManagerRuntimeSession({
			deps: params.deps,
			cfg: input.cfg,
			meta,
			sessionKey,
			agentId,
			logPrefix: "acp close fast-reset"
		});
		params.runtimeHandles.clear(params);
	} else try {
		const { runtime: ensuredRuntime, handle } = await params.ensureRuntimeHandle({
			cfg: input.cfg,
			sessionKey,
			agentId,
			meta
		});
		await withAcpRuntimeErrorBoundary({
			run: async () => await ensuredRuntime.close({
				handle,
				reason: input.reason,
				discardPersistentState: input.discardPersistentState
			}),
			fallbackCode: "ACP_TURN_FAILED",
			fallbackMessage: "ACP close failed before completion."
		});
		runtimeClosed = true;
		params.runtimeHandles.clear(params);
	} catch (error) {
		const acpError = toAcpRuntimeError({
			error,
			fallbackCode: "ACP_TURN_FAILED",
			fallbackMessage: "ACP close failed before completion."
		});
		if (!isAcpOwnerRepairRequired(acpError) && input.allowBackendUnavailable && (acpError.code === "ACP_BACKEND_MISSING" || acpError.code === "ACP_BACKEND_UNAVAILABLE" || input.discardPersistentState && acpError.code === "ACP_SESSION_INIT_FAILED" || input.discardPersistentState && acpError.code === "ACP_BACKEND_UNSUPPORTED_CONTROL" || isRecoverableManagerAcpxExitError(acpError.message))) {
			if (input.discardPersistentState) await tryPrepareFreshManagerRuntimeSession({
				deps: params.deps,
				cfg: input.cfg,
				meta,
				sessionKey,
				agentId,
				logPrefix: "acp close recovery",
				missingBackendError: acpError
			});
			params.runtimeHandles.clear(params);
			runtimeNotice = acpError.message;
		} else throw acpError;
	}
	let metaCleared = false;
	if (input.discardPersistentState && !input.clearMeta) await discardPersistedManagerRuntimeState({
		cfg: input.cfg,
		sessionKey,
		agentId,
		writeSessionMeta: params.writeSessionMeta
	});
	if (input.clearMeta) {
		await params.writeSessionMeta({
			cfg: input.cfg,
			sessionKey,
			agentId,
			mutate: (_current, entry) => {
				if (!entry) return null;
				return null;
			},
			failOnError: true
		});
		metaCleared = true;
	}
	return {
		runtimeClosed,
		runtimeNotice,
		metaCleared
	};
}
//#endregion
//#region src/acp/control-plane/manager.identity-reconcile.ts
/** Reconciles ACP runtime identity observations back into persisted session metadata. */
/** Reconciles runtime-reported session identifiers into persisted ACP session metadata. */
async function reconcileManagerRuntimeSessionIdentifiers(params) {
	let runtimeStatus = params.runtimeStatus;
	if (!runtimeStatus && params.runtime.getStatus) try {
		runtimeStatus = await withAcpRuntimeErrorBoundary({
			run: async () => await params.runtime.getStatus({ handle: params.handle }),
			fallbackCode: "ACP_TURN_FAILED",
			fallbackMessage: "Could not read ACP runtime status."
		});
	} catch (error) {
		if (params.failOnStatusError || isAcpOwnerRepairRequired(error)) throw error;
		logVerbose(`acp-manager: failed to refresh ACP runtime status for ${params.sessionKey}: ${String(error)}`);
		return {
			handle: params.handle,
			meta: params.meta,
			runtimeStatus
		};
	}
	const now = Date.now();
	const currentIdentity = resolveSessionIdentityFromMeta(params.meta);
	const eventIdentity = createIdentityFromHandleEvent({
		handle: params.handle,
		now
	});
	const identityAfterEvent = mergeSessionIdentity({
		current: currentIdentity,
		incoming: eventIdentity,
		now
	}) ?? currentIdentity;
	const nextIdentity = mergeSessionIdentity({
		current: identityAfterEvent,
		incoming: createIdentityFromStatus({
			status: runtimeStatus,
			now
		}),
		now
	}) ?? identityAfterEvent;
	const handleIdentifiers = resolveRuntimeHandleIdentifiersFromIdentity(nextIdentity);
	const handleChanged = handleIdentifiers.backendSessionId !== params.handle.backendSessionId || handleIdentifiers.agentSessionId !== params.handle.agentSessionId;
	const nextHandle = handleChanged ? {
		...params.handle,
		...handleIdentifiers.backendSessionId ? { backendSessionId: handleIdentifiers.backendSessionId } : {},
		...handleIdentifiers.agentSessionId ? { agentSessionId: handleIdentifiers.agentSessionId } : {}
	} : params.handle;
	if (handleChanged) params.setCachedHandle(params, nextHandle);
	if (!(!identityEquals(currentIdentity, nextIdentity) || hasLegacyAcpIdentityProjection(params.meta))) return {
		handle: nextHandle,
		meta: params.meta,
		runtimeStatus
	};
	const nextMeta = {
		backend: params.meta.backend,
		agent: params.meta.agent,
		runtimeSessionName: params.meta.runtimeSessionName,
		...nextIdentity ? { identity: nextIdentity } : {},
		mode: params.meta.mode,
		...params.meta.runtimeOptions ? { runtimeOptions: params.meta.runtimeOptions } : {},
		...params.meta.cwd ? { cwd: params.meta.cwd } : {},
		lastActivityAt: now,
		state: params.meta.state,
		...params.meta.lastError ? { lastError: params.meta.lastError } : {}
	};
	if (!identityEquals(currentIdentity, nextIdentity)) {
		const currentAgentSessionId = currentIdentity?.agentSessionId ?? "<none>";
		const nextAgentSessionId = nextIdentity?.agentSessionId ?? "<none>";
		const currentAcpxSessionId = currentIdentity?.acpxSessionId ?? "<none>";
		const nextAcpxSessionId = nextIdentity?.acpxSessionId ?? "<none>";
		const currentAcpxRecordId = currentIdentity?.acpxRecordId ?? "<none>";
		const nextAcpxRecordId = nextIdentity?.acpxRecordId ?? "<none>";
		logVerbose(`acp-manager: session identity updated for ${params.sessionKey} (agentSessionId ${currentAgentSessionId} -> ${nextAgentSessionId}, acpxSessionId ${currentAcpxSessionId} -> ${nextAcpxSessionId}, acpxRecordId ${currentAcpxRecordId} -> ${nextAcpxRecordId})`);
	}
	await params.writeSessionMeta({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		mutate: (current, entry) => {
			if (!entry) return null;
			const base = current;
			if (!base) return null;
			return {
				backend: base.backend,
				agent: base.agent,
				runtimeSessionName: base.runtimeSessionName,
				...nextIdentity ? { identity: nextIdentity } : {},
				mode: base.mode,
				...base.runtimeOptions ? { runtimeOptions: base.runtimeOptions } : {},
				...base.cwd ? { cwd: base.cwd } : {},
				state: base.state,
				lastActivityAt: now,
				...base.lastError ? { lastError: base.lastError } : {}
			};
		}
	});
	return {
		handle: nextHandle,
		meta: nextMeta,
		runtimeStatus
	};
}
//#endregion
//#region src/acp/control-plane/manager.initialize-session.ts
/** Session initialization path for ACP runtime handles and persisted manager metadata. */
/** Initializes an ACP runtime session and persists its metadata before caching the handle. */
async function runManagerInitializeSession(params) {
	const { input, sessionKey, agentId } = params;
	const backend = params.deps.requireRuntimeBackend(input.backendId || input.cfg.acp?.backend);
	const runtime = backend.runtime;
	assertAcpRuntimeOwnerSupport(runtime, params);
	const agent = normalizeAgentId(input.agent);
	const initialRuntimeOptions = validateRuntimeOptionPatch({
		...input.runtimeOptions,
		...input.cwd !== void 0 ? { cwd: input.cwd } : {}
	});
	const requestedCwd = initialRuntimeOptions.cwd;
	const requestedModel = initialRuntimeOptions.model;
	const requestedThinking = initialRuntimeOptions.thinking;
	const previousMeta = params.deps.loadSessionEntry({
		cfg: input.cfg,
		sessionKey,
		agentId
	})?.acp;
	input.assertActive?.();
	const handle = {
		...await withAcpRuntimeErrorBoundary({
			run: async () => await runtime.ensureSession({
				sessionKey,
				agentId,
				persistedHandle: previousMeta?.backend === backend.id ? persistedAcpRuntimeHandle(params, previousMeta) : void 0,
				agent,
				mode: input.mode,
				resumeSessionId: input.resumeSessionId,
				...requestedModel ? { model: requestedModel } : {},
				...requestedModel && input.modelExplicit ? { modelExplicit: true } : {},
				...requestedThinking ? { thinking: requestedThinking } : {},
				cwd: requestedCwd
			}),
			fallbackCode: "ACP_SESSION_INIT_FAILED",
			fallbackMessage: "Could not initialize ACP session runtime."
		}),
		agentId,
		sessionKey
	};
	const effectiveCwd = normalizeOptionalString(handle.cwd) ?? requestedCwd;
	const effectiveModel = resolveEffectiveSessionModel({
		requestedModel,
		appliedModel: handle.appliedModel
	});
	const effectiveRuntimeOptions = normalizeRuntimeOptions({
		...initialRuntimeOptions,
		model: effectiveModel,
		...effectiveCwd ? { cwd: effectiveCwd } : {}
	});
	const identityNow = Date.now();
	const initializedIdentity = mergeSessionIdentity({
		current: void 0,
		incoming: createIdentityFromEnsure({
			handle,
			now: identityNow
		}),
		now: identityNow
	}) ?? {
		state: "pending",
		source: "ensure",
		lastUpdatedAt: identityNow
	};
	const meta = {
		backend: handle.backend || backend.id,
		agent,
		runtimeSessionName: handle.runtimeSessionName,
		identity: initializedIdentity,
		mode: input.mode,
		...Object.keys(effectiveRuntimeOptions).length > 0 ? { runtimeOptions: effectiveRuntimeOptions } : {},
		cwd: effectiveCwd,
		state: "idle",
		lastActivityAt: Date.now()
	};
	const persisted = await persistInitializedSessionMeta({
		cfg: input.cfg,
		sessionKey,
		agentId,
		meta,
		runtime,
		handle,
		writeSessionMeta: params.writeSessionMeta,
		assertCommitAllowed: input.assertActive
	});
	if (!persisted?.acp) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", `Could not persist ACP metadata for ${sessionKey}.`);
	params.runtimeHandles.set(params, {
		runtime,
		handle,
		backend: handle.backend || backend.id,
		agent,
		mode: input.mode,
		cwd: effectiveCwd,
		configSignature: resolveRuntimeConfigCacheKey(input.cfg)
	});
	return {
		runtime,
		handle,
		meta,
		sessionEntry: persisted
	};
}
function resolveEffectiveSessionModel(params) {
	const { appliedModel } = params;
	if (!appliedModel) return params.requestedModel;
	return appliedModel.kind === "applied" ? appliedModel.model : void 0;
}
async function persistInitializedSessionMeta(params) {
	try {
		const persisted = await params.writeSessionMeta({
			cfg: params.cfg,
			sessionKey: params.sessionKey,
			agentId: params.agentId,
			mutate: () => params.meta,
			failOnError: true,
			assertCommitAllowed: params.assertCommitAllowed
		});
		if (persisted?.acp) return persisted;
	} catch (error) {
		await closeRuntimeAfterInitMetaFailure(params);
		throw error;
	}
	await closeRuntimeAfterInitMetaFailure(params);
	return null;
}
async function closeRuntimeAfterInitMetaFailure(params) {
	await params.runtime.close({
		handle: params.handle,
		reason: "init-meta-failed"
	}).catch((closeError) => {
		logVerbose(`acp-manager: cleanup close failed after metadata write error for ${params.sessionKey}: ${String(closeError)}`);
	});
}
//#endregion
//#region src/acp/control-plane/manager.lifecycle.ts
const ACP_SESSION_MANAGER_DISPOSERS = /* @__PURE__ */ new WeakMap();
function registerAcpSessionManagerDisposer(manager, dispose) {
	ACP_SESSION_MANAGER_DISPOSERS.set(manager, dispose);
}
/** Stops active turns and closes process-local handles without widening the public manager API. */
async function disposeAcpSessionManagerInstance(manager, reason) {
	const dispose = ACP_SESSION_MANAGER_DISPOSERS.get(manager);
	if (!dispose) throw new Error("ACP session manager disposer unavailable");
	await dispose(reason);
}
//#endregion
//#region src/acp/control-plane/manager.runtime-controls.ts
const OPTIONAL_TIMEOUT_CONFIG_KEYS = /* @__PURE__ */ new Set(["timeout", "timeout_seconds"]);
const ACP_CONFIG_REJECTION_CODE_RE = /-3260[23]/;
const CONFIG_OPTION_REJECTION_RE = /invalid params|unsupported|not supported|not implement|invalid value|unknown config option|unknown value|not a valid value|must be one of/;
function extractConfigOptionKeys(value) {
	if (!Array.isArray(value)) return [];
	return value.map((entry) => {
		if (typeof entry === "string") return normalizeOptionalString(entry);
		const record = asNullableRecord(entry);
		return normalizeOptionalString(record?.id ?? record?.key);
	}).filter(Boolean);
}
function extractRuntimeStatusConfigOptionKeys(status) {
	const details = asNullableRecord(status?.details);
	return [...extractConfigOptionKeys(details?.configOptions), ...extractConfigOptionKeys(details?.config_options)];
}
function isOptionalTimeoutConfigKey(key) {
	return OPTIONAL_TIMEOUT_CONFIG_KEYS.has(normalizeLowercaseStringOrEmpty(key));
}
function isUnsupportedControlRejection(error) {
	return (error && typeof error === "object" ? error.code : null) === "ACP_BACKEND_UNSUPPORTED_CONTROL";
}
function describeConfigOptionRejection(error) {
	const described = toAcpRuntimeError({
		error,
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: ""
	});
	return normalizeLowercaseStringOrEmpty(`${formatAcpErrorChain(error)} ${described.message}`);
}
function isUnsupportedOptionalTimeoutConfigRejection(key, error) {
	if (!isOptionalTimeoutConfigKey(key)) return false;
	if (isUnsupportedControlRejection(error)) return true;
	const message = error instanceof Error ? error.message : typeof error === "string" ? error : String(error);
	const normalized = normalizeLowercaseStringOrEmpty(message);
	return normalized.includes("session/set_config_option") && (normalized.includes("-32602") || normalized.includes("invalid params") || normalized.includes("unsupported") || normalized.includes("not supported") || normalized.includes("not implement"));
}
function isRejectedThinkingConfigOption(key, error) {
	if (!isThinkingConfigKey(key)) return false;
	if (isUnsupportedControlRejection(error)) return true;
	const description = describeConfigOptionRejection(error);
	return (description.includes("session/set_config_option") || description.includes("config option") && description.includes(normalizeLowercaseStringOrEmpty(key))) && ACP_CONFIG_REJECTION_CODE_RE.test(description) && CONFIG_OPTION_REJECTION_RE.test(description);
}
/** Resolves backend-advertised controls plus locally inferred runtime control support. */
async function resolveManagerRuntimeCapabilities(params) {
	let reported;
	if (params.runtime.getCapabilities) reported = await withAcpRuntimeErrorBoundary({
		run: async () => await params.runtime.getCapabilities({ handle: params.handle }),
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "Could not read ACP runtime capabilities."
	});
	const controls = new Set(reported?.controls ?? []);
	if (params.runtime.setMode) controls.add("session/set_mode");
	if (params.runtime.setConfigOption) controls.add("session/set_config_option");
	if (params.runtime.getStatus) controls.add("session/status");
	const normalizedKeys = new Set((reported?.configOptionKeys ?? []).map((entry) => normalizeOptionalString(entry)).filter(Boolean));
	if (normalizedKeys.size === 0 && params.includeStatusConfigOptionKeys && params.runtime.getStatus) try {
		const status = await params.runtime.getStatus({ handle: params.handle });
		for (const key of extractRuntimeStatusConfigOptionKeys(status)) normalizedKeys.add(key);
	} catch (error) {
		if (isAcpOwnerRepairRequired(error)) throw error;
	}
	return {
		controls: [...controls].toSorted(),
		...normalizedKeys.size > 0 ? { configOptionKeys: [...normalizedKeys] } : {}
	};
}
/** Applies persisted runtime options to a live handle once per unique option signature. */
async function applyManagerRuntimeControls(params) {
	let options = resolveRuntimeOptionsFromMeta(params.meta);
	const signature = buildRuntimeControlSignature(options);
	const cached = params.getCachedRuntimeState(params.sessionKey);
	if (cached?.appliedControlSignature === signature) return;
	const needsConfigOptionKeys = buildRuntimeConfigOptionPairs(options).length > 0;
	const capabilities = await resolveManagerRuntimeCapabilities({
		runtime: params.runtime,
		handle: params.handle,
		includeStatusConfigOptionKeys: needsConfigOptionKeys
	});
	const backend = params.handle.backend || params.meta.backend;
	const runtimeMode = normalizeOptionalString(options.runtimeMode);
	const configOptions = buildRuntimeConfigOptionPairs(options, capabilities.configOptionKeys);
	const thinkingConfigKey = options.thinking ? resolveRuntimeConfigOptionKey("thinking", capabilities.configOptionKeys) : void 0;
	const advertisedKeys = new Set((capabilities.configOptionKeys ?? []).map((entry) => normalizeLowercaseStringOrEmpty(entry)).filter(Boolean));
	await withAcpRuntimeErrorBoundary({
		run: async () => {
			if (runtimeMode) {
				if (!capabilities.controls.includes("session/set_mode") || !params.runtime.setMode) throw createUnsupportedControlError({
					backend,
					control: "session/set_mode"
				});
				await params.runtime.setMode({
					handle: params.handle,
					mode: runtimeMode
				});
			}
			if (configOptions.length > 0) {
				if (!capabilities.controls.includes("session/set_config_option") || !params.runtime.setConfigOption) throw createUnsupportedControlError({
					backend,
					control: "session/set_config_option"
				});
				for (const [key, requestedValue] of configOptions) {
					const value = key === thinkingConfigKey ? options.thinking : requestedValue;
					if (value === void 0) continue;
					if (advertisedKeys.size > 0 && !advertisedKeys.has(normalizeLowercaseStringOrEmpty(key))) throw new AcpRuntimeError("ACP_BACKEND_UNSUPPORTED_CONTROL", `ACP backend "${backend}" does not accept config key "${key}".`);
					try {
						const result = await params.runtime.setConfigOption({
							handle: params.handle,
							key,
							value
						});
						const accepted = reconcileAcceptedRuntimeOptions(options, result, normalizeLowercaseStringOrEmpty(key) === "model" ? options.thinking : void 0);
						if (!runtimeOptionsEqual(options, accepted)) {
							await params.onOptionsChanged(accepted);
							options = accepted;
						}
					} catch (error) {
						if (isUnsupportedOptionalTimeoutConfigRejection(key, error) || isRejectedThinkingConfigOption(key, error)) continue;
						throw error;
					}
				}
			}
		},
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "Could not apply ACP runtime options before turn execution."
	});
	if (cached) cached.appliedControlSignature = buildRuntimeControlSignature(options);
}
//#endregion
//#region src/acp/control-plane/manager.runtime-handle-cache.ts
/** Process-local ACP runtime handle cache with lifecycle cleanup and reuse checks. */
/** Process-local cache of live ACP runtime handles keyed by canonical session actor. */
var ManagerRuntimeHandleCache = class {
	constructor() {
		this.runtimeCache = /* @__PURE__ */ new Map();
	}
	get(target) {
		return this.runtimeCache.get(acpSessionActorKey(target)) ?? null;
	}
	set(target, state) {
		this.runtimeCache.set(acpSessionActorKey(target), state);
	}
	clear(target) {
		this.runtimeCache.delete(acpSessionActorKey(target));
	}
	/** Returns cache counters used by ACP manager observability snapshots. */
	getObservabilitySnapshot() {
		return {
			activeSessions: this.runtimeCache.size,
			idleTtlMs: 0,
			evictedTotal: 0
		};
	}
	/** Closes and removes one cached runtime handle when present. */
	async close(params) {
		const cached = this.get(params);
		if (!cached || params.expectedHandle && cached.handle !== params.expectedHandle) return;
		try {
			await cached.runtime.close({
				handle: cached.handle,
				reason: params.reason
			});
		} catch (error) {
			if (params.expectedHandle && isAcpOwnerRepairRequired(error)) throw error;
			logVerbose(`acp-manager: cached runtime close failed for ${params.sessionKey}: ${String(error)}`);
		} finally {
			this.clear(params);
		}
	}
	/** Drains every cached handle behind its session actor before process shutdown. */
	async closeAll(params) {
		await Promise.all([...this.runtimeCache.keys()].map((actorKey) => params.actorQueue.run(actorKey, async () => {
			const cached = this.runtimeCache.get(actorKey);
			if (!cached) return;
			try {
				await cached.runtime.close({
					handle: cached.handle,
					reason: params.reason
				});
			} catch (error) {
				logVerbose(`acp-manager: cached runtime close failed for ${cached.handle.sessionKey}: ${String(error)}`);
			} finally {
				this.runtimeCache.delete(actorKey);
			}
		})));
	}
	/** Clears a cached handle only when the caller still owns the same runtime identifiers. */
	clearIfHandleMatches(params) {
		const cached = this.get(params);
		if (!cached || !this.runtimeHandlesMatch(cached.handle, params.handle)) return;
		this.clear(params);
	}
	/** Checks whether a cached runtime handle is still healthy enough to reuse. */
	async isReusable(params) {
		if (!params.runtime.getStatus) return true;
		try {
			const status = await params.runtime.getStatus({ handle: params.handle });
			if (isRuntimeStatusUnavailable(status)) {
				logVerbose(`acp-manager: evicting cached runtime handle for ${params.sessionKey} after unhealthy status probe: ${status.summary ?? "status unavailable"}`);
				return false;
			}
			return true;
		} catch (error) {
			if (isAcpOwnerRepairRequired(error)) throw error;
			logVerbose(`acp-manager: evicting cached runtime handle for ${params.sessionKey} after status probe failed: ${String(error)}`);
			return false;
		}
	}
	handleMatchesMeta(params) {
		const identity = resolveSessionIdentityFromMeta(params.meta);
		const expectedHandleIds = resolveRuntimeHandleIdentifiersFromIdentity(identity);
		if ((params.handle.backendSessionId ?? "") !== (expectedHandleIds.backendSessionId ?? "")) return false;
		if ((params.handle.agentSessionId ?? "") !== (expectedHandleIds.agentSessionId ?? "")) return false;
		const expectedAcpxRecordId = identity?.acpxRecordId ?? "";
		return (normalizeOptionalString(params.handle.acpxRecordId) ?? "") === expectedAcpxRecordId;
	}
	runtimeHandlesMatch(a, b) {
		return a.sessionKey === b.sessionKey && a.agentId === b.agentId && a.backend === b.backend && a.runtimeSessionName === b.runtimeSessionName && (a.cwd ?? "") === (b.cwd ?? "") && (a.acpxRecordId ?? "") === (b.acpxRecordId ?? "") && (a.backendSessionId ?? "") === (b.backendSessionId ?? "") && (a.agentSessionId ?? "") === (b.agentSessionId ?? "");
	}
};
function isRuntimeStatusUnavailable(status) {
	if (!status) return false;
	const detailsStatus = normalizeLowercaseStringOrEmpty(status.details?.status);
	if (detailsStatus === "dead" || detailsStatus === "no-session") return true;
	const summaryMatch = status.summary?.match(/\bstatus=([^\s]+)/i);
	const summaryStatus = normalizeLowercaseStringOrEmpty(summaryMatch?.[1]);
	return summaryStatus === "dead" || summaryStatus === "no-session";
}
//#endregion
//#region src/acp/control-plane/manager.runtime-handle-ensure.ts
/** Ensures or recreates a live ACP runtime handle for persisted session metadata. */
/** Returns a reusable cached handle or initializes a fresh runtime session for the metadata. */
async function ensureManagerRuntimeHandle(params) {
	const agent = normalizeOptionalString(params.meta.agent) || resolveAcpAgentFromSessionKey(params.sessionKey, "main");
	const mode = params.meta.mode;
	const runtimeOptions = resolveRuntimeOptionsFromMeta(params.meta);
	const cwd = runtimeOptions.cwd ?? normalizeOptionalString(params.meta.cwd);
	const model = normalizeOptionalString(runtimeOptions.model);
	const thinking = normalizeOptionalString(runtimeOptions.thinking);
	const configuredBackend = (params.selectedBackend || params.meta.backend || params.cfg.acp?.backend || "").trim();
	const configSignature = resolveRuntimeConfigCacheKey(params.cfg);
	const backend = params.deps.requireRuntimeBackend(configuredBackend || void 0);
	const runtime = backend.runtime;
	assertAcpRuntimeOwnerSupport(runtime, params);
	const cached = params.runtimeHandles.get(params);
	if (cached) {
		const backendMatches = !configuredBackend || cached.backend === configuredBackend;
		const agentMatches = cached.agent === agent;
		const modeMatches = cached.mode === mode;
		const cwdMatches = (cached.cwd ?? "") === (cwd ?? "");
		const configMatches = cached.configSignature === configSignature;
		const handleMatchesMeta = params.runtimeHandles.handleMatchesMeta({
			handle: cached.handle,
			meta: params.meta
		});
		if (backendMatches && agentMatches && modeMatches && cwdMatches && configMatches && handleMatchesMeta && await params.runtimeHandles.isReusable({
			sessionKey: params.sessionKey,
			runtime: cached.runtime,
			handle: cached.handle
		})) return {
			runtime: cached.runtime,
			handle: cached.handle,
			meta: params.meta
		};
		await params.runtimeHandles.close({
			sessionKey: params.sessionKey,
			agentId: params.agentId,
			reason: "runtime-handle-replaced"
		});
	}
	const previousMeta = params.meta;
	const persistedIdentity = resolveSessionIdentityFromMeta(previousMeta);
	const backendOwnsPreviousIdentity = previousMeta.backend === backend.id;
	const previousIdentity = backendOwnsPreviousIdentity ? persistedIdentity : void 0;
	const persistedHandle = backendOwnsPreviousIdentity ? persistedAcpRuntimeHandle(params, previousMeta) : void 0;
	let identityForEnsure = previousIdentity;
	const persistedResumeSessionId = mode === "persistent" ? resolveRuntimeResumeSessionId(previousIdentity) : void 0;
	const shouldPrepareFreshPersistentSession = mode === "persistent" && previousIdentity != null && !identityHasStableSessionId(previousIdentity);
	const ensureSession = async (resumeSessionId) => await withAcpRuntimeErrorBoundary({
		run: async () => await runtime.ensureSession({
			persistedHandle,
			sessionKey: params.sessionKey,
			agentId: params.agentId,
			agent,
			mode,
			...resumeSessionId ? { resumeSessionId } : {},
			...model ? { model } : {},
			...thinking ? { thinking } : {},
			cwd
		}),
		fallbackCode: "ACP_SESSION_INIT_FAILED",
		fallbackMessage: "Could not initialize ACP session runtime."
	});
	let ensured;
	if (shouldPrepareFreshPersistentSession) await runtime.prepareFreshSession?.({
		persistedHandle,
		sessionKey: params.sessionKey,
		agentId: params.agentId
	});
	if (persistedResumeSessionId) try {
		ensured = await ensureSession(persistedResumeSessionId);
	} catch (error) {
		const acpError = toAcpRuntimeError({
			error,
			fallbackCode: "ACP_SESSION_INIT_FAILED",
			fallbackMessage: "Could not initialize ACP session runtime."
		});
		if (isAcpOwnerRepairRequired(acpError) || acpError.code !== "ACP_SESSION_INIT_FAILED") throw acpError;
		logVerbose(`acp-manager: resume init failed for ${params.sessionKey}; retrying without persisted ACP session id: ${acpError.message}`);
		if (identityForEnsure) {
			const { acpxSessionId: _staleAcpxSessionId, agentSessionId: _staleAgentSessionId, ...retryIdentity } = identityForEnsure;
			identityForEnsure = {
				...retryIdentity,
				state: "pending"
			};
		}
		ensured = await ensureSession();
	}
	else ensured = await ensureSession();
	const now = Date.now();
	const effectiveCwd = normalizeOptionalString(ensured.cwd) ?? cwd;
	const nextRuntimeOptions = normalizeRuntimeOptions({
		...runtimeOptions,
		...effectiveCwd ? { cwd: effectiveCwd } : {}
	});
	const nextIdentity = mergeSessionIdentity({
		current: identityForEnsure,
		incoming: createIdentityFromEnsure({
			handle: ensured,
			now
		}),
		now
	}) ?? identityForEnsure;
	const nextHandleIdentifiers = resolveRuntimeHandleIdentifiersFromIdentity(nextIdentity);
	const nextHandle = {
		...ensured,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		...nextHandleIdentifiers.backendSessionId ? { backendSessionId: nextHandleIdentifiers.backendSessionId } : {},
		...nextHandleIdentifiers.agentSessionId ? { agentSessionId: nextHandleIdentifiers.agentSessionId } : {}
	};
	const nextMeta = {
		backend: ensured.backend || backend.id,
		agent,
		runtimeSessionName: ensured.runtimeSessionName,
		...nextIdentity ? { identity: nextIdentity } : {},
		mode: params.meta.mode,
		...Object.keys(nextRuntimeOptions).length > 0 ? { runtimeOptions: nextRuntimeOptions } : {},
		...effectiveCwd ? { cwd: effectiveCwd } : {},
		state: previousMeta.state,
		lastActivityAt: now,
		...previousMeta.lastError ? { lastError: previousMeta.lastError } : {}
	};
	if (previousMeta.backend !== nextMeta.backend || previousMeta.runtimeSessionName !== nextMeta.runtimeSessionName || !identityEquals(persistedIdentity, nextIdentity) || previousMeta.agent !== nextMeta.agent || previousMeta.cwd !== nextMeta.cwd || !runtimeOptionsEqual(previousMeta.runtimeOptions, nextMeta.runtimeOptions) || hasLegacyAcpIdentityProjection(previousMeta)) await params.writeSessionMeta({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		mutate: (_current, entry) => {
			if (!entry) return null;
			return nextMeta;
		}
	});
	params.runtimeHandles.set(params, {
		runtime,
		handle: nextHandle,
		backend: ensured.backend || backend.id,
		agent,
		mode,
		cwd: effectiveCwd,
		configSignature,
		appliedControlSignature: void 0
	});
	return {
		runtime,
		handle: nextHandle,
		meta: nextMeta
	};
}
//#endregion
//#region src/acp/control-plane/manager.runtime-options-commands.ts
/** Applies a backend runtime mode control and persists the selected mode. */
async function runSetManagerSessionRuntimeMode(params) {
	const resolution = params.resolveSession({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId
	});
	const resolvedMeta = requireReadySessionMeta(resolution);
	const { runtime, handle, meta } = await params.ensureRuntimeHandle({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		meta: resolvedMeta
	});
	if (!(await params.resolveRuntimeCapabilities({
		runtime,
		handle
	})).controls.includes("session/set_mode") || !runtime.setMode) throw createUnsupportedControlError({
		backend: handle.backend || meta.backend,
		control: "session/set_mode"
	});
	await withAcpRuntimeErrorBoundary({
		run: async () => await runtime.setMode({
			handle,
			mode: params.runtimeMode
		}),
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "Could not update ACP runtime mode."
	});
	const nextOptions = mergeRuntimeOptions({
		current: resolveRuntimeOptionsFromMeta(meta),
		patch: { runtimeMode: params.runtimeMode }
	});
	await persistManagerRuntimeOptions({
		...params,
		options: nextOptions
	});
	return nextOptions;
}
/** Applies a backend config-option control and persists the inferred runtime option patch. */
async function runSetManagerSessionConfigOption(params) {
	const resolution = params.resolveSession({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId
	});
	const resolvedMeta = requireReadySessionMeta(resolution);
	const { runtime, handle, meta } = await params.ensureRuntimeHandle({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		meta: resolvedMeta
	});
	const inferredPatch = inferRuntimeOptionPatchFromConfigOption(params.key, params.value);
	const capabilities = await params.resolveRuntimeCapabilities({
		runtime,
		handle,
		includeStatusConfigOptionKeys: true
	});
	if (!capabilities.controls.includes("session/set_config_option") || !runtime.setConfigOption) throw createUnsupportedControlError({
		backend: handle.backend || meta.backend,
		control: "session/set_config_option"
	});
	const advertisedKeys = new Set((capabilities.configOptionKeys ?? []).map((entry) => normalizeLowercaseStringOrEmpty(entry)).filter(Boolean));
	const wireKey = resolveRuntimeConfigOptionKey(params.key, capabilities.configOptionKeys);
	if (advertisedKeys.size > 0 && !advertisedKeys.has(normalizeLowercaseStringOrEmpty(wireKey))) throw new AcpRuntimeError("ACP_BACKEND_UNSUPPORTED_CONTROL", `ACP backend "${handle.backend || meta.backend}" does not accept config key "${wireKey}".`);
	const result = await withAcpRuntimeErrorBoundary({
		run: async () => await runtime.setConfigOption({
			handle,
			key: wireKey,
			value: params.value
		}),
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "Could not update ACP runtime config option."
	});
	const nextOptions = reconcileAcceptedRuntimeOptions(mergeRuntimeOptions({
		current: resolveRuntimeOptionsFromMeta(meta),
		patch: inferredPatch
	}), result);
	await persistManagerRuntimeOptions({
		...params,
		options: nextOptions
	});
	return nextOptions;
}
/** Persists runtime option changes that do not need an immediate backend control call. */
async function runUpdateManagerSessionRuntimeOptions(params) {
	const resolution = params.resolveSession({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId
	});
	const resolvedMeta = requireReadySessionMeta(resolution);
	const nextOptions = mergeRuntimeOptions({
		current: resolveRuntimeOptionsFromMeta(resolvedMeta),
		patch: params.patch
	});
	await persistManagerRuntimeOptions({
		...params,
		options: nextOptions
	});
	return nextOptions;
}
/** Closes the current runtime handle and clears persisted runtime options. */
async function runResetManagerSessionRuntimeOptions(params) {
	const resolution = params.resolveSession({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId
	});
	const resolvedMeta = requireReadySessionMeta(resolution);
	const { runtime, handle } = await params.ensureRuntimeHandle({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		meta: resolvedMeta
	});
	await withAcpRuntimeErrorBoundary({
		run: async () => await runtime.close({
			handle,
			reason: "reset-runtime-options"
		}),
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "Could not reset ACP runtime options."
	});
	params.runtimeHandles.clear(params);
	await persistManagerRuntimeOptions({
		...params,
		options: {}
	});
	return {};
}
async function persistManagerRuntimeOptions(params) {
	const normalized = normalizeRuntimeOptions(params.options);
	const hasOptions = Object.keys(normalized).length > 0;
	await params.writeSessionMeta({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		mutate: (current, entry) => {
			if (!entry) return null;
			const base = current;
			if (!base) return null;
			return {
				backend: base.backend,
				agent: base.agent,
				runtimeSessionName: base.runtimeSessionName,
				...base.identity ? { identity: base.identity } : {},
				mode: base.mode,
				runtimeOptions: hasOptions ? normalized : void 0,
				cwd: normalized.cwd,
				state: base.state,
				lastActivityAt: Date.now(),
				...base.lastError ? { lastError: base.lastError } : {}
			};
		},
		failOnError: true
	});
	const cached = params.runtimeHandles.get(params);
	if (!cached) return;
	if ((cached.cwd ?? "") !== (normalized.cwd ?? "")) {
		params.runtimeHandles.clear(params);
		return;
	}
	cached.appliedControlSignature = void 0;
}
//#endregion
//#region src/acp/control-plane/manager.startup-identity-reconcile.ts
/** Startup scan that resolves pending ACP session identities when backends can report status. */
/** Resolves pending ACP session identities opportunistically during manager startup. */
async function runManagerStartupIdentityReconcile(params) {
	let checked = 0;
	let resolved = 0;
	let failed = 0;
	let acpSessions;
	try {
		acpSessions = await params.deps.listAcpSessions({ cfg: params.cfg });
	} catch (error) {
		logVerbose(`acp-manager: startup identity scan failed: ${String(error)}`);
		return {
			checked,
			resolved,
			failed: failed + 1
		};
	}
	for (const session of acpSessions) {
		if (!session.acp || !session.sessionKey) continue;
		if (session.acp.mode === "oneshot") continue;
		const currentIdentity = resolveSessionIdentityFromMeta(session.acp);
		if (!isSessionIdentityPending(currentIdentity) || !identityHasStableSessionId(currentIdentity)) continue;
		checked += 1;
		try {
			const target = resolveAcpSessionTarget({
				cfg: params.cfg,
				sessionKey: session.sessionKey,
				agentId: session.agentId
			});
			if (await params.withSessionActor(target, async () => {
				const resolution = params.resolveSession({
					cfg: params.cfg,
					...target
				});
				if (resolution.kind !== "ready") return false;
				const { runtime, handle, meta } = await params.ensureRuntimeHandle({
					cfg: params.cfg,
					...target,
					meta: resolution.meta
				});
				const reconciled = await params.reconcileRuntimeSessionIdentifiers({
					cfg: params.cfg,
					...target,
					runtime,
					handle,
					meta,
					failOnStatusError: false
				});
				return !isSessionIdentityPending(resolveSessionIdentityFromMeta(reconciled.meta));
			})) resolved += 1;
		} catch (error) {
			failed += 1;
			logVerbose(`acp-manager: startup identity reconcile failed for ${session.sessionKey}: ${String(error)}`);
		}
	}
	return {
		checked,
		resolved,
		failed
	};
}
//#endregion
//#region src/acp/control-plane/manager.status.ts
/** Reads ACP session status from the runtime and reconciles persisted identity metadata. */
/** Reads a fresh ACP session status and reconciles runtime identifiers from the status response. */
async function runManagerGetSessionStatus(params) {
	params.throwIfAborted(params.signal);
	const resolution = params.resolveSession({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId
	});
	const resolvedMeta = requireReadySessionMeta(resolution);
	const { runtime, handle: ensuredHandle, meta: initialMeta } = await params.ensureRuntimeHandle({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		meta: resolvedMeta
	});
	let handle = ensuredHandle;
	const capabilities = await params.resolveRuntimeCapabilities({
		runtime,
		handle
	});
	let runtimeStatus;
	if (runtime.getStatus) runtimeStatus = await withAcpRuntimeErrorBoundary({
		run: async () => {
			params.throwIfAborted(params.signal);
			const status = await runtime.getStatus({
				handle,
				...params.signal ? { signal: params.signal } : {}
			});
			params.throwIfAborted(params.signal);
			return status;
		},
		fallbackCode: "ACP_TURN_FAILED",
		fallbackMessage: "Could not read ACP runtime status."
	});
	const reconciledSession = await params.reconcileRuntimeSessionIdentifiers({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		runtime,
		handle,
		meta: initialMeta,
		runtimeStatus,
		failOnStatusError: true
	});
	handle = reconciledSession.handle;
	const meta = reconciledSession.meta;
	runtimeStatus = reconciledSession.runtimeStatus;
	const identity = resolveSessionIdentityFromMeta(meta);
	return {
		sessionKey: params.sessionKey,
		agentId: params.agentId,
		backend: handle.backend || meta.backend,
		agent: meta.agent,
		...identity ? { identity } : {},
		state: meta.state,
		mode: meta.mode,
		runtimeOptions: resolveRuntimeOptionsFromMeta(meta),
		capabilities,
		runtimeStatus,
		lastActivityAt: meta.lastActivityAt,
		lastError: meta.lastError
	};
}
//#endregion
//#region src/acp/control-plane/active-turns.ts
/** Process-local active-turn registry for ACP maintenance and recovery decisions. */
const ACP_ACTIVE_TURN_STATE_KEY = Symbol.for("openclaw.acp.activeTurns");
function getAcpActiveTurnState() {
	return resolveGlobalSingleton(ACP_ACTIVE_TURN_STATE_KEY, () => ({ activeTurnKeys: /* @__PURE__ */ new Set() }));
}
/** Marks a session as currently running an ACP turn. */
function markAcpTurnActive(target) {
	if (!target.sessionKey) return;
	getAcpActiveTurnState().activeTurnKeys.add(acpSessionActorKey(target));
}
/** Clears the active-turn marker for a session. */
function clearAcpTurnActive(target) {
	if (!target.sessionKey) return;
	getAcpActiveTurnState().activeTurnKeys.delete(acpSessionActorKey(target));
}
/** Returns whether the process currently owns an in-flight ACP turn for a session. */
function isAcpTurnActive(target) {
	if (!target.sessionKey) return false;
	return getAcpActiveTurnState().activeTurnKeys.has(acpSessionActorKey(target));
}
//#endregion
//#region src/acp/control-plane/manager.backend-failover.ts
/** Builds the deduped backend order from configured primary, resolved primary, and fallbacks. */
function resolveBackendCandidatePlan(params) {
	const configuredPrimaryBackend = normalizeOptionalString(params.configuredPrimaryBackend);
	const resolvedPrimaryBackend = normalizeOptionalString(params.resolvedPrimaryBackend);
	const fallbackBackends = Array.isArray(params.fallbackBackends) ? params.fallbackBackends.map((backend) => normalizeOptionalString(backend)).filter((backend) => backend != null) : [];
	return {
		candidateBackends: Array.from(/* @__PURE__ */ new Set([configuredPrimaryBackend ?? resolvedPrimaryBackend ?? "", ...fallbackBackends])),
		describeBackendCandidate: (backend) => backend || resolvedPrimaryBackend || configuredPrimaryBackend || "<auto>"
	};
}
/** Returns true for early transient backend errors where trying another backend is safe. */
function isFailoverWorthyBackendError(attempt) {
	return !attempt.promptStarted && !attempt.sawOutput && (attempt.code === "ACP_TURN_FAILED" || attempt.code === "ACP_SESSION_INIT_FAILED" || attempt.code === "ACP_BACKEND_UNAVAILABLE") && /\b(?:unavailable|rate[-\s]?limit(?:ed|ing)?|quota|exhausted|temporar(?:y|ily)|overloaded)\b/i.test(attempt.error);
}
/** Returns whether another backend candidate remains after the current index. */
function shouldAttemptBackendFailover(params) {
	return params.backendIndex < params.candidateBackends.length - 1;
}
//#endregion
//#region src/acp/control-plane/manager.background-task.ts
/** Mirrors child ACP turns into detached-task status for requester-facing progress. */
const ACP_BACKGROUND_TASK_TEXT_MAX_LENGTH = 160;
const ACP_BACKGROUND_TASK_PROGRESS_MAX_LENGTH = 240;
/** Produces the bounded task label shown for a child ACP background run. */
function summarizeBackgroundTaskText(text) {
	const normalized = normalizeOptionalString(text) ?? "ACP background task";
	if (normalized.length <= ACP_BACKGROUND_TASK_TEXT_MAX_LENGTH) return normalized;
	return `${truncateUtf16Safe(normalized, 159)}…`;
}
/** Appends bounded progress text while preserving a single-line task summary. */
function appendBackgroundTaskProgressSummary(current, chunk) {
	const normalizedChunk = chunk.replace(/\s+/g, " ");
	if (!normalizedChunk) return current;
	const chunkToAppend = current ? normalizedChunk : normalizedChunk.trimStart();
	if (!chunkToAppend) return current;
	const combined = `${current}${chunkToAppend}`.replace(/\s+/g, " ");
	if (combined.length <= ACP_BACKGROUND_TASK_PROGRESS_MAX_LENGTH) return combined;
	return `${truncateUtf16Safe(combined, 239)}…`;
}
/** Maps ACP runtime failures to detached-task terminal states. */
function resolveBackgroundTaskFailureStatus(error) {
	return error.detailCode === "TURN_TIMEOUT" ? "timed_out" : "failed";
}
/** Infers blocked terminal outcomes from final completion text when the child turn reports one. */
function resolveBackgroundTaskTerminalResult(completionText) {
	const requiredCompletionResult = resolveRequiredCompletionTerminalResult(completionText);
	if (requiredCompletionResult.terminalOutcome) return requiredCompletionResult;
	const normalized = normalizeOptionalString(completionText)?.replace(/\s+/g, " ").trim();
	if (!normalized) return {};
	const permissionDeniedMatch = normalized.match(/\b(?:write failed:\s*)?permission denied(?: for (?<path>\S+))?\.?/i);
	if (permissionDeniedMatch) {
		const path = normalizeOptionalString(permissionDeniedMatch.groups?.path)?.replace(/[.,;:!?]+$/, "");
		return {
			terminalOutcome: "blocked",
			terminalSummary: path ? `Permission denied for ${path}.` : "Permission denied."
		};
	}
	if (/\bneed a writable session\b/i.test(normalized) || /\bfilesystem authorization\b/i.test(normalized) || /`?apply_patch`?/i.test(normalized)) return {
		terminalOutcome: "blocked",
		terminalSummary: "Writable session or apply_patch authorization required."
	};
	return {};
}
/** Resolves the requester task context for a spawned child ACP session. */
function resolveBackgroundTaskContext(params) {
	const childEntry = params.deps.loadSessionEntry({
		cfg: params.cfg,
		sessionKey: params.sessionKey,
		agentId: params.agentId
	})?.entry;
	const requesterSessionKey = normalizeOptionalString(childEntry?.spawnedBy) ?? normalizeOptionalString(childEntry?.parentSessionKey);
	if (!requesterSessionKey) return null;
	const requesterOwners = new Set(listTasksForRelatedSessionKey(params.sessionKey).filter((task) => task.runtime === "acp" && task.childSessionKey === params.sessionKey && task.agentId === params.agentId && (task.requesterSessionKey === requesterSessionKey || task.ownerKey === requesterSessionKey)).flatMap((task) => task.requesterAgentId ? [task.requesterAgentId] : []));
	if (childEntry?.createdVia === "spawn" && childEntry.createdActor?.type === "agent" && childEntry.createdActor.id) requesterOwners.add(childEntry.createdActor.id);
	if (requesterOwners.size > 1) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "ACP requester ownership is ambiguous; repair the child task relationship before retrying.");
	const parentTarget = resolveAcpSessionTarget({
		cfg: params.cfg,
		sessionKey: requesterSessionKey,
		agentId: requesterOwners.values().next().value
	});
	const parentEntry = params.deps.loadSessionEntry({
		cfg: params.cfg,
		...parentTarget
	})?.entry;
	return {
		agentId: params.agentId,
		requesterAgentId: parentTarget.agentId,
		requesterSessionKey,
		requesterOrigin: deliveryContextFromSession(parentEntry) ?? deliveryContextFromSession(childEntry),
		childSessionKey: params.sessionKey,
		runId: params.requestId,
		label: normalizeOptionalString(childEntry?.label),
		task: summarizeBackgroundTaskText(params.text)
	};
}
function createBackgroundTaskRecord(context, startedAt, instanceId) {
	try {
		const task = createRunningTaskRun({
			runtime: "acp",
			sourceId: context.runId,
			ownerKey: context.requesterSessionKey,
			agentId: context.agentId,
			requesterAgentId: context.requesterAgentId,
			scopeKind: "session",
			requesterOrigin: context.requesterOrigin,
			childSessionKey: context.childSessionKey,
			runId: context.runId,
			label: context.label,
			task: context.task,
			startedAt,
			detail: createNextAcpTaskBackingDetail({
				childSessionKey: context.childSessionKey,
				instanceId
			})
		});
		if (!task) {
			logVerbose(`acp-manager: failed creating background task for ${context.runId}: persist_failed`);
			return;
		}
		return {
			taskId: task.taskId,
			...task.parentFlowId ? { parentFlowId: task.parentFlowId } : {}
		};
	} catch (error) {
		logVerbose(`acp-manager: failed creating background task for ${context.runId}: ${String(error)}`);
		return;
	}
}
/** Links ACP owner rows only when the runtime reaches its prompt-submitted boundary. */
function bindBackgroundTaskExecution(record, admitted) {
	try {
		const taskResult = bindTaskRunExecution({
			admitted,
			taskId: record.taskId
		});
		if ([taskResult, record.parentFlowId ? isRetainedExecutionOwnerBinding(taskResult) ? bindTaskFlowExecution({
			admitted,
			flowId: record.parentFlowId
		}) : taskResult : void 0].some((result) => result === "mismatch" || result === "missing")) logVerbose("acp-manager: exact task execution binding was not retained");
	} catch (error) {
		logVerbose(`acp-manager: failed binding background task execution: ${String(error)}`);
	}
}
function markBackgroundTaskRunning(runId, params) {
	try {
		startTaskRunByRunId({
			runId,
			runtime: "acp",
			sessionKey: params.sessionKey,
			lastEventAt: params.lastEventAt,
			progressSummary: params.progressSummary
		});
	} catch (error) {
		logVerbose(`acp-manager: failed updating background task for ${runId}: ${String(error)}`);
	}
}
function markBackgroundTaskTerminal(runId, params) {
	try {
		if (params.status === "succeeded") {
			completeTaskRunByRunId({
				runId,
				runtime: "acp",
				sessionKey: params.sessionKey,
				endedAt: params.endedAt,
				lastEventAt: params.lastEventAt,
				progressSummary: params.progressSummary,
				terminalSummary: params.terminalSummary,
				terminalOutcome: params.terminalOutcome
			});
			return;
		}
		failTaskRunByRunId({
			runId,
			runtime: "acp",
			sessionKey: params.sessionKey,
			status: params.status,
			endedAt: params.endedAt,
			lastEventAt: params.lastEventAt,
			error: params.error,
			progressSummary: params.progressSummary,
			terminalSummary: params.terminalSummary
		});
	} catch (error) {
		logVerbose(`acp-manager: failed updating background task for ${runId}: ${String(error)}`);
	}
}
//#endregion
//#region src/acp/control-plane/manager.turn-stream.ts
function isCancellationStopReason(stopReason) {
	return stopReason === "cancel" || stopReason === "cancelled" || stopReason === "manual-cancel";
}
/** Resolves legacy and current done events to the manager's canonical terminal status. */
function resolveAcpTurnTerminalStatus(event) {
	return event.status ?? (isCancellationStopReason(event.stopReason) ? "cancelled" : "completed");
}
async function consumeAcpTurnEvents(params) {
	let streamError = null;
	let sawOutput = false;
	let terminalStatus;
	for await (const event of params.events) {
		if (!params.eventGate.open) continue;
		let forwardedEvent = event;
		if (event.type === "done") {
			terminalStatus = resolveAcpTurnTerminalStatus(event);
			forwardedEvent = {
				...event,
				status: terminalStatus
			};
		} else if (event.type === "error") streamError = new AcpRuntimeError(normalizeAcpErrorCode(event.code), normalizeOptionalString(event.message) || "ACP turn failed before completion.", event.detailCode ? { detailCode: event.detailCode } : void 0);
		else if (event.type === "text_delta" || event.type === "tool_call") {
			sawOutput = true;
			await params.onOutputEvent?.(event);
		}
		await params.onEvent?.(forwardedEvent);
	}
	if (params.eventGate.open && streamError) throw streamError;
	return {
		sawOutput,
		terminalStatus
	};
}
function errorFromTurnResult(result) {
	return new AcpRuntimeError(normalizeAcpErrorCode(result.error.code), normalizeOptionalString(result.error.message) || "ACP turn failed before completion.", result.error.detailCode ? { detailCode: result.error.detailCode } : void 0);
}
function waitForQueuedEvents() {
	return new Promise((resolve) => {
		setTimeout(() => resolve("pending"), 0);
	});
}
async function notifyTerminalResult(params) {
	if (!params.eventGate.open) return;
	if (params.result.status === "completed" || params.result.status === "cancelled") {
		await params.onEvent?.({
			type: "done",
			status: params.result.status,
			...params.result.stopReason ? { stopReason: params.result.stopReason } : {}
		});
		return;
	}
	await params.onEvent?.({
		type: "error",
		code: normalizeAcpErrorCode(params.result.error.code),
		...params.result.error.detailCode ? { detailCode: params.result.error.detailCode } : {},
		message: normalizeOptionalString(params.result.error.message) || "ACP turn failed before completion.",
		...params.result.error.retryable === void 0 ? {} : { retryable: params.result.error.retryable }
	});
}
/** Consumes runtime turn APIs and emits normalized events while tracking output/terminal state. */
async function consumeAcpTurnStream(params) {
	if (params.onBeforePrompt) await params.onBeforePrompt();
	if (params.runtime.startTurn) {
		const turn = params.runtime.startTurn(params.turn);
		let promptReadinessOpen = true;
		const readinessPromise = turn.promptStarted?.then(async () => {
			if (!promptReadinessOpen) return { kind: "prompt-start-closed" };
			await params.onPromptStarted?.({ authoritative: true });
			return { kind: "prompt-started" };
		}, (error) => ({
			kind: "prompt-start-error",
			error
		}));
		const eventsPromise = consumeAcpTurnEvents({
			events: turn.events,
			eventGate: params.eventGate,
			onEvent: params.onEvent,
			onOutputEvent: params.onOutputEvent
		}).then((outcome) => ({
			kind: "events",
			outcome
		}), (error) => ({
			kind: "event-error",
			error
		}));
		const resultPromise = turn.result.then((result) => {
			promptReadinessOpen = false;
			return {
				kind: "result",
				result
			};
		}, (error) => {
			promptReadinessOpen = false;
			return {
				kind: "result-error",
				error
			};
		});
		if (readinessPromise) {
			const readiness = await Promise.race([readinessPromise, resultPromise]);
			if (readiness.kind === "prompt-start-error") {
				await turn.closeStream({ reason: "turn-prompt-start-error" }).catch(() => {});
				const terminalOutcome = await resultPromise;
				if (terminalOutcome.kind === "result" && terminalOutcome.result.status === "completed") throw readiness.error;
			}
		} else await params.onPromptStarted?.({ authoritative: false });
		let eventOutcome = null;
		let result = null;
		const firstOutcome = await Promise.race([eventsPromise, resultPromise]);
		if (firstOutcome.kind === "event-error") {
			await turn.closeStream({ reason: "turn-events-error" }).catch(() => {});
			throw firstOutcome.error;
		}
		if (firstOutcome.kind === "events") eventOutcome = firstOutcome.outcome;
		else if (firstOutcome.kind === "result-error") {
			await turn.closeStream({ reason: "turn-result-error" }).catch(() => {});
			throw firstOutcome.error;
		} else result = firstOutcome.result;
		if (!result) {
			const terminalOutcome = await resultPromise;
			if (terminalOutcome.kind === "result-error") {
				await turn.closeStream({ reason: "turn-result-error" }).catch(() => {});
				throw terminalOutcome.error;
			}
			result = terminalOutcome.result;
		}
		let closedTerminalStream = false;
		if (!eventOutcome) {
			let eventsOutcome = await Promise.race([eventsPromise, waitForQueuedEvents()]);
			if (eventsOutcome === "pending") {
				await turn.closeStream({ reason: `turn-result-${result.status}` }).catch(() => {});
				closedTerminalStream = true;
				eventsOutcome = await eventsPromise;
			}
			if (eventsOutcome.kind === "event-error") throw eventsOutcome.error;
			eventOutcome = eventsOutcome.outcome;
		}
		if (result.status !== "completed" && !closedTerminalStream) await turn.closeStream({ reason: `turn-result-${result.status}` }).catch(() => {});
		await notifyTerminalResult({
			result,
			eventGate: params.eventGate,
			onEvent: params.onEvent
		});
		if (result.status === "failed") throw errorFromTurnResult(result);
		return {
			sawOutput: eventOutcome.sawOutput,
			terminalStatus: result.status
		};
	}
	const events = params.runtime.runTurn(params.turn);
	await params.onPromptStarted?.({ authoritative: false });
	return await consumeAcpTurnEvents({
		events,
		eventGate: params.eventGate,
		onEvent: params.onEvent,
		onOutputEvent: params.onOutputEvent
	});
}
//#endregion
//#region src/acp/control-plane/manager.turn-runner.ts
const ACP_TURN_TIMEOUT_GRACE_MS = 1e3;
const ACP_COMPLETION_EVIDENCE_MAX_BYTES = 102400;
/** Executes one ACP prompt turn against the selected backend and records terminal state. */
async function runManagerTurn(params) {
	const { input, sessionKey, agentId } = params;
	if (input.admittedRunContext.operationalRunInstance.runId !== input.requestId) throw new Error("ACP operational run instance disagrees with the admitted request");
	const turnStartedAt = Date.now();
	const actorKey = acpSessionActorKey(params);
	const taskContext = input.mode === "prompt" ? resolveBackgroundTaskContext({
		deps: params.deps,
		cfg: input.cfg,
		sessionKey,
		agentId,
		requestId: input.requestId,
		text: input.text
	}) : null;
	const taskRecord = taskContext ? createBackgroundTaskRecord(taskContext, turnStartedAt, input.admittedRunContext.operationalRunInstance.instanceId) : void 0;
	let taskExecutionBound = false;
	let taskProgressSummary = "";
	const initialResolution = params.resolveSession({
		cfg: input.cfg,
		sessionKey,
		agentId
	});
	const initialMeta = requireReadySessionMeta(initialResolution);
	recordSessionHumanDirectMessage({
		sessionKey,
		entry: initialResolution.kind === "ready" ? initialResolution.entry : void 0,
		actor: { actorType: input.provenance },
		channel: "acp",
		runId: input.requestId
	});
	const spawnedByWatcher = initialResolution.kind === "ready" ? initialResolution.entry?.spawnedBy ?? initialResolution.entry?.parentSessionKey : void 0;
	const { candidateBackends, describeBackendCandidate } = resolveBackendCandidatePlan({
		configuredPrimaryBackend: input.cfg.acp?.backend,
		resolvedPrimaryBackend: initialMeta.backend,
		fallbackBackends: input.cfg.acp?.fallbacks
	});
	const backendAttempts = [];
	const recordBackendFailure = async (error) => {
		const failedBackends = backendAttempts.map((attempt) => `${attempt.backend}: ${attempt.error}`).join(" | ");
		const errorToRecord = backendAttempts.length > 1 ? new AcpRuntimeError(error.code, `All ACP backends failed (${backendAttempts.length}): ${failedBackends}`, { detailCode: error.detailCode }) : error;
		params.recordTurnCompletion({
			startedAt: turnStartedAt,
			errorCode: errorToRecord.code
		});
		if (taskContext) {
			const failureStatus = resolveBackgroundTaskFailureStatus(errorToRecord);
			markBackgroundTaskTerminal(taskContext.runId, {
				sessionKey,
				status: failureStatus,
				endedAt: Date.now(),
				lastEventAt: Date.now(),
				error: formatAcpErrorChain(errorToRecord),
				progressSummary: taskProgressSummary || null,
				terminalSummary: failureStatus === "timed_out" ? taskProgressSummary || null : null
			});
			if (spawnedByWatcher) recordSubagentTerminalState({
				childSessionKey: sessionKey,
				runId: taskContext.runId,
				requesterSessionKey: spawnedByWatcher,
				outcomeStatus: failureStatus === "timed_out" ? "timeout" : "error"
			});
		}
		await params.setSessionState({
			cfg: input.cfg,
			sessionKey,
			agentId,
			state: "error",
			lastError: formatAcpErrorChain(errorToRecord)
		});
		throw errorToRecord;
	};
	let acpTurnMarkedActive = false;
	if (taskContext) {
		markAcpTurnActive(params);
		acpTurnMarkedActive = true;
	}
	try {
		for (const [backendIdx, currentBackend] of candidateBackends.entries()) {
			if (backendIdx > 0) {
				await params.runtimeHandles.close({
					sessionKey,
					agentId,
					reason: "backend-failover"
				});
				logVerbose(`acp-manager: switching backend for ${sessionKey} from ${describeBackendCandidate(expectDefined(candidateBackends[backendIdx - 1], "candidate backends entry at backend idx 1"))} to ${describeBackendCandidate(currentBackend)}`);
			}
			for (let attempt = 0; attempt < 2; attempt += 1) {
				const resolution = backendIdx === 0 && attempt === 0 ? initialResolution : params.resolveSession({
					cfg: input.cfg,
					sessionKey,
					agentId
				});
				const resolvedMeta = requireReadySessionMeta(resolution);
				let runtime;
				let handle;
				let meta;
				let activeTurn;
				let internalAbortController;
				let onCallerAbort;
				let activeTurnStarted = false;
				let promptStarted = false;
				let sawTurnOutput = false;
				let retryFreshHandle = false;
				let skipPostTurnCleanup = false;
				let completionEvidenceText = "";
				let completionEvidenceBytes = 0;
				let completionEvidenceOverflowed = false;
				try {
					const ensured = await params.ensureRuntimeHandle({
						cfg: input.cfg,
						sessionKey,
						agentId,
						meta: resolvedMeta,
						selectedBackend: currentBackend
					});
					runtime = ensured.runtime;
					handle = ensured.handle;
					meta = ensured.meta;
					await applyManagerRuntimeControls({
						sessionKey,
						runtime,
						handle,
						meta,
						getCachedRuntimeState: () => params.runtimeHandles.get(params),
						onOptionsChanged: async (runtimeOptions) => {
							await params.writeSessionMeta({
								cfg: input.cfg,
								sessionKey,
								agentId,
								mutate: (current) => current ? {
									...current,
									runtimeOptions
								} : null,
								failOnError: true
							});
							meta = {
								...ensured.meta,
								runtimeOptions
							};
						}
					});
					await params.setSessionState({
						cfg: input.cfg,
						sessionKey,
						agentId,
						state: "running",
						clearLastError: true
					});
					internalAbortController = new AbortController();
					onCallerAbort = () => {
						internalAbortController?.abort();
					};
					if (input.signal?.aborted) internalAbortController.abort();
					else if (input.signal) input.signal.addEventListener("abort", onCallerAbort, { once: true });
					activeTurn = {
						requestId: input.requestId,
						instanceId: input.admittedRunContext.operationalRunInstance.instanceId,
						runtime,
						handle,
						abortController: internalAbortController
					};
					params.activeTurnBySession.set(actorKey, activeTurn);
					activeTurnStarted = true;
					const combinedSignal = input.signal ? AbortSignal.any([input.signal, internalAbortController.signal]) : internalAbortController.signal;
					const eventGate = { open: true };
					const turnPromise = consumeAcpTurnStream({
						runtime,
						turn: {
							handle,
							text: input.text,
							attachments: input.attachments,
							mode: input.mode,
							requestId: input.requestId,
							signal: combinedSignal,
							onElicitation: input.onElicitation
						},
						eventGate,
						onBeforePrompt: input.onBeforePrompt,
						onPromptStarted: async ({ authoritative }) => {
							promptStarted = authoritative;
							if (authoritative && taskRecord && !taskExecutionBound) {
								taskExecutionBound = true;
								bindBackgroundTaskExecution(taskRecord, input.admittedRunContext);
							}
							try {
								await input.onLifecycle?.({
									type: "prompt_submitted",
									at: Date.now()
								});
							} catch (error) {
								logVerbose(`acp-manager: prompt submission observer failed for ${sessionKey}: ${String(error)}`);
							}
						},
						onOutputEvent: (event) => {
							sawTurnOutput = true;
							if (event.type === "text_delta" && event.stream !== "thought" && event.text) {
								taskProgressSummary = appendBackgroundTaskProgressSummary(taskProgressSummary, event.text);
								if (taskContext && !completionEvidenceOverflowed) {
									completionEvidenceText += event.text;
									completionEvidenceBytes = Buffer.byteLength(completionEvidenceText, "utf8");
									if (completionEvidenceBytes > ACP_COMPLETION_EVIDENCE_MAX_BYTES) {
										completionEvidenceOverflowed = true;
										completionEvidenceText = "";
									}
								}
							}
							if (taskContext) markBackgroundTaskRunning(taskContext.runId, {
								sessionKey,
								lastEventAt: Date.now(),
								progressSummary: taskProgressSummary || null
							});
						},
						onEvent: input.onEvent
					});
					const turnTimeoutMs = resolveTurnTimeoutMs({
						cfg: input.cfg,
						meta
					});
					const sessionMode = meta.mode;
					const turnOutcome = await awaitTurnWithTimeout({
						sessionKey,
						turnPromise,
						timeoutMs: turnTimeoutMs + ACP_TURN_TIMEOUT_GRACE_MS,
						timeoutLabelMs: turnTimeoutMs,
						onTimeout: async () => {
							eventGate.open = false;
							skipPostTurnCleanup = true;
							if (!activeTurn) return;
							await cleanupTimedOutTurn({
								sessionKey,
								activeTurn,
								mode: sessionMode,
								clearCachedRuntimeStateIfHandleMatches: (turn) => {
									params.runtimeHandles.clearIfHandleMatches({
										sessionKey,
										agentId,
										handle: turn.handle
									});
								}
							});
						}
					});
					if (!turnOutcome.terminalStatus) throw new AcpRuntimeError("ACP_TURN_FAILED", "ACP turn ended without a terminal done event.");
					params.recordTurnCompletion({ startedAt: turnStartedAt });
					if (taskContext) {
						const terminalResult = turnOutcome.terminalStatus === "cancelled" ? {} : completionEvidenceOverflowed ? {
							terminalOutcome: "blocked",
							terminalSummary: "Required completion output exceeded the 100 KB verification limit; inspect the child session for the final deliverable."
						} : resolveBackgroundTaskTerminalResult(completionEvidenceText);
						markBackgroundTaskTerminal(taskContext.runId, {
							sessionKey,
							status: turnOutcome.terminalStatus === "cancelled" ? "cancelled" : "succeeded",
							endedAt: Date.now(),
							lastEventAt: Date.now(),
							error: void 0,
							progressSummary: taskProgressSummary || null,
							terminalSummary: terminalResult.terminalSummary ?? null,
							terminalOutcome: terminalResult.terminalOutcome
						});
						if (spawnedByWatcher) recordSubagentTerminalState({
							childSessionKey: sessionKey,
							runId: taskContext.runId,
							requesterSessionKey: spawnedByWatcher,
							outcomeStatus: turnOutcome.terminalStatus === "cancelled" ? "cancelled" : "ok"
						});
					}
					await params.setSessionState({
						cfg: input.cfg,
						sessionKey,
						agentId,
						state: "idle",
						clearLastError: true
					});
					return;
				} catch (error) {
					const acpError = toAcpRuntimeError({
						error,
						fallbackCode: activeTurnStarted ? "ACP_TURN_FAILED" : "ACP_SESSION_INIT_FAILED",
						fallbackMessage: activeTurnStarted ? "ACP turn failed before completion." : "Could not initialize ACP session runtime."
					});
					retryFreshHandle = await prepareFreshManagerRuntimeHandleRetry({
						attempt,
						cfg: input.cfg,
						sessionKey,
						agentId,
						error: acpError,
						promptStarted,
						sawTurnOutput,
						runtime,
						meta,
						runtimeHandles: params.runtimeHandles,
						writeSessionMeta: params.writeSessionMeta
					});
					if (retryFreshHandle) continue;
					const backendAttempt = {
						backend: describeBackendCandidate(currentBackend),
						error: acpError.message,
						code: acpError.code,
						promptStarted,
						sawOutput: sawTurnOutput
					};
					backendAttempts.push(backendAttempt);
					if (isAcpOwnerRepairRequired(acpError) || !isFailoverWorthyBackendError(backendAttempt) || !shouldAttemptBackendFailover({
						backendIndex: backendIdx,
						candidateBackends
					})) await recordBackendFailure(acpError);
					break;
				} finally {
					if (input.signal && onCallerAbort) input.signal.removeEventListener("abort", onCallerAbort);
					if (activeTurn && params.activeTurnBySession.get(actorKey) === activeTurn) params.activeTurnBySession.delete(actorKey);
					if (!retryFreshHandle && !skipPostTurnCleanup && runtime && handle && meta) ({handle, meta} = await params.reconcileRuntimeSessionIdentifiers({
						cfg: input.cfg,
						sessionKey,
						agentId,
						runtime,
						handle,
						meta,
						failOnStatusError: false
					}));
					if (!retryFreshHandle && !skipPostTurnCleanup && runtime && handle && meta && meta.mode === "oneshot") try {
						await runtime.close({
							handle,
							reason: "oneshot-complete"
						});
					} catch (error) {
						logVerbose(`acp-manager: ACP oneshot close failed for ${sessionKey}: ${String(error)}`);
					} finally {
						params.runtimeHandles.clear(params);
					}
				}
			}
		}
	} finally {
		if (acpTurnMarkedActive) clearAcpTurnActive(params);
	}
}
//#endregion
//#region src/acp/control-plane/manager.types.ts
const DEFAULT_DEPS = {
	listAcpSessions: listAcpSessionEntries,
	loadSessionEntry: readAcpSessionEntry,
	upsertSessionMeta: upsertAcpSessionMeta,
	getRuntimeBackend: getAcpRuntimeBackend,
	requireRuntimeBackend: requireAcpRuntimeBackend
};
//#endregion
//#region src/acp/control-plane/session-actor-queue.ts
/** Per-session async queue wrapper used by ACP manager operations. */
/** Per-session async queue that serializes ACP runtime operations and exposes queue depth. */
var SessionActorQueue = class {
	constructor() {
		this.queue = new KeyedAsyncQueue();
		this.pendingBySession = /* @__PURE__ */ new Map();
	}
	getTotalPendingCount() {
		let total = 0;
		for (const count of this.pendingBySession.values()) total += count;
		return total;
	}
	async run(actorKey, op) {
		return this.queue.enqueue(actorKey, op, {
			onEnqueue: () => {
				this.pendingBySession.set(actorKey, (this.pendingBySession.get(actorKey) ?? 0) + 1);
			},
			onSettle: () => {
				const pending = (this.pendingBySession.get(actorKey) ?? 1) - 1;
				if (pending <= 0) this.pendingBySession.delete(actorKey);
				else this.pendingBySession.set(actorKey, pending);
			}
		});
	}
};
//#endregion
//#region src/acp/control-plane/manager.core.ts
/** Coordinates ACP session metadata, runtime handles, per-session queues, and turn execution. */
var AcpSessionManager = class {
	constructor(deps = DEFAULT_DEPS) {
		this.actorQueue = new SessionActorQueue();
		this.runtimeHandles = new ManagerRuntimeHandleCache();
		this.activeTurnBySession = /* @__PURE__ */ new Map();
		this.turnLatencyStats = {
			completed: 0,
			failed: 0,
			totalMs: 0,
			maxMs: 0
		};
		this.errorCountsByCode = /* @__PURE__ */ new Map();
		this.deps = deps;
		registerAcpSessionManagerDisposer(this, async (reason) => {
			await Promise.all([...this.activeTurnBySession.values()].map(async (activeTurn) => {
				try {
					await cancelManagerActiveTurn({
						activeTurn,
						reason
					});
				} catch (error) {
					logVerbose(`acp-manager: active runtime cancel failed for ${activeTurn.handle.sessionKey}: ${String(error)}`);
				}
			}));
			await this.runtimeHandles.closeAll({
				actorQueue: this.actorQueue,
				reason
			});
			this.activeTurnBySession.clear();
		});
	}
	resolveSession(params) {
		if (!params.sessionKey.trim()) return {
			kind: "none",
			sessionKey: ""
		};
		const target = resolveAcpSessionTarget(params);
		const { sessionKey } = target;
		const stored = this.deps.loadSessionEntry({
			cfg: params.cfg,
			...target,
			clone: false
		});
		const acp = stored?.acp;
		if (acp) return {
			kind: "ready",
			...target,
			meta: acp,
			entry: stored.entry
		};
		if (isAcpSessionKey(sessionKey)) return {
			kind: "stale",
			...target,
			error: resolveMissingMetaError(sessionKey)
		};
		return {
			kind: "none",
			...target
		};
	}
	getObservabilitySnapshot() {
		const completedTurns = this.turnLatencyStats.completed + this.turnLatencyStats.failed;
		const averageLatencyMs = completedTurns > 0 ? Math.round(this.turnLatencyStats.totalMs / completedTurns) : 0;
		return {
			runtimeCache: this.runtimeHandles.getObservabilitySnapshot(),
			turns: {
				active: this.activeTurnBySession.size,
				queueDepth: this.actorQueue.getTotalPendingCount(),
				completed: this.turnLatencyStats.completed,
				failed: this.turnLatencyStats.failed,
				averageLatencyMs,
				maxLatencyMs: this.turnLatencyStats.maxMs
			},
			errorsByCode: Object.fromEntries([...this.errorCountsByCode.entries()].toSorted(([a], [b]) => a.localeCompare(b)))
		};
	}
	async reconcilePendingSessionIdentities(params) {
		return await runManagerStartupIdentityReconcile({
			cfg: params.cfg,
			deps: this.deps,
			withSessionActor: this.withSessionActor.bind(this),
			resolveSession: this.resolveSession.bind(this),
			ensureRuntimeHandle: this.ensureRuntimeHandle.bind(this),
			reconcileRuntimeSessionIdentifiers: this.reconcileRuntimeSessionIdentifiers.bind(this)
		});
	}
	async initializeSession(input) {
		const target = resolveAcpSessionTarget(input);
		return await this.withSessionActor(target, async () => {
			const initialized = await runManagerInitializeSession({
				input,
				...target,
				deps: this.deps,
				runtimeHandles: this.runtimeHandles,
				writeSessionMeta: this.writeSessionMeta.bind(this)
			});
			return {
				...initialized,
				closeRuntimeOnFailure: () => this.withSessionActor(target, () => this.runtimeHandles.close({
					...target,
					reason: "spawn-failed",
					expectedHandle: initialized.handle
				}))
			};
		});
	}
	async getSessionStatus(params) {
		const target = resolveAcpSessionTarget(params);
		this.throwIfAborted(params.signal);
		return await this.withSessionActor(target, async () => await runManagerGetSessionStatus({
			cfg: params.cfg,
			...target,
			signal: params.signal,
			throwIfAborted: this.throwIfAborted.bind(this),
			resolveSession: this.resolveSession.bind(this),
			ensureRuntimeHandle: this.ensureRuntimeHandle.bind(this),
			resolveRuntimeCapabilities: this.resolveRuntimeCapabilities.bind(this),
			reconcileRuntimeSessionIdentifiers: this.reconcileRuntimeSessionIdentifiers.bind(this)
		}), params.signal);
	}
	async setSessionRuntimeMode(params) {
		const target = resolveAcpSessionTarget(params);
		const runtimeMode = validateRuntimeModeInput(params.runtimeMode);
		return await this.withSessionActor(target, async () => {
			return await runSetManagerSessionRuntimeMode({
				cfg: params.cfg,
				...target,
				runtimeMode,
				...this.runtimeOptionCommandServices()
			});
		});
	}
	async setSessionConfigOption(params) {
		const target = resolveAcpSessionTarget(params);
		const normalizedOption = validateRuntimeConfigOptionInput(params.key, params.value);
		const key = normalizedOption.key;
		const value = normalizedOption.value;
		return await this.withSessionActor(target, async () => {
			return await runSetManagerSessionConfigOption({
				cfg: params.cfg,
				...target,
				key,
				value,
				...this.runtimeOptionCommandServices()
			});
		});
	}
	async updateSessionRuntimeOptions(params) {
		const target = resolveAcpSessionTarget(params);
		const validatedPatch = validateRuntimeOptionPatch(params.patch);
		return await this.withSessionActor(target, async () => {
			return await runUpdateManagerSessionRuntimeOptions({
				cfg: params.cfg,
				...target,
				patch: validatedPatch,
				...this.runtimeOptionCommandServices()
			});
		});
	}
	async resetSessionRuntimeOptions(params) {
		const target = resolveAcpSessionTarget(params);
		return await this.withSessionActor(target, async () => {
			return await runResetManagerSessionRuntimeOptions({
				cfg: params.cfg,
				...target,
				...this.runtimeOptionCommandServices()
			});
		});
	}
	async runTurn(input) {
		const target = resolveAcpSessionTarget(input);
		await this.withSessionActor(target, async () => await runManagerTurn({
			input,
			...target,
			deps: this.deps,
			runtimeHandles: this.runtimeHandles,
			activeTurnBySession: this.activeTurnBySession,
			resolveSession: this.resolveSession.bind(this),
			ensureRuntimeHandle: this.ensureRuntimeHandle.bind(this),
			setSessionState: this.setSessionState.bind(this),
			recordTurnCompletion: this.recordTurnCompletion.bind(this),
			reconcileRuntimeSessionIdentifiers: this.reconcileRuntimeSessionIdentifiers.bind(this),
			writeSessionMeta: this.writeSessionMeta.bind(this)
		}), input.signal);
	}
	async cancelSession(params) {
		const target = resolveAcpSessionTarget(params);
		await runManagerCancelSession({
			cfg: params.cfg,
			...target,
			reason: params.reason,
			expectedRunId: params.expectedRunId,
			expectedInstanceId: params.expectedInstanceId,
			expectedOwnerKey: params.expectedOwnerKey,
			activeTurnBySession: this.activeTurnBySession,
			withSessionActor: this.withSessionActor.bind(this),
			resolveSession: this.resolveSession.bind(this),
			ensureRuntimeHandle: this.ensureRuntimeHandle.bind(this),
			setSessionState: this.setSessionState.bind(this)
		});
	}
	async closeSession(input) {
		const target = resolveAcpSessionTarget(input);
		return await this.withSessionActor(target, async () => await runManagerCloseSession({
			input,
			...target,
			deps: this.deps,
			runtimeHandles: this.runtimeHandles,
			resolveSession: this.resolveSession.bind(this),
			ensureRuntimeHandle: this.ensureRuntimeHandle.bind(this),
			writeSessionMeta: this.writeSessionMeta.bind(this)
		}));
	}
	async ensureRuntimeHandle(params) {
		return await ensureManagerRuntimeHandle({
			...params,
			deps: this.deps,
			runtimeHandles: this.runtimeHandles,
			writeSessionMeta: async (writeParams) => await this.writeSessionMeta(writeParams)
		});
	}
	runtimeOptionCommandServices() {
		return {
			runtimeHandles: this.runtimeHandles,
			resolveSession: this.resolveSession.bind(this),
			ensureRuntimeHandle: this.ensureRuntimeHandle.bind(this),
			resolveRuntimeCapabilities: this.resolveRuntimeCapabilities.bind(this),
			writeSessionMeta: this.writeSessionMeta.bind(this)
		};
	}
	recordTurnCompletion(params) {
		const durationMs = Math.max(0, Date.now() - params.startedAt);
		this.turnLatencyStats.totalMs += durationMs;
		this.turnLatencyStats.maxMs = Math.max(this.turnLatencyStats.maxMs, durationMs);
		if (params.errorCode) {
			this.turnLatencyStats.failed += 1;
			this.recordErrorCode(params.errorCode);
			return;
		}
		this.turnLatencyStats.completed += 1;
	}
	recordErrorCode(code) {
		const normalized = normalizeAcpErrorCode(code);
		this.errorCountsByCode.set(normalized, (this.errorCountsByCode.get(normalized) ?? 0) + 1);
	}
	async resolveRuntimeCapabilities(params) {
		return await resolveManagerRuntimeCapabilities(params);
	}
	async setSessionState(params) {
		await this.writeSessionMeta({
			cfg: params.cfg,
			sessionKey: params.sessionKey,
			agentId: params.agentId,
			skipMaintenance: true,
			takeCacheOwnership: true,
			mutate: (current, entry) => {
				if (!entry) return null;
				const base = current;
				if (!base) return null;
				const next = {
					backend: base.backend,
					agent: base.agent,
					runtimeSessionName: base.runtimeSessionName,
					...base.identity ? { identity: base.identity } : {},
					mode: base.mode,
					...base.runtimeOptions ? { runtimeOptions: base.runtimeOptions } : {},
					...base.cwd ? { cwd: base.cwd } : {},
					state: params.state,
					lastActivityAt: Date.now(),
					...base.lastError ? { lastError: base.lastError } : {}
				};
				const lastError = normalizeOptionalString(params.lastError);
				if (lastError) next.lastError = lastError;
				else if (params.clearLastError) delete next.lastError;
				return next;
			}
		});
	}
	async reconcileRuntimeSessionIdentifiers(params) {
		return await reconcileManagerRuntimeSessionIdentifiers({
			...params,
			setCachedHandle: (target, handle) => {
				const cached = this.runtimeHandles.get(target);
				if (cached) cached.handle = handle;
			},
			writeSessionMeta: async (writeParams) => await this.writeSessionMeta(writeParams)
		});
	}
	async writeSessionMeta(params) {
		try {
			return await this.deps.upsertSessionMeta({
				cfg: params.cfg,
				sessionKey: params.sessionKey,
				agentId: params.agentId,
				mutate: params.mutate,
				assertCommitAllowed: params.assertCommitAllowed,
				...params.skipMaintenance === true ? { skipMaintenance: true } : {},
				...params.takeCacheOwnership === true ? { takeCacheOwnership: true } : {}
			});
		} catch (error) {
			if (params.failOnError || error instanceof AgentSelectionRequiredError) throw error;
			logVerbose(`acp-manager: failed persisting ACP metadata for ${params.sessionKey}: ${String(error)}`);
			return null;
		}
	}
	async withSessionActor(target, op, signal) {
		const actorKey = acpSessionActorKey(target);
		this.throwIfAborted(signal);
		let actorStarted = false;
		const queued = this.actorQueue.run(actorKey, async () => {
			actorStarted = true;
			this.throwIfAborted(signal);
			return await op();
		});
		if (!signal) return await queued;
		return await new Promise((resolve, reject) => {
			let settled = false;
			const cleanup = () => {
				signal.removeEventListener("abort", onAbort);
			};
			const settleValue = (value) => {
				if (settled) return;
				settled = true;
				cleanup();
				resolve(value);
			};
			const settleError = (error) => {
				if (settled) return;
				settled = true;
				cleanup();
				reject(toErrorObject(error, "Non-Error rejection"));
			};
			const onAbort = () => {
				if (actorStarted) return;
				try {
					this.throwIfAborted(signal);
				} catch (error) {
					settleError(error);
				}
			};
			signal.addEventListener("abort", onAbort, { once: true });
			queued.then(settleValue, settleError);
			if (signal.aborted) onAbort();
		});
	}
	throwIfAborted(signal) {
		if (!signal?.aborted) return;
		throw new AcpRuntimeError("ACP_TURN_FAILED", "ACP operation aborted.");
	}
};
//#endregion
//#region src/acp/control-plane/manager.ts
/** Public singleton facade for the ACP session manager control plane. */
let ACP_SESSION_MANAGER_SINGLETON = null;
/** Returns the process-wide ACP session manager singleton. */
function getAcpSessionManager() {
	if (!ACP_SESSION_MANAGER_SINGLETON) ACP_SESSION_MANAGER_SINGLETON = new AcpSessionManager();
	return ACP_SESSION_MANAGER_SINGLETON;
}
const testing = {
	resetAcpSessionManagerForTests() {
		ACP_SESSION_MANAGER_SINGLETON = null;
	},
	setAcpSessionManagerForTests(manager) {
		ACP_SESSION_MANAGER_SINGLETON = manager;
	}
};
//#endregion
export { disposeAcpSessionManagerInstance as a, isAcpTurnActive as i, testing as n, tryPrepareFreshManagerRuntimeSession as o, AcpSessionManager as r, isAcpOwnerRepairRequired as s, getAcpSessionManager as t };