UNPKG

acpx

Version:

Headless CLI client for the Agent Client Protocol (ACP) — talk to coding agents from the command line

2,573 lines • 110 kB
#!/usr/bin/env node
import { c as __exportAll, n as getTextErrorRemediationHints, o as toTimerMilliseconds, t as createOutputFormatter } from "./output-C_T39Ap1.js";
import { C as renderArgvIdentity, i as DEFAULT_AGENT_NAME, l as normalizeAgentName$1, s as listBuiltInAgents } from "./agent-registry-CxpAF3nI.js";
import { A as findSession, At as EXIT_CODES, C as textPrompt, Ct as exitCodeForOutputErrorCode, Et as normalizeOutputError, F as normalizeName, G as normalizeAgentSessionId, H as canonicalAgentCommand, It as AcpxOperationalError, Mt as OUTPUT_FORMATS, N as listSessions, O as absolutePath, Rt as AgentSpawnError, St as isProcessAlive, U as serializeSessionRecordForDisk, V as parseSessionRecord, _t as sessionEventLockPath, b as mergePromptSourceWithText, c as probeQueueOwnerHealth, dt as boundedEventMaxSegments, ft as defaultSessionEventLog, gt as sessionEventActivePath, j as findSessionByDirectoryWalk, k as findGitRepositoryRoot, v as PromptInputValidationError, x as parsePromptSource, xt as _usingCtx, yt as writePrivateFile, z as writeSessionRecord } from "./ipc-CQUNQ8QQ.js";
import { ct as listSessionEvents, d as flushPerfMetricsCapture, dt as TimeoutError, f as installPerfMetricsCapture, i as runOnce, lt as isLegacyZedCodexAcpInvocation, n as runSessionQueueOwner, ot as acquireSessionImport, s as DISCARD_OUTPUT_FORMATTER, st as acquireSessionScope, tt as getAcpxVersion, u as buildQueueOwnerArgOverride, ut as InterruptedError } from "./queue-owner-runtime-ucHvXJ33.js";
import { C as resolvePermissionMode, E as resolveSessionsListFlags, S as resolveOutputPolicy, T as resolveSessionNameFromFlags, _ as parseSessionName, a as addGlobalFlags, b as resolveAgentInvocation, c as addSessionOption, d as parseDaysOlderThan, f as parseHistoryLimit, g as parsePruneBeforeDate, h as parseOutputFormat$1, i as addExecConfigOption, m as parseNonEmptyValue, n as sessionConnectionOptions, o as addPromptInputOption, p as parseMaxTurns, r as sessionOptionsFromGlobalFlags, s as addSessionNameOption, t as resolvePermissionPolicyFromFlags, u as parseAllowedTools, v as parseTimeoutSeconds, w as resolvePromptFlags, x as resolveGlobalFlags, y as parseTtlSeconds } from "./invocation-options-8lEkbe7g.js";
import { realpathSync } from "node:fs";
import { fileURLToPath, pathToFileURL } from "node:url";
import path from "node:path";
import { Command, CommanderError, InvalidArgumentError, Option } from "commander";
import fs$1 from "node:fs/promises";
import os from "node:os";
import { ZodError, z } from "zod";
import { createHash, randomUUID } from "node:crypto";
import { text } from "node:stream/consumers";
import { performance } from "node:perf_hooks";
// src/cli-public.ts
function configurePublicCli(options) {
	const builtInAgents = options.listBuiltInAgents(options.config.agents);
	for (const agentName of builtInAgents) options.registerAgentCommand(options.program, agentName, options.config);
	options.registerDefaultCommands(options.program, options.config);
	const scan = options.detectAgentToken(options.argv);
	if (!scan.hasAgentOverride && scan.token && !options.topLevelVerbs.has(scan.token) && !builtInAgents.includes(scan.token)) options.registerAgentCommand(options.program, scan.token, options.config);
	options.program.helpCommand(true);
	options.program.argument("[prompt...]", "Prompt text").action(async function(promptParts) {
		if (promptParts.length === 0 && process.stdin.isTTY) {
			if (options.requestedJsonStrict) throw new InvalidArgumentError("Prompt is required (pass as argument, --file, or pipe via stdin)");
			this.outputHelp();
			return;
		}
		await options.handlePromptAction(this, promptParts);
	});
	options.program.addHelpText("after", `
Examples:
  acpx pi "review recent changes"
  acpx openclaw exec "summarize active session state"
  acpx codex sessions new
  acpx codex "fix the tests"
  acpx codex prompt "fix the tests"
  acpx codex --no-wait "queue follow-up task"
  acpx codex exec "what does this repo do"
  acpx codex cancel
  acpx codex set-mode plan
  acpx codex set model 'gpt-5.2[high]'
  acpx codex -s backend "fix the API"
  acpx codex sessions
  acpx codex sessions new --name backend
  acpx codex sessions ensure --name backend
  acpx codex sessions close backend
  acpx codex status
  acpx config show
  acpx config init
  acpx --ttl 30 codex "investigate flaky tests"
  acpx claude "refactor auth"
  acpx --agent ./my-custom-server "do something"`);
}
// src/session/export.ts
var SessionExportError = class extends AcpxOperationalError {
	code;
	exitCode = 2;
	constructor(message, code) {
		super(message, {
			outputCode: "USAGE",
			detailCode: code,
			origin: "cli"
		});
		this.code = code;
	}
};
function sessionLookupError(message, code) {
	return new SessionExportError(message, code);
}
async function loadSessionRecord(sessionLookup) {
	const cwd = path.resolve(sessionLookup.cwd ?? process.cwd());
	const name = normalizeName(sessionLookup.name);
	const active = await findSession({
		agentCommand: sessionLookup.agentCommand,
		cwd,
		name
	});
	if (active) return active;
	return (await listSessions()).find((session) => {
		if (session.agentCommand !== sessionLookup.agentCommand || session.cwd !== cwd) return false;
		if (name == null) return session.name == null;
		return session.name === name;
	});
}
function parseEventLockPayload(raw) {
	try {
		const parsed = JSON.parse(raw);
		if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
		const record = parsed;
		return { pid: typeof record.pid === "number" ? record.pid : void 0 };
	} catch {
		return {};
	}
}
async function hasLiveEventLock(recordId) {
	try {
		const payload = await fs$1.readFile(sessionEventLockPath(recordId), "utf8");
		return isProcessAlive(parseEventLockPayload(payload).pid);
	} catch (error) {
		if (error.code === "ENOENT") return false;
		throw error;
	}
}
async function isSessionActive(record) {
	if (record.closed) return false;
	return isProcessAlive(record.pid) || await hasLiveEventLock(record.acpxRecordId);
}
function cwdRelativeToHome(cwd, home) {
	const relative = path.relative(home, cwd);
	if (relative.length === 0) return ".";
	if (relative.length > 0 && !relative.startsWith("..") && !path.isAbsolute(relative)) return relative;
	return cwd;
}
function serializeSessionRecordForArchive(record, cwdRelative) {
	const state = serializeSessionRecordForDisk(record);
	state.cwd = cwdRelative;
	if (state.event_log && typeof state.event_log === "object" && !Array.isArray(state.event_log)) state.event_log = {
		...state.event_log,
		active_path: ".stream.ndjson"
	};
	return state;
}
async function exportSession(sessionLookup, outputPath) {
	const record = await loadSessionRecord(sessionLookup);
	if (!record) throw sessionLookupError("session not found", "not-found");
	if (await isSessionActive(record)) throw sessionLookupError("session is currently locked by a running queue owner; close it first with `acpx sessions close`", "session-locked");
	const home = os.homedir();
	const cwdRelative = cwdRelativeToHome(record.cwd, home);
	const exported = {
		format_version: 1,
		exported_at: (/* @__PURE__ */ new Date()).toISOString(),
		exported_by: "acpx",
		session: {
			record_id: record.acpxRecordId,
			name: record.name ?? null,
			agent: record.agentCommand,
			agent_name: normalizeAgentName(sessionLookup.agentName),
			cwd_relative: cwdRelative,
			cwd_original: cwdRelative,
			created_at: record.createdAt,
			updated_at: record.lastUsedAt,
			state: serializeSessionRecordForArchive(record, cwdRelative)
		},
		history: await listSessionEvents(record.acpxRecordId, record.eventLog.max_segments)
	};
	const target = await resolveExportTarget(outputPath);
	await writePrivateFile(target, `${JSON.stringify(exported, null, 2)}\n`, { privateDirectory: false });
}
async function resolveExportTarget(filePath) {
	for (let hops = 0; hops < 40; hops += 1) {
		const stat = await fs$1.lstat(filePath).catch((error) => {
			if (error.code === "ENOENT") return;
			throw error;
		});
		if (!stat || stat.isFile()) return filePath;
		if (!stat.isSymbolicLink()) throw new Error("Session export output must be a regular file");
		const target = await fs$1.readlink(filePath);
		filePath = path.isAbsolute(target) ? target : `${path.dirname(filePath)}${path.sep}${target}`;
	}
	throw new Error("Too many symbolic links in session export output");
}
function normalizeAgentName(agentName) {
	const normalized = agentName?.trim().toLowerCase();
	if (!normalized || normalized.length === 0) return;
	return normalized === "factory-droid" || normalized === "factorydroid" ? "droid" : normalized;
}
// src/session/import.ts
const SUPPORTED_FORMAT_VERSION = 1;
const exportedSessionSchema = z.object({
	format_version: z.literal(SUPPORTED_FORMAT_VERSION),
	exported_at: z.string(),
	exported_by: z.string(),
	session: z.object({
		record_id: z.string(),
		name: z.string().nullable(),
		agent: z.string(),
		agent_name: z.string().optional(),
		cwd_relative: z.string(),
		cwd_original: z.string().optional(),
		cwd_absolute_original: z.string().optional(),
		created_at: z.string(),
		updated_at: z.string(),
		state: z.unknown()
	}),
	history: z.array(z.unknown())
});
var SessionImportError = class extends AcpxOperationalError {
	code;
	exitCode = 2;
	constructor(message, code) {
		super(message, {
			outputCode: "USAGE",
			detailCode: code,
			origin: "cli"
		});
		this.code = code;
	}
};
function importError(message, code) {
	return new SessionImportError(message, code);
}
function parseArchiveJson(raw) {
	try {
		return JSON.parse(raw);
	} catch (error) {
		throw importError(`Invalid session export archive JSON: ${error instanceof Error ? error.message : String(error)}`, "invalid-archive");
	}
}
function assertSupportedFormatVersion(parsed) {
	const record = parsed && typeof parsed === "object" ? parsed : {};
	if (record.format_version !== SUPPORTED_FORMAT_VERSION) throw importError(`Unsupported session export format_version ${String(record.format_version)}; supported version is ${SUPPORTED_FORMAT_VERSION}`, "unsupported-format-version");
}
function parseArchive(raw) {
	const parsed = parseArchiveJson(raw);
	assertSupportedFormatVersion(parsed);
	try {
		return exportedSessionSchema.parse(parsed);
	} catch (error) {
		if (error instanceof ZodError) throw importError(`Invalid session export archive: ${error.issues[0]?.message}`, "invalid-archive");
		throw error;
	}
}
async function generateRecordId(sessionsDir) {
	for (;;) {
		const recordId = randomUUID();
		const filePath = path.join(sessionsDir, `${encodeURIComponent(recordId)}.json`);
		try {
			await fs$1.access(filePath);
		} catch (error) {
			if (error.code === "ENOENT") return recordId;
			throw error;
		}
	}
}
function resolveImportedCwd(cwdRelative, newCwd) {
	if (newCwd) return path.resolve(newCwd);
	if (path.isAbsolute(cwdRelative)) return cwdRelative;
	return path.join(os.homedir(), cwdRelative);
}
function resolveImportedName(parsed, requestedName) {
	return requestedName ?? parsed.session.name ?? void 0;
}
function assertExpectedAgentCommand(parsed, sourceRecord, options) {
	const expectedAgentCommand = options.expectedAgentCommand;
	if (!expectedAgentCommand) return;
	const expectedAgentName = normalizeAgentIdentity(options.expectedAgentName);
	const archiveAgentName = normalizeAgentIdentity(parsed.session.agent_name);
	const archiveCommandMatches = agentCommandMatchesExpected(parsed.session.agent, expectedAgentCommand, expectedAgentName);
	const stateCommandMatches = agentCommandMatchesExpected(sourceRecord.agentCommand, expectedAgentCommand, expectedAgentName);
	if (archiveCommandMatches && stateCommandMatches && archiveAgentNameMatches({
		archiveAgentName,
		expectedAgentName,
		archiveCommand: parsed.session.agent,
		stateCommand: sourceRecord.agentCommand,
		expectedAgentCommand
	})) {
		sourceRecord.agentCommand = expectedAgentCommand;
		return;
	}
	throw importError("Session export archive agent does not match the requested agent", "agent-mismatch");
}
function archiveAgentNameMatches(params) {
	if (params.archiveCommand === params.expectedAgentCommand && params.stateCommand === params.expectedAgentCommand) return true;
	return params.archiveAgentName == null || params.expectedAgentName == null || params.archiveAgentName === params.expectedAgentName;
}
function normalizeAgentIdentity(agentName) {
	const normalized = agentName?.trim().toLowerCase();
	if (!normalized || normalized.length === 0) return;
	return normalized === "factory-droid" || normalized === "factorydroid" ? "droid" : normalized;
}
function agentCommandMatchesExpected(archivedCommand, expectedAgentCommand, expectedAgentName) {
	if (archivedCommand === expectedAgentCommand || canonicalAgentCommand(archivedCommand) === expectedAgentCommand) return true;
	return expectedAgentName ? commandLooksLikeBuiltInAgent(archivedCommand, expectedAgentName) : false;
}
function commandLooksLikeBuiltInAgent(command, agentName) {
	const normalized = command.trim();
	switch (agentName) {
		case "pi": return /(?:^|\s|=)pi-acp(?:@|\s|$)/.test(normalized);
		case "codex": return /(?:^|\s|=)@agentclientprotocol\/codex-acp(?:@|\s|$)/.test(normalized);
		case "claude": return /(?:^|\s|=)@agentclientprotocol\/claude-agent-acp(?:@|\s|$)/.test(normalized);
		default: return false;
	}
}
async function assertDestinationScopeAvailable(record) {
	if (!await findSession({
		agentCommand: record.agentCommand,
		cwd: record.cwd,
		name: record.name
	})) return;
	throw importError("A session already exists for the import destination scope; pass --name or --cwd to import a separate copy", "session-scope-exists");
}
async function assertProviderSessionAvailable(record) {
	if (!(await listSessions()).find((session) => session.acpSessionId === record.acpSessionId)) return;
	throw importError("A local session already uses this provider session id; prune or remove the existing record before importing this archive", "session-provider-exists");
}
function buildImportedRecord(parsed, sourceRecord, options) {
	const eventLog = {
		...defaultSessionEventLog(options.newRecordId),
		max_segment_bytes: sourceRecord.eventLog.max_segment_bytes,
		max_segments: boundedEventMaxSegments(sourceRecord.eventLog.max_segments),
		segment_count: parsed.history.length > 0 ? 1 : sourceRecord.eventLog.segment_count
	};
	return {
		...sourceRecord,
		acpxRecordId: options.newRecordId,
		cwd: options.cwd,
		name: resolveImportedName(parsed, options.name),
		closed: false,
		closedAt: void 0,
		pid: void 0,
		agentStartedAt: void 0,
		lastAgentExitCode: void 0,
		lastAgentExitSignal: void 0,
		lastAgentExitAt: void 0,
		lastAgentDisconnectReason: void 0,
		eventLog,
		importedFrom: {
			recordId: parsed.session.record_id,
			cwdOriginal: parsed.session.cwd_original ?? parsed.session.cwd_relative,
			exportedBy: parsed.exported_by,
			exportedAt: parsed.exported_at
		}
	};
}
async function importSession(archivePath, options = {}) {
	try {
		var _usingCtx$1 = _usingCtx();
		const parsed = parseArchive(await fs$1.readFile(archivePath, "utf8"));
		const sourceRecord = parseSessionRecord(parsed.session.state);
		if (!sourceRecord) throw importError("Invalid session export archive: session.state is not a session record", "invalid-archive");
		assertExpectedAgentCommand(parsed, sourceRecord, options);
		const sessionsDir = path.join(os.homedir(), ".acpx", "sessions");
		await fs$1.mkdir(sessionsDir, { recursive: true });
		const cwd = resolveImportedCwd(parsed.session.cwd_relative, options.newCwd);
		const newRecordId = await generateRecordId(sessionsDir);
		const newRecord = buildImportedRecord(parsed, sourceRecord, {
			newRecordId,
			cwd,
			name: options.name
		});
		_usingCtx$1.a(await acquireSessionScope({
			agentCommand: newRecord.agentCommand,
			cwd: absolutePath(newRecord.cwd),
			name: normalizeName(newRecord.name)
		}));
		_usingCtx$1.a(await acquireSessionImport());
		await assertDestinationScopeAvailable(newRecord);
		await assertProviderSessionAvailable(newRecord);
		if (parsed.history.length > 0) {
			const history = parsed.history;
			await writePrivateFile(sessionEventActivePath(newRecordId), `${history.map((entry) => JSON.stringify(entry)).join("\n")}\n`);
		}
		await writeSessionRecord(newRecord);
		return {
			record_id: newRecordId,
			cwd
		};
	} catch (_) {
		_usingCtx$1.e = _;
	} finally {
		await _usingCtx$1.d();
	}
}
// src/cli/output/json-output.ts
function emitJsonResult(format, payload) {
	if (format !== "json") return false;
	process.stdout.write(`${JSON.stringify(payload)}\n`);
	return true;
}
// src/cli/prompt-input.ts
async function readPromptInput(filePath, promptText, cwd, positionalLabel = "argument") {
	try {
		if (filePath) return await readPromptFile(filePath, promptText, cwd);
		const joined = promptText.trim();
		if (joined.length > 0) return textPrompt(joined);
		if (process.stdin.isTTY) throw new InvalidArgumentError(`Prompt is required (pass as ${positionalLabel}, --file, or pipe via stdin)`);
		const prompt = parsePromptSource(await text(process.stdin));
		if (prompt.length === 0) throw new InvalidArgumentError("Prompt from stdin is empty");
		return prompt;
	} catch (error) {
		if (error instanceof PromptInputValidationError) throw new InvalidArgumentError(error.message);
		throw error;
	}
}
async function readPromptFile(filePath, promptText, cwd) {
	const source = filePath === "-" ? await text(process.stdin) : await fs$1.readFile(path.resolve(cwd, filePath), "utf8");
	const prompt = mergePromptSourceWithText(source, promptText);
	if (prompt.length === 0) throw new InvalidArgumentError("Prompt from --file is empty");
	return prompt;
}
// src/cli/command-handlers.ts
var NoSessionError = class extends Error {
	constructor(message) {
		super(message);
		this.name = "NoSessionError";
	}
};
let sessionModulePromise;
let outputModulePromise;
let outputRenderModulePromise;
function loadSessionModule() {
	sessionModulePromise ??= import("./output-C_T39Ap1.js").then((n) => n.s);
	return sessionModulePromise;
}
function loadOutputModule() {
	outputModulePromise ??= import("./output-C_T39Ap1.js").then((n) => n.r);
	return outputModulePromise;
}
function loadOutputRenderModule() {
	outputRenderModulePromise ??= Promise.resolve().then(() => render_exports);
	return outputRenderModulePromise;
}
function applyPermissionExitCode(result, quietOutput) {
	const stats = result.permissionStats;
	const deniedOrCancelled = stats.denied + stats.cancelled;
	if (stats.requested > 0 && stats.approved === 0 && deniedOrCancelled > 0) {
		process.exitCode = EXIT_CODES.PERMISSION_DENIED;
		quietOutput?.onError({
			code: "PERMISSION_DENIED",
			origin: "runtime",
			message: "Permission request denied or cancelled"
		});
		quietOutput?.flush();
	}
}
function resolveCompatibleConfigId(agent, configId) {
	if (isLegacyZedCodexAcpInvocation(agent.agentCommand) && configId === "thought_level") return "reasoning_effort";
	return configId;
}
function resolveRequestedOutputPolicy(globalFlags) {
	return {
		...resolveOutputPolicy(globalFlags.format, globalFlags.jsonStrict === true),
		suppressReads: globalFlags.suppressReads === true
	};
}
function buildSessionStartOptions(params) {
	return {
		...sessionConnectionOptions(params.globalFlags, params.config),
		agentCommand: params.agent.agentCommand,
		agentArgv: params.agent.agentArgv,
		cwd: params.agent.cwd,
		name: params.flags.name,
		resumeSessionId: params.flags.resumeSession,
		permissionMode: params.permissionMode,
		permissionPolicy: params.permissionPolicy,
		sessionOptions: sessionOptionsFromGlobalFlags(params.globalFlags),
		onModelWarning: params.globalFlags.jsonStrict ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
	};
}
function resolveSessionListFilterCwd(flags, agentCwd) {
	return flags.filterCwd ? path.resolve(agentCwd, flags.filterCwd) : void 0;
}
async function printLocalSessionsList(agentCommand, filterCwd, format) {
	const [{ listSessionsForAgent }, { printSessionsByFormat }] = await Promise.all([loadSessionModule(), loadOutputRenderModule()]);
	const sessions = await listSessionsForAgent(agentCommand);
	printSessionsByFormat(filterCwd ? sessions.filter((session) => session.cwd === filterCwd) : sessions, format);
}
function missingScopedSessionMessage(agent, sessionName) {
	return sessionName ? `No named session "${sessionName}" for cwd ${agent.cwd} and agent ${agent.agentName}` : `No cwd session for ${agent.cwd} and agent ${agent.agentName}`;
}
async function findScopedSessionOrThrow(agent, sessionName) {
	const record = await findSession({
		agentCommand: agent.agentCommand,
		cwd: agent.cwd,
		name: sessionName,
		includeClosed: true
	});
	if (!record) throw new Error(missingScopedSessionMessage(agent, sessionName));
	return record;
}
async function findRoutedSessionOrThrow(agentCommand, agentName, cwd, sessionName) {
	const walkBoundary = findGitRepositoryRoot(cwd) ?? cwd;
	const record = await findSessionByDirectoryWalk({
		agentCommand,
		cwd,
		name: sessionName,
		boundary: walkBoundary
	});
	if (record) return record;
	throw new NoSessionError(`⚠ No acpx session found (searched up to ${walkBoundary}).\nCreate one: ${sessionName ? `acpx ${agentName} sessions new --name ${sessionName}` : `acpx ${agentName} sessions new`}`);
}
async function handlePrompt(explicitAgentName, promptParts, flags, command, config) {
	const promptFlags = resolvePromptFlags(flags, command);
	const globalFlags = resolveGlobalFlags(command, config);
	const outputPolicy = resolveRequestedOutputPolicy(globalFlags);
	const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
	const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
	const prompt = await readPromptInput(promptFlags.file, promptParts.join(" "), globalFlags.cwd);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const [{ createOutputFormatter }, { printPromptSessionBanner, printQueuedPromptByFormat }, { sendSession }] = await Promise.all([
		loadOutputModule(),
		loadOutputRenderModule(),
		loadSessionModule()
	]);
	const record = await findRoutedSessionOrThrow(agent.agentCommand, agent.agentName, agent.cwd, resolveSessionNameFromFlags(flags, command));
	const outputFormatter = createOutputFormatter(outputPolicy.format, {
		jsonContext: { sessionId: record.acpxRecordId },
		suppressReads: outputPolicy.suppressReads
	});
	await printPromptSessionBanner(record, agent.cwd, outputPolicy.format, outputPolicy.jsonStrict);
	const result = await sendSession({
		...sessionConnectionOptions(globalFlags, config),
		sessionId: record.acpxRecordId,
		prompt,
		mcpConfigPath: config.mcpConfigPath,
		mcpConfigFingerprint: config.mcpConfigFingerprint,
		permissionMode,
		permissionPolicy,
		outputFormatter,
		errorEmissionPolicy: { queueErrorAlreadyEmitted: outputPolicy.queueErrorAlreadyEmitted },
		suppressSdkConsoleErrors: outputPolicy.suppressSdkConsoleErrors,
		ttlMs: globalFlags.ttl,
		maxQueueDepth: config.queueMaxDepth,
		promptRetries: globalFlags.promptRetries,
		waitForCompletion: promptFlags.wait,
		sessionOptions: sessionOptionsFromGlobalFlags(globalFlags)
	});
	if ("queued" in result) {
		printQueuedPromptByFormat(result, outputPolicy.format);
		return;
	}
	applyPermissionExitCode(result, outputPolicy.format === "quiet" ? outputFormatter : void 0);
	if (globalFlags.verbose && result.loadError) process.stderr.write(`[acpx] session reconnect failed, started fresh session: ${result.loadError}\n`);
}
async function handleExec(explicitAgentName, promptParts, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	if (config.disableExec) throw new AcpxOperationalError("exec subcommand is disabled by configuration (disableExec: true)", {
		outputCode: "EXEC_DISABLED",
		origin: "cli"
	});
	const outputPolicy = resolveRequestedOutputPolicy(globalFlags);
	const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
	const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
	const prompt = await readPromptInput(resolvePromptFlags(flags, command).file, promptParts.join(" "), globalFlags.cwd);
	const [{ createOutputFormatter }, { runOnce }] = await Promise.all([loadOutputModule(), loadSessionModule()]);
	const outputFormatter = createOutputFormatter(outputPolicy.format, { suppressReads: outputPolicy.suppressReads });
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	applyPermissionExitCode(await runOnce({
		...sessionConnectionOptions(globalFlags, config),
		agentCommand: agent.agentCommand,
		agentArgv: agent.agentArgv,
		cwd: agent.cwd,
		prompt,
		permissionMode,
		permissionPolicy,
		outputFormatter,
		errorEmissionPolicy: { queueErrorAlreadyEmitted: outputPolicy.queueErrorAlreadyEmitted },
		suppressSdkConsoleErrors: outputPolicy.suppressSdkConsoleErrors,
		promptRetries: globalFlags.promptRetries,
		sessionOptions: sessionOptionsFromGlobalFlags(globalFlags),
		configOptions: flags.configOption
	}), outputPolicy.format === "quiet" ? outputFormatter : void 0);
}
function printCancelResultByFormat(result, format) {
	if (emitJsonResult(format, {
		action: "cancel_result",
		acpxRecordId: result.sessionId || "unknown",
		cancelled: result.cancelled
	})) return;
	process.stdout.write(result.cancelled ? "cancel requested\n" : "nothing to cancel\n");
}
function printSetModeResultByFormat(modeId, result, format) {
	if (emitJsonResult(format, {
		action: "mode_set",
		modeId,
		resumed: result.resumed,
		acpxRecordId: result.record.acpxRecordId,
		acpxSessionId: result.record.acpSessionId,
		agentSessionId: result.record.agentSessionId
	})) return;
	process.stdout.write(format === "quiet" ? `${modeId}\n` : `mode set: ${modeId}\n`);
}
function printSetModelResultByFormat(modelId, result, format) {
	if (emitJsonResult(format, {
		action: "model_set",
		modelId,
		resumed: result.resumed,
		acpxRecordId: result.record.acpxRecordId,
		acpxSessionId: result.record.acpSessionId,
		agentSessionId: result.record.agentSessionId
	})) return;
	process.stdout.write(format === "quiet" ? `${modelId}\n` : `model set: ${modelId}\n`);
}
function printSetConfigOptionResultByFormat(configId, value, result, format) {
	const options = result.response?.configOptions;
	const configOptions = Array.isArray(options) ? options : result.record.acpx?.config_options ?? [];
	if (emitJsonResult(format, {
		action: "config_set",
		configId,
		value,
		resumed: result.resumed,
		configOptions,
		acpxRecordId: result.record.acpxRecordId,
		acpxSessionId: result.record.acpSessionId,
		agentSessionId: result.record.agentSessionId
	})) return;
	process.stdout.write(format === "quiet" ? `${value}\n` : `config set: ${configId}=${value} (${configOptions.length} options)\n`);
}
async function handleCancel(explicitAgentName, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const { cancelSessionPrompt } = await loadSessionModule();
	const walkBoundary = findGitRepositoryRoot(agent.cwd) ?? agent.cwd;
	const record = await findSessionByDirectoryWalk({
		agentCommand: agent.agentCommand,
		cwd: agent.cwd,
		name: resolveSessionNameFromFlags(flags, command),
		boundary: walkBoundary
	});
	if (!record) {
		printCancelResultByFormat({
			sessionId: "",
			cancelled: false
		}, globalFlags.format);
		return;
	}
	printCancelResultByFormat(await cancelSessionPrompt({
		sessionId: record.acpxRecordId,
		verbose: globalFlags.verbose
	}), globalFlags.format);
}
async function handleSetMode(explicitAgentName, modeId, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const { setSessionMode } = await loadSessionModule();
	const record = await findRoutedSessionOrThrow(agent.agentCommand, agent.agentName, agent.cwd, resolveSessionNameFromFlags(flags, command));
	const result = await setSessionMode({
		...sessionConnectionOptions(globalFlags, config),
		sessionId: record.acpxRecordId,
		modeId
	});
	if (globalFlags.verbose && result.loadError) process.stderr.write(`[acpx] session reconnect failed, started fresh session: ${result.loadError}\n`);
	printSetModeResultByFormat(modeId, result, globalFlags.format);
}
async function handleSetModel(explicitAgentName, modelId, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const { setSessionModel } = await loadSessionModule();
	const record = await findRoutedSessionOrThrow(agent.agentCommand, agent.agentName, agent.cwd, resolveSessionNameFromFlags(flags, command));
	const result = await setSessionModel({
		...sessionConnectionOptions(globalFlags, config),
		sessionId: record.acpxRecordId,
		modelId
	});
	if (globalFlags.verbose && result.loadError) process.stderr.write(`[acpx] session reconnect failed, started fresh session: ${result.loadError}\n`);
	printSetModelResultByFormat(modelId, result, globalFlags.format);
}
async function handleSetConfigOption(explicitAgentName, configId, value, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	if (configId === "model") {
		await handleSetModel(explicitAgentName, value, flags, command, config);
		return;
	}
	const resolvedConfigId = resolveCompatibleConfigId(agent, configId);
	const { setSessionConfigOption } = await loadSessionModule();
	const record = await findRoutedSessionOrThrow(agent.agentCommand, agent.agentName, agent.cwd, resolveSessionNameFromFlags(flags, command));
	const result = await setSessionConfigOption({
		...sessionConnectionOptions(globalFlags, config),
		sessionId: record.acpxRecordId,
		configId: resolvedConfigId,
		value
	});
	if (globalFlags.verbose && result.loadError) process.stderr.write(`[acpx] session reconnect failed, started fresh session: ${result.loadError}\n`);
	printSetConfigOptionResultByFormat(configId, value, result, globalFlags.format);
}
async function tryListAgentSessions(agent, flags, globalFlags, config) {
	const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
	const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
	const { listAgentSessions } = await loadSessionModule();
	try {
		return await listAgentSessions({
			...sessionConnectionOptions(globalFlags, config),
			agentCommand: agent.agentCommand,
			agentArgv: agent.agentArgv,
			cwd: agent.cwd,
			cursor: flags.cursor,
			filterCwd: resolveSessionListFilterCwd(flags, agent.cwd),
			permissionMode,
			permissionPolicy
		});
	} catch (error) {
		if (error instanceof AgentSpawnError) return "spawn-failed";
		throw error;
	}
}
async function handleSessionsList(explicitAgentName, flags, command, config) {
	const listFlags = resolveSessionsListFlags(flags, command);
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const filterCwd = resolveSessionListFilterCwd(listFlags, agent.cwd);
	if (listFlags.local) {
		if (listFlags.cursor) throw new InvalidArgumentError("--cursor cannot be combined with --local");
		await printLocalSessionsList(agent.agentCommand, filterCwd, globalFlags.format);
		return;
	}
	const [result, { printAgentSessionsByFormat }] = await Promise.all([tryListAgentSessions(agent, listFlags, globalFlags, config), loadOutputRenderModule()]);
	if (!result || result === "spawn-failed") {
		if (result !== "spawn-failed" && (listFlags.cursor || listFlags.filterCwd)) throw new Error(`Agent command "${agent.agentCommand}" does not advertise sessionCapabilities.list; cannot use agent-side session/list filters`);
		await printLocalSessionsList(agent.agentCommand, void 0, globalFlags.format);
		return;
	}
	printAgentSessionsByFormat(result, globalFlags.format);
}
async function handleSessionsClose(explicitAgentName, sessionName, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const [{ closeSession }, { printClosedSessionByFormat }] = await Promise.all([loadSessionModule(), loadOutputRenderModule()]);
	const record = await findSession({
		agentCommand: agent.agentCommand,
		cwd: agent.cwd,
		name: sessionName
	});
	if (!record) throw new Error(missingScopedSessionMessage(agent, sessionName));
	printClosedSessionByFormat(await closeSession(record.acpxRecordId), globalFlags.format);
}
async function handleSessionsNew(explicitAgentName, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
	const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const [{ createSession, closeSession }, { printCreatedSessionBanner, printNewSessionByFormat }] = await Promise.all([loadSessionModule(), loadOutputRenderModule()]);
	const replaced = await findSession({
		agentCommand: agent.agentCommand,
		cwd: agent.cwd,
		name: flags.name
	});
	const resumesSameRecord = replaced != null && replaced.acpxRecordId === flags.resumeSession;
	const created = await createSession(buildSessionStartOptions({
		agent,
		flags,
		globalFlags,
		config,
		permissionMode,
		permissionPolicy
	}));
	if (replaced) {
		if (!resumesSameRecord) await closeSession(replaced.acpxRecordId);
		if (globalFlags.verbose) process.stderr.write(`[acpx] soft-closed prior session: ${replaced.acpxRecordId}\n`);
	}
	printCreatedSessionBanner(created, agent.agentName, globalFlags.format, globalFlags.jsonStrict);
	if (globalFlags.verbose) {
		const scope = flags.name ? `named session "${flags.name}"` : "cwd session";
		process.stderr.write(`[acpx] created ${scope}: ${created.acpxRecordId}\n`);
	}
	printNewSessionByFormat(created, replaced, globalFlags.format);
}
async function handleSessionsEnsure(explicitAgentName, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
	const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const [{ ensureSession }, { printCreatedSessionBanner, printEnsuredSessionByFormat }] = await Promise.all([loadSessionModule(), loadOutputRenderModule()]);
	const result = await ensureSession(buildSessionStartOptions({
		agent,
		flags,
		globalFlags,
		config,
		permissionMode,
		permissionPolicy
	}));
	if (result.created) printCreatedSessionBanner(result.record, agent.agentName, globalFlags.format, globalFlags.jsonStrict);
	printEnsuredSessionByFormat(result.record, result.created, globalFlags.format);
}
function userContentToText(content) {
	if ("Text" in content) return content.Text;
	if ("Mention" in content) return content.Mention.content;
	if ("Image" in content) return `[image] ${content.Image.mime_type || "image"}`;
	if ("Audio" in content) return `[audio] ${content.Audio.mime_type || "audio"}`;
	return "";
}
function agentContentToText(content) {
	if ("Text" in content) return content.Text;
	if ("Thinking" in content) return content.Thinking.text;
	if ("RedactedThinking" in content) return "[redacted_thinking]";
	if ("ToolUse" in content) return `[tool:${content.ToolUse.name}]`;
	return "";
}
function conversationHistoryEntries(record) {
	const entries = [];
	for (const message of record.messages) {
		if (message === "Resume") continue;
		if ("User" in message) {
			const text = message.User.content.map((entry) => userContentToText(entry)).join(" ").trim();
			if (!text) continue;
			entries.push({
				role: "user",
				timestamp: record.updated_at,
				textPreview: text
			});
			continue;
		}
		if ("Agent" in message) {
			const text = message.Agent.content.map((entry) => agentContentToText(entry)).join(" ").trim();
			if (!text) continue;
			entries.push({
				role: "assistant",
				timestamp: record.updated_at,
				textPreview: text
			});
		}
	}
	return entries;
}
function printSessionDetailsByFormat(record, format) {
	if (format === "json") {
		process.stdout.write(`${JSON.stringify(record)}\n`);
		return;
	}
	if (format === "quiet") {
		process.stdout.write(`${record.acpxRecordId}\n`);
		return;
	}
	for (const line of sessionDetailsLines(record)) process.stdout.write(`${line}\n`);
}
function sessionDetailsLines(record) {
	return [
		`id: ${record.acpxRecordId}`,
		`sessionId: ${record.acpSessionId}`,
		`agentSessionId: ${displayValue(record.agentSessionId)}`,
		`agent: ${record.agentCommand}`,
		`cwd: ${record.cwd}`,
		`name: ${displayValue(record.name)}`,
		`created: ${record.createdAt}`,
		`lastActivity: ${record.lastUsedAt}`,
		`lastPrompt: ${displayValue(record.lastPromptAt)}`,
		`closed: ${record.closed ? "yes" : "no"}`,
		`closedAt: ${displayValue(record.closedAt)}`,
		`pid: ${displayValue(record.pid)}`,
		`agentStartedAt: ${displayValue(record.agentStartedAt)}`,
		`lastExitCode: ${displayValue(record.lastAgentExitCode)}`,
		`lastExitSignal: ${displayValue(record.lastAgentExitSignal)}`,
		`lastExitAt: ${displayValue(record.lastAgentExitAt)}`,
		`disconnectReason: ${displayValue(record.lastAgentDisconnectReason)}`,
		`historyEntries: ${conversationHistoryEntries(record).length}`
	];
}
function displayValue(value) {
	return value == null ? "-" : String(value);
}
function printSessionHistoryByFormat(record, limit, format) {
	const history = conversationHistoryEntries(record);
	const visible = limit === 0 ? history : history.slice(Math.max(0, history.length - limit));
	if (format === "json") {
		process.stdout.write(`${JSON.stringify({
			id: record.acpxRecordId,
			sessionId: record.acpSessionId,
			limit,
			count: visible.length,
			entries: visible
		})}\n`);
		return;
	}
	if (format === "quiet") {
		for (const entry of visible) process.stdout.write(`${entry.textPreview}\n`);
		return;
	}
	process.stdout.write(`session: ${record.acpxRecordId} (${visible.length}/${history.length} shown)\n`);
	if (visible.length === 0) {
		process.stdout.write("No history\n");
		return;
	}
	for (const entry of visible) process.stdout.write(`${entry.timestamp}\t${entry.role}\t${entry.textPreview}\n`);
}
async function handleSessionsShow(explicitAgentName, sessionName, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	printSessionDetailsByFormat(await findScopedSessionOrThrow(resolveAgentInvocation(explicitAgentName, globalFlags, config), sessionName), globalFlags.format);
}
async function handleSessionsHistory(explicitAgentName, sessionName, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	printSessionHistoryByFormat(await findScopedSessionOrThrow(resolveAgentInvocation(explicitAgentName, globalFlags, config), sessionName), flags.limit, globalFlags.format);
}
async function handleSessionsWatch(explicitAgentName, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const sessionName = resolveSessionNameFromFlags({ session: flags.name }, command);
	const scope = {
		agentCommand: agent.agentCommand,
		cwd: agent.cwd,
		name: sessionName,
		readOnly: true
	};
	const record = await findSession(scope) ?? await findSession({
		...scope,
		includeClosed: true
	});
	if (!record) throw new Error(missingScopedSessionMessage(agent, sessionName));
	const { runSessionWatch } = await import("./session-watch-D0YvKsnQ.js");
	await runSessionWatch(record, {
		cursor: flags.cursor,
		policy: resolveRequestedOutputPolicy(globalFlags)
	});
}
async function handleSessionsExport(explicitAgentName, sessionName, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const cwd = flags.sourceCwd ? path.resolve(agent.cwd, flags.sourceCwd) : agent.cwd;
	await exportSession({
		agentName: globalFlags.agent ? void 0 : agent.agentName,
		agentCommand: agent.agentCommand,
		cwd,
		name: sessionName
	}, flags.output);
	if (emitJsonResult(globalFlags.format, {
		action: "session_exported",
		output: flags.output
	})) return;
	if (globalFlags.format === "quiet") {
		process.stdout.write(`${flags.output}\n`);
		return;
	}
	process.stdout.write(`exported session to ${flags.output}\n`);
}
async function handleSessionsImport(explicitAgentName, archivePath, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const result = await importSession(archivePath, {
		name: flags.name,
		newCwd: flags.destinationCwd ? path.resolve(globalFlags.cwd, flags.destinationCwd) : void 0,
		expectedAgentName: globalFlags.agent ? void 0 : agent.agentName,
		expectedAgentCommand: agent.agentCommand
	});
	if (emitJsonResult(globalFlags.format, {
		action: "session_imported",
		record_id: result.record_id,
		cwd: result.cwd
	})) return;
	if (globalFlags.format === "quiet") {
		process.stdout.write(`${result.record_id}\n`);
		return;
	}
	process.stdout.write(`imported session ${result.record_id} at ${result.cwd}\n`);
}
async function handleSessionsPrune(explicitAgentName, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const [{ pruneSessions }, { printPruneResultByFormat }] = await Promise.all([loadSessionModule(), loadOutputRenderModule()]);
	const olderThanMs = flags.olderThan != null ? flags.olderThan * 24 * 60 * 60 * 1e3 : void 0;
	printPruneResultByFormat(await pruneSessions({
		agentCommand: agent.agentCommand,
		before: flags.before,
		olderThanMs,
		includeHistory: flags.includeHistory,
		dryRun: flags.dryRun
	}), globalFlags.format);
}
// src/cli/compare-args.ts
function addCompareOptions(command) {
	return command.option("--cwd <dir>", "Target workspace").option("--approve-all", "Auto-approve all permission requests").option("--approve-reads", "Auto-approve read/search requests and prompt for writes").option("--deny-all", "Deny all permission requests").option("--timeout <seconds>", "Per-agent timeout in seconds", parseTimeoutSeconds).option("--format <fmt>", "Output format: text, json, quiet", parseOutputFormat$1).option("--json", "Alias for --format json").option("-f, --file <path>", "Read prompt text from file path (use - for stdin)", (value) => parseNonEmptyValue("Prompt file", value)).option("--prompt-file <path>", "Alias for --file", (value) => parseNonEmptyValue("Prompt file", value));
}
const VALUE_FLAGS = new Set(addCompareOptions(new Command()).options.filter((option) => option.required).flatMap((option) => [option.long, option.short].filter((flag) => flag !== void 0)));
function captureCompareOutputFlag(output, token, nextToken) {
	if (token === "--json") output.json = true;
	else if (token === "--format") output.format = nextToken;
	else if (token.startsWith("--format=")) output.format = token.slice(9);
}
function scanCompareArgs(argv) {
	let cwd;
	const output = {};
	for (let index = 0; index < argv.length; index += 1) {
		const token = argv[index];
		if (token === "--") return {
			cwd,
			...output,
			promptTokens: argv.slice(index + 1)
		};
		if (token === "--cwd") cwd = argv[index + 1];
		else if (token.startsWith("--cwd=")) cwd = token.slice(6);
		captureCompareOutputFlag(output, token, argv[index + 1]);
		if (VALUE_FLAGS.has(token)) index += 1;
	}
	return {
		cwd,
		...output
	};
}
// src/cli/compare-command.ts
const DEFAULT_COMPARE_TIMEOUT_MS = 3e5;
const FINAL_MESSAGE_PREVIEW_CHARS = 200;
function asRecord$2(value) {
	return Boolean(value) && typeof value === "object" && !Array.isArray(value) ? value : void 0;
}
function numberField(source, keys) {
	if (!source) return null;
	for (const key of keys) {
		const value = source[key];
		if (typeof value === "number" && Number.isFinite(value)) return value;
	}
	return null;
}
function collapseWhitespace(value) {
	return value.replace(/\s+/g, " ").trim();
}
function truncate(value, maxChars) {
	if (value.length <= maxChars) return value;
	return `${value.slice(0, Math.max(0, maxChars - 3))}...`;
}
function splitCompareArgs(args, filePath, promptTokens) {
	if (promptTokens !== void 0) {
		const agents = args.slice(0, args.length - promptTokens.length);
		if (agents.length === 0) throw new InvalidArgumentError("At least one agent is required");
		return {
			agents,
			promptText: promptTokens.join(" ")
		};
	}
	if (filePath) {
		if (args.length === 0) throw new InvalidArgumentError("At least one agent is required");
		return {
			agents: args,
			promptText: ""
		};
	}
	if (args.length < 2) throw new InvalidArgumentError("Usage: acpx compare <agent>... '<prompt>'");
	return {
		agents: args.slice(0, -1),
		promptText: args[args.length - 1] ?? ""
	};
}
function captureUsage(update, capture) {
	const source = asRecord$2(asRecord$2(update._meta)?.usage) ?? update;
	capture.usage = {
		input_tokens: numberField(source, ["input_tokens", "inputTokens"]) ?? void 0,
		output_tokens: numberField(source, ["output_tokens", "outputTokens"]) ?? void 0,
		total_tokens: numberField(source, ["total_tokens", "totalTokens"]) ?? void 0
	};
}
function captureSessionUpdate(notification, capture) {
	const update = asRecord$2(notification.update);
	if (!update) return;
	if (update.sessionUpdate === "agent_message_chunk") {
		const content = asRecord$2(update.content);
		if (content?.type === "text" && typeof content.text === "string") capture.finalMessage += content.text;
		return;
	}
	if (update.sessionUpdate === "usage_update") captureUsage(update, capture);
}
function rowStatusFromResult(result) {
	if (result.stopReason === "cancelled") return "cancelled";
	return result.permissionStats.denied + result.permissionStats.cancelled > 0 ? "permission_denied" : "ok";
}
function rowStatusFromError(error) {
	if (error instanceof TimeoutError) return "cancelled";
	const { code } = normalizeOutputError(error);
	return code === "PERMISSION_PROMPT_UNAVAILABLE" || code === "PERMISSION_DENIED" ? "permission_denied" : "error";
}
async function runAgentForCompare(params) {
	const capture = {
		finalMessage: "",
		usage: {},
		permissionStats: {
			requested: 0,
			approved: 0,
			denied: 0,
			cancelled: 0
		}
	};
	const t0 = performance.now();
	let status;
	let stopReason = null;
	let error = null;
	let permissionStats;
	let meta;
	try {
		const agent = resolveAgentInvocation(params.agentName, params.globalFlags, params.config);
		const result = await runOnce({
			...sessionConnectionOptions(params.globalFlags, params.config),
			agentCommand: agent.agentCommand,
			agentArgv: agent.agentArgv,
			cwd: agent.cwd,
			prompt: params.prompt,
			permissionMode: params.permissionMode,
			permissionPolicy: params.permissionPolicy,
			outputFormatter: DISCARD_OUTPUT_FORMATTER,
			suppressSdkConsoleErrors: true,
			timeoutMs: params.globalFlags.timeout ?? DEFAULT_COMPARE_TIMEOUT_MS,
			promptRetries: params.globalFlags.promptRetries,
			sessionOptions: sessionOptionsFromGlobalFlags(params.globalFlags),
			onSessionUpdate: (notification) => captureSessionUpdate(notification, capture),
			onPermissionStats: (stats) => {
				capture.permissionStats = stats;
			}
		});
		permissionStats = result.permissionStats;
		status = rowStatusFromResult(result);
		stopReason = result.stopReason;
		meta = result._meta;
	} catch (caught) {
		permissionStats = capture.permissionStats;
		status = rowStatusFromError(caught);
		error = truncate(collapseWhitespace(caught instanceof Error ? caught.message : String(caught)), FINAL_MESSAGE_PREVIEW_CHARS);
	}
	return {
		agent: params.agentName,
		status,
		stop_reason: stopReason,
		wall_ms: Math.round(performance.now() - t0),
		input_tokens: capture.usage.input_tokens ?? null,
		output_tokens: capture.usage.output_tokens ?? null,
		total_tokens: capture.usage.total_tokens ?? null,
		final_message: truncate(collapseWhitespace(capture.finalMessage), FINAL_MESSAGE_PREVIEW_CHARS),
		error,
		permission_requests: permissionStats.requested,
		permission_denied: permissionStats.denied + permissionStats.cancelled,
		...meta === void 0 ? {} : { _meta: meta }
	};
}
function formatCell(value) {
	if (value == null || value === "") return "-";
	if (typeof value === "string") return collapseWhitespace(value);
	if (typeof value === "number" || typeof value === "boolean") return String(value);
	return collapseWhitespace(JSON.stringify(value));
}
function renderTable(rows) {
	const headers = [
		"agent",
		"status",
		"wall_ms",
		"input",
		"output",
		"total",
		"permissions",
		"stop_reason",
		"final_message",
		"error"
	];
	const body = rows.map((row) => [
		row.agent,
		row.status,
		row.wall_ms,
		row.input_tokens,
		row.output_tokens,
		row.total_tokens,
		`${row.permission_denied}/${row.permission_requests}`,
		row.stop_reason,
		row.final_message,
		row.error
	]);
	const widths = headers.map((header, index) => Math.max(header.length, ...body.map((cells) => formatCell(cells[index]).length)));
	const formatRow = (cells) => cells.map((cell, index) => truncate(formatCell(cell), widths[index] ?? 24).padEnd(widths[index] ?? 24)).join("  ").trimEnd();
	return [
		formatRow(headers),
		widths.map((width) => "-".repeat(width)).join("  "),
		...body.map(formatRow)
	].join("\n");
}
function printRows(rows, format) {
	if (format === "json") {
		process.stdout.write(`${JSON.stringify(rows)}\n`);
		return;
	}
	if (format === "quiet") {
		for (const row of rows) process.stdout.write(`${row.agent}\t${row.status}\n`);
		return;
	}
	process.stdout.write(`${renderTable(rows)}\n`);
}
function updateCompareExitCode(rows) {
	if (rows.some((row) => row.status === "error")) {
		process.exitCode = EXIT_CODES.ERROR;
		return;
	}
	if (rows.some((row) => row.status === "permission_denied")) {
		process.exitCode = EXIT_CODES.PERMISSION_DENIED;
		return;
	}
	if (rows.some((row) => row.status === "cancelled")) process.exitCode = EXIT_CODES.TIMEOUT;
}
async function runCompareAgents(agents, run) {
	const rows = [];
	let interrupted = false;
	const onInterrupt = () => {
		interrupted = true;
	};
	const signals = [
		"SIGINT",
		"SIGTERM",
		"SIGHUP"
	];
	for (const signal of signals) process.once(signal, onInterrupt);
	try {
		for (const agentName of agents) {
			if (interrupted) break;
			const row = await run(agentName);
			rows.push(interrupted ? {
				...row,
				status: "cancelled",
				error: "Interrupted"
			} : row);
		}
	} finally {
		for (const signal of signals) process.off(signal, onInterrupt);
	}
	return {
		rows,
		interrupted
	};
}
function resolvePromptFile(flags) {
	if (flags.file && flags.promptFile && flags.file !== flags.promptFile) throw new InvalidArgumentError("Use only one prompt file flag: --file or --prompt-file");
	return flags.file ?? flags.promptFile;
}
function registerCompareCommand(program, config) {
	addCompareOptions(program.command("compare").description("Run one prompt across multiple agents and summarize the results").argument("<args...>", "Agents followed by prompt text, or agents with --file")).action(async function(args, flags) {
		if (config.disableExec) throw new Error("compare subcommand is disabled by configuration (disableExec: true)");
		const globalFlags = resolveGlobalFlags(this, config);
		if (flags.cwd !== void 0) globalFlags.cwd = path.resolve(flags.cwd);
		if (globalFlags.agent) throw new InvalidArgumentError("Do not combine compare with --agent; pass agent names");
		const outputPolicy = resolveOutputPolicy(globalFlags.format, globalFlags.jsonStrict === true);
		const promptFile = resolvePromptFile(flags);
		const { promptTokens } = scanCompareArgs(program.args.slice(1));
		const { agents, promptText } = splitCompareArgs(args, promptFile, promptTokens);
		const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
		const prompt = await readPromptInput(promptFile, promptText, globalFlags.cwd, "final argument");
		const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
		const { rows, interrupted } = await runCompareAgents(agents, async (agentName) => await runAgentForCompare({
			agentName,
			prompt,
			config,
			globalFlags,
			permissionMode,
			permissionPolicy
		}));
		printRows(rows, outputPolicy.format);
		if (interrupted) process.exitCode = EXIT_CODES.INTERRUPTED;
		else updateCompareExitCode(rows);
	});
}
// src/mcp-servers.ts
function asRecord$1(value) {
	if (!value || typeof value !== "object" || Array.isArray(value)) return;
	return value;
}
function parseNonEmptyString(value, path) {
	if (typeof value !== "string" || value.trim().length === 0) throw new Error(`Invalid ${path}: expected non-empty string`);
	return value.trim();
}
function parseString(value, path) {
	if (typeof value !== "string") throw new Error(`Invalid ${path}: expected string`);
	return value;
}
function parseNameValuePairs(value, path) {
	if (value == null) return [];
	if (!Array.isArray(value)) throw new Error(`Invalid ${path}: expected array`);
	const entries = [];
	for (const [index, rawEntry] of value.entries()) {
		const entry = asRecord$1(rawEntry);
		if (!entry) throw new Error(`Invalid ${path}[${index}]: expected object`);
		entries.push({
			name: parseNonEmptyString(entry.name, `${path}[${index}].name`),
			value: parseString(entry.value, `${path}[${index}].value`)
		});
	}
	return entries;
}
function parseArgs(value, path) {
	if (value == null) return [];
	if (!Array.isArray(value)) throw new Error(`Invalid ${path}: expected array`);
	const args = [];
	for (const [index, rawArg] of value.entries()) args.push(parseString(rawArg, `${path}[${index}]`));
	return args;
}
function parseMeta(value, path) {
	if (value === void 0) return;
	if (value === null) return null;
	if (!asRecord$1(value)) throw new Error(`Invalid ${path}: expected object or null`);
	return value;
}
function parseServerType(rawType, path) {
	if (rawType === void 0) return "stdio";
	const parsedType = parseNonEmptyString(rawType, `${path}.type`);
	if (parsedType !== "http" && parsedType !== "sse" && parsedType !== "stdio") throw new Error(`Invalid ${path}.type: expected http, sse, or stdio`);
	return parsedType;
}
function parseHttpServer(serverRecord, path, type, name, _meta) {
	return {
		type,
		name,
		url: parseNonEmptyString(serverRecord.url, `${path}.url`),
		headers: parseNameValuePairs(serverRecord.headers, `${path}.headers`),
		_meta
	};
}
function parseStdioServer(serverRecord, path, name, _meta) {
	return {
		name,
		command: parseNonEmptyString(serverRecord.command, `${path}.command`),
		args: parseArgs(serverRecord.args, `${path}.args`),
		env: parseNameValuePairs(serverRecord.env, `${path}.env`),
		_meta
	};
}
function parseServer(rawServer, path) {
	const serverRecord = asRecord$1(rawServer);
	if (!serverRecord) throw new Error(`Invalid ${path}: expected object`);
	const name = parseNonEmptyString(serverRecord.name, `${path}.name`);
	const _meta = parseMeta(serverRecord._meta, `${path}._meta`);
	const typeValue = parseServerType(serverRecord.type, path);
	if (typeValue === "http" || typeValue === "sse") return parseHttpServer(serverRecord, path, typeValue, name, _meta);
	return parseStdioServer(serverRecord, path, name, _meta);
}
function parseMcpServers(value, sourcePath, fieldName = "mcpServers") {
	const fieldPath = `${fieldName} in ${sourcePath}`;
	if (!Array.isArray(value)) throw new Error(`Invalid ${fieldPath}: expected array`);
	const parsed = [];
	for (const [index, rawServer] of value.entries()) parsed.push(parseServer(rawServer, `${fieldName}[${index}] in ${sourcePath}`));
	return parsed;
}
function parseOptionalMcpServers(value, sourcePath, fieldName = "mcpServers") {
	if (value === void 0) return;
	return parseMcpServers(value, sourcePath, fieldName);
}
// src/cli/config.ts
const DEFAULT_TTL_MS = 3e5;
const DEFAULT_PERMISSION_MODE = "approve-reads";
const DEFAULT_NON_INTERACTIVE_PERMISSION_POLICY = "deny";
const DEFAULT_AUTH_POLICY = "skip";
const DEFAULT_OUTPUT_FORMAT = "text";
const DEFAULT_QUEUE_MAX_DEPTH = 16;
const DEFAULT_DISABLE_EXEC = false;
const VALID_PERMISSION_MODES = /* @__PURE__ */ new Set([
	"approve-all",
	"approve-reads",
	"deny-all"
]);
const VALID_NON_INTERACTIVE_PERMISSION_POLICIES = /* @__PURE__ */ new Set(["deny", "fail"]);
const VALID_AUTH_POLICIES = /* @__PURE__ */ new Set(["skip", "fail"]);
const VALID_OUTPUT_FORMATS = /* @__PURE__ */ new Set([
	"text",
	"json",
	"quiet"
]);
function defaultGlobalConfigPath() {
	return path.join(os.homedir(), ".acpx", "config.json");
}
function projectConfigPath(cwd) {
	return path.join(path.resolve(cwd), ".acpxrc.json");
}
function isObject(value) {
	return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}
function parseTtlMs(value, sourcePath) {
	if (value == null) return;
	if (typeof value !== "number" || !Number.isFinite(value) || value < 0) throw new Error(`Invalid config ttl in ${sourcePath}: expected non-negative seconds`);
	const milliseconds = toTimerMilliseconds(value, true);
	if (milliseconds === void 0) throw new Error(`Invalid config ttl in ${sourcePath}: exceeds maximum supported timer delay`);
	return milliseconds;
}
function parseTimeoutMs(value, sourcePath) {
	if (value == null) return;
	if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) throw new Error(`Invalid config timeout in ${sourcePath}: expected positive seconds or null`);
	const milliseconds = toTimerMilliseconds(value, false);
	if (milliseconds === void 0) throw new Error(`Invalid config timeout in ${sourcePath}: exceeds maximum supported timer delay`);
	return milliseconds;
}
function parseQueueMaxDepth(value, sourcePath) {
	if (value == null) return;
	if (!Number.isInteger(value) || value <= 0) throw new Error(`Invalid config queueMaxDepth in ${sourcePath}: expected positive integer`);
	return value;
}
function parsePermissionMode(value, sourcePath) {
	if (value == null) return;
	if (typeof value !== "string" || !VALID_PERMISSION_MODES.has(value)) throw new Error(`Invalid config defaultPermissions in ${sourcePath}: expected approve-all, approve-reads, or deny-all`);
	return value;
}
function parseNonInteractivePermissionPolicy(value, sourcePath) {
	if (value == null) return;
	if (typeof value !== "string" || !VALID_NON_INTERACTIVE_PERMISSION_POLICIES.has(value)) throw new Error(`Invalid config nonInteractivePermissions in ${sourcePath}: expected deny or fail`);
	return value;
}
function parseAuthPolicy(value, sourcePath) {
	if (value == null) return;
	if (typeof value !== "string" || !VALID_AUTH_POLICIES.has(value)) throw new Error(`Invalid config authPolicy in ${sourcePath}: expected skip or fail`);
	return value;
}
function parseOutputFormat(value, sourcePath) {
	if (value == null) return;
	if (typeof value !== "string" || !VALID_OUTPUT_FORMATS.has(value)) throw new Error(`Invalid config format in ${sourcePath}: expected text, json, or quiet`);
	return value;
}
function parseDefaultAgent(value, sourcePath) {
	if (value == null) return;
	if (typeof value !== "string" || value.trim().length === 0) throw new Error(`Invalid config defaultAgent in ${sourcePath}: expected non-empty string`);
	return normalizeAgentName$1(value);
}
function parseAgents(value, sourcePath) {
	if (value == null) return;
	if (!isObject(value)) throw new Error(`Invalid config agents in ${sourcePath}: expected object`);
	return Object.fromEntries(Object.entries(value).map(([name, raw]) => [normalizeAgentName$1(name), parseAgentEntry(raw, name, sourcePath)]));
}
function parseAgentEntry(raw, name, sourcePath) {
	if (!isObject(raw)) throw new Error(`Invalid config agents.${name} in ${sourcePath}: expected object with command`);
	return Object.prototype.hasOwnProperty.call(raw, "argv") ? parseArgvAgentEntry(raw, name, sourcePath) : parseLegacyAgentEntry(raw, name, sourcePath);
}
function parseArgvAgentEntry(raw, name, sourcePath) {
	if (Object.prototype.hasOwnProperty.call(raw, "command") || Object.prototype.hasOwnProperty.call(raw, "args")) throw new Error(`Invalid config agents.${name} in ${sourcePath}: use argv alone, not command or args`);
	const argv = parseAgentArgv(raw.argv, name, sourcePath);
	return {
		command: renderArgvIdentity(argv),
		argv
	};
}
function parseLegacyAgentEntry(raw, name, sourcePath) {
	const command = raw.command;
	if (typeof command !== "string" || command.trim().length === 0) throw new Error(`Invalid config agents.${name}.command in ${sourcePath}: expected non-empty string`);
	const trimmedCommand = command.trim();
	if (!Object.prototype.hasOwnProperty.call(raw, "args")) return { command: trimmedCommand };
	if (/[\s'"]/u.test(trimmedCommand)) throw new Error(`Invalid config agents.${name}.command in ${sourcePath}: command must be an unquoted executable with no whitespace when args is present; migrate the complete launch to argv`);
	const args = parseAgentArgs(raw.args, name, sourcePath);
	return {
		command: args.length > 0 ? `${trimmedCommand} ${args.map(quoteCommandArg).join(" ")}` : trimmedCommand,
		argv: [trimmedCommand, ...args]
	};
}
function parseAgentArgv(value, agentName, sourcePath) {
	if (!Array.isArray(value) || value.length === 0) throw new Error(`Invalid config agents.${agentName}.argv in ${sourcePath}: expected non-empty array of strings`);
	const argv = value.map((arg, index) => {
		if (typeof arg !== "string") throw new Error(`Invalid config agents.${agentName}.argv[${index}] in ${sourcePath}: expected string`);
		return arg;
	});
	if (argv[0]?.length === 0) throw new Error(`Invalid config agents.${agentName}.argv[0] in ${sourcePath}: expected non-empty executable`);
	return argv;
}
function parseAgentArgs(value, agentName, sourcePath) {
	if (value == null) return [];
	if (!Array.isArray(value)) throw new Error(`Invalid config agents.${agentName}.args in ${sourcePath}: expected array of strings`);
	return value.map((arg, index) => {
		if (typeof arg !== "string") throw new Error(`Invalid config agents.${agentName}.args[${index}] in ${sourcePath}: expected string`);
		return arg;
	});
}
function quoteCommandArg(value) {
	return JSON.stringify(value);
}
function parseAuth(value, sourcePath) {
	if (value == null) return;
	if (!isObject(value)) throw new Error(`Invalid config auth in ${sourcePath}: expected object`);
	const parsed = {};
	for (const [methodId, rawCredential] of Object.entries(value)) {
		if (typeof rawCredential !== "string" || rawCredential.trim().length === 0) throw new Error(`Invalid config auth.${methodId} in ${sourcePath}: expected non-empty string`);
		parsed[methodId] = rawCredential;
	}
	return parsed;
}
function parseDisableExec(value, sourcePath) {
	if (value == null) return;
	if (typeof value !== "boolean") throw new Error(`Invalid config disableExec in ${sourcePath}: expected boolean`);
	return value;
}
async function readConfigFile(filePath) {
	try {
		const payload = await fs$1.readFile(filePath, "utf8");
		let parsed;
		try {
			parsed = JSON.parse(payload);
		} catch (error) {
			const reason = error instanceof Error ? error.message : String(error);
			throw new Error(`Invalid JSON in ${filePath}: ${reason}`, { cause: error });
		}
		if (!isObject(parsed)) throw new Error(`Invalid config in ${filePath}: expected top-level JSON object`);
		return parsed;
	} catch (error) {
		if (error.code === "ENOENT") return;
		throw error;
	}
}
async function loadExplicitMcpConfig(configuredPath) {
	if (!configuredPath) return;
	const config = await readConfigFile(configuredPath);
	if (config === void 0) throw new Error(`MCP config file not found: ${configuredPath}`);
	return config;
}
async function loadResolvedConfig(cwd, options = {}) {
	const globalPath = defaultGlobalConfigPath();
	const projectPath = projectConfigPath(cwd);
	const mcpConfigPath = options.mcpConfigPath ? path.resolve(cwd, options.mcpConfigPath) : void 0;
	const [globalConfig, projectConfig, mcpConfig] = await Promise.all([
		readConfigFile(globalPath),
		readConfigFile(projectPath),
		loadExplicitMcpConfig(mcpConfigPath)
	]);
	function resolveScalar(key, parse, fallback) {
		return parse(projectConfig?.[key], projectPath) ?? parse(globalConfig?.[key], globalPath) ?? fallback;
	}
	const scalar = {
		defaultAgent: resolveScalar("defaultAgent", parseDefaultAgent, DEFAULT_AGENT_NAME),
		defaultPermissions: resolveScalar("defaultPermissions", parsePermissionMode, DEFAULT_PERMISSION_MODE),
		nonInteractivePermissions: resolveScalar("nonInteractivePermissions", parseNonInteractivePermissionPolicy, DEFAULT_NON_INTERACTIVE_PERMISSION_POLICY),
		authPolicy: resolveScalar("authPolicy", parseAuthPolicy, DEFAULT_AUTH_POLICY),
		ttlMs: resolveScalar("ttl", parseTtlMs, DEFAULT_TTL_MS),
		timeoutMs: resolveTimeoutMs(projectConfig, projectPath, globalConfig, globalPath),
		queueMaxDepth: resolveScalar("queueMaxDepth", parseQueueMaxDepth, DEFAULT_QUEUE_MAX_DEPTH),
		format: resolveScalar("format", parseOutputFormat, DEFAULT_OUTPUT_FORMAT)
	};
	const agents = {
		...parseAgents(globalConfig?.agents, globalPath),
		...parseAgents(projectConfig?.agents, projectPath)
	};
	const auth = {
		...parseAuth(globalConfig?.auth, globalPath),
		...parseAuth(projectConfig?.auth, projectPath)
	};
	const mcpServers = resolveMcpServers(projectConfig, projectPath, globalConfig, globalPath, mcpConfig, mcpConfigPath);
	const disableExec = resolveScalar("disableExec", parseDisableExec, DEFAULT_DISABLE_EXEC);
	return {
		...scalar,
		agents,
		auth,
		disableExec,
		mcpServers,
		globalPath,
		projectPath,
		mcpConfigPath,
		mcpConfigFingerprint: mcpConfigPath ? createHash("sha256").update(JSON.stringify(mcpServers)).digest("hex") : void 0,
		hasGlobalConfig: globalConfig !== void 0,
		hasProjectConfig: projectConfig !== void 0
	};
}
function hasConfigKey(config, key) {
	return config != null && Object.prototype.hasOwnProperty.call(config, key);
}
function resolveTimeoutMs(projectConfig, projectPath, globalConfig, globalPath) {
	if (hasConfigKey(projectConfig, "timeout")) return parseTimeoutMs(projectConfig?.timeout, projectPath);
	if (hasConfigKey(globalConfig, "timeout")) return parseTimeoutMs(globalConfig?.timeout, globalPath);
}
function resolveMcpServers(projectConfig, projectPath, globalConfig, globalPath, mcpConfig, mcpConfigPath) {
	if (mcpConfigPath) return parseMcpServers(mcpConfig?.mcpServers, mcpConfigPath);
	if (hasConfigKey(projectConfig, "mcpServers")) return parseMcpServers(projectConfig?.mcpServers, projectPath);
	if (hasConfigKey(globalConfig, "mcpServers")) return parseMcpServers(globalConfig?.mcpServers, globalPath);
	return [];
}
function toConfigDisplay(config) {
	const agents = Object.fromEntries(Object.entries(config.agents).map(([name, agent]) => [name, agent.argv ? { argv: [...agent.argv] } : { command: agent.command }]));
	return {
		defaultAgent: config.defaultAgent,
		defaultPermissions: config.defaultPermissions,
		nonInteractivePermissions: config.nonInteractivePermissions,
		authPolicy: config.authPolicy,
		ttl: config.ttlMs / 1e3,
		timeout: config.timeoutMs == null ? null : config.timeoutMs / 1e3,
		queueMaxDepth: config.queueMaxDepth,
		format: config.format,
		agents,
		authMethods: Object.keys(config.auth).toSorted(),
		disableExec: config.disableExec
	};
}
async function initGlobalConfigFile() {
	const configPath = defaultGlobalConfigPath();
	await fs$1.mkdir(path.dirname(configPath), {
		recursive: true,
		mode: 448
	});
	try {
		await fs$1.access(configPath);
		return {
			path: configPath,
			created: false
		};
	} catch {}
	const payload = {
		defaultAgent: DEFAULT_AGENT_NAME,
		defaultPermissions: "approve-all",
		nonInteractivePermissions: "deny",
		authPolicy: "skip",
		ttl: 300,
		timeout: null,
		queueMaxDepth: DEFAULT_QUEUE_MAX_DEPTH,
		format: "text",
		agents: {},
		auth: {}
	};
	try {
		await fs$1.writeFile(configPath, `${JSON.stringify(payload, null, 2)}\n`, {
			encoding: "utf8",
			flag: "wx",
			mode: 384
		});
	} catch (error) {
		if (error.code === "EEXIST") return {
			path: configPath,
			created: false
		};
		throw error;
	}
	return {
		path: configPath,
		created: true
	};
}
// src/cli/config-command.ts
async function handleConfigShow(command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const payload = {
		...toConfigDisplay(config),
		paths: {
			global: config.globalPath,
			project: config.projectPath,
			...config.mcpConfigPath ? { mcp: config.mcpConfigPath } : {}
		},
		loaded: {
			global: config.hasGlobalConfig,
			project: config.hasProjectConfig
		}
	};
	if (globalFlags.format === "json") {
		process.stdout.write(`${JSON.stringify(payload)}\n`);
		return;
	}
	process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
}
async function handleConfigInit(command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const result = await initGlobalConfigFile();
	if (globalFlags.format === "json") {
		process.stdout.write(`${JSON.stringify({
			path: result.path,
			created: result.created
		})}\n`);
		return;
	}
	if (globalFlags.format === "quiet") {
		process.stdout.write(`${result.path}\n`);
		return;
	}
	if (result.created) {
		process.stdout.write(`Created ${result.path}\n`);
		return;
	}
	process.stdout.write(`Config already exists: ${result.path}\n`);
}
function registerConfigCommand(program, config) {
	const configCommand = program.command("config").description("Inspect and initialize acpx configuration");
	configCommand.command("show").description("Show resolved config").option("--format <fmt>", "Output format: text, json, quiet", parseOutputFormat$1).action(async function() {
		await handleConfigShow(this, config);
	});
	configCommand.command("init").description("Create global config template").option("--format <fmt>", "Output format: text, json, quiet", parseOutputFormat$1).action(async function() {
		await handleConfigInit(this, config);
	});
	configCommand.action(async function() {
		await handleConfigShow(this, config);
	});
}
// src/cli/output/render.ts
var render_exports = /* @__PURE__ */ __exportAll({
	agentSessionIdPayload: () => agentSessionIdPayload,
	classifySessionConnectionStatus: () => classifySessionConnectionStatus,
	formatPromptSessionBannerLine: () => formatPromptSessionBannerLine,
	printAgentSessionsByFormat: () => printAgentSessionsByFormat,
	printClosedSessionByFormat: () => printClosedSessionByFormat,
	printCreatedSessionBanner: () => printCreatedSessionBanner,
	printEnsuredSessionByFormat: () => printEnsuredSessionByFormat,
	printNewSessionByFormat: () => printNewSessionByFormat,
	printPromptSessionBanner: () => printPromptSessionBanner,
	printPruneResultByFormat: () => printPruneResultByFormat,
	printQueuedPromptByFormat: () => printQueuedPromptByFormat,
	printSessionsByFormat: () => printSessionsByFormat
});
function formatSessionLabel(record) {
	return record.name ?? "cwd";
}
function formatRoutedFrom(sessionCwd, currentCwd) {
	const relative = path.relative(sessionCwd, currentCwd);
	if (!relative || relative === ".") return;
	return relative.startsWith(".") ? relative : `.${path.sep}${relative}`;
}
function classifySessionConnectionStatus(health) {
	if (health.healthy) return "connected";
	return health.hasLease ? "needs reconnect" : "starting";
}
async function resolveSessionConnectionStatus(record) {
	return classifySessionConnectionStatus(await probeQueueOwnerHealth(record.acpxRecordId));
}
function printSessionsByFormat(sessions, format) {
	if (format === "json") {
		process.stdout.write(`${JSON.stringify(sessions)}\n`);
		return;
	}
	if (format === "quiet") {
		printQuietSessions(sessions);
		return;
	}
	if (sessions.length === 0) {
		process.stdout.write("No sessions\n");
		return;
	}
	for (const session of sessions) {
		const closedMarker = session.closed ? " [closed]" : "";
		process.stdout.write(`${session.acpxRecordId}${closedMarker}\t${session.name ?? "-"}\t${session.cwd}\t${session.lastUsedAt}\n`);
	}
}
function printQuietSessions(sessions) {
	for (const session of sessions) {
		const closedMarker = session.closed ? " [closed]" : "";
		process.stdout.write(`${session.acpxRecordId}${closedMarker}\n`);
	}
}
function printAgentSessionsByFormat(result, format) {
	if (format === "json") {
		process.stdout.write(`${JSON.stringify(result)}\n`);
		return;
	}
	if (format === "quiet") {
		printQuietAgentSessions(result);
		return;
	}
	printTextAgentSessions(result);
}
function printQuietAgentSessions(result) {
	for (const session of result.sessions) process.stdout.write(`${session.sessionId}\n`);
}
function printTextAgentSessions(result) {
	if (result.sessions.length === 0) process.stdout.write("No sessions\n");
	else for (const session of result.sessions) {
		const title = session.title ?? "-";
		const updatedAt = session.updatedAt ?? "-";
		const meta = session._meta ? JSON.stringify(session._meta) : "-";
		process.stdout.write(`${session.sessionId}\t${title}\t${session.cwd}\t${updatedAt}\t${meta}\n`);
	}
	if (result.nextCursor) process.stdout.write(`Next cursor: ${result.nextCursor}\n`);
}
function printClosedSessionByFormat(record, format) {
	if (emitJsonResult(format, {
		action: "session_closed",
		acpxRecordId: record.acpxRecordId,
		acpxSessionId: record.acpSessionId,
		agentSessionId: record.agentSessionId
	})) return;
	if (format === "quiet") return;
	process.stdout.write(`${record.acpxRecordId}\n`);
}
function printNewSessionByFormat(record, replaced, format) {
	if (emitJsonResult(format, {
		action: "session_ensured",
		created: true,
		acpxRecordId: record.acpxRecordId,
		acpxSessionId: record.acpSessionId,
		agentSessionId: record.agentSessionId,
		name: record.name,
		replacedSessionId: replaced?.acpxRecordId
	})) return;
	if (format === "quiet") {
		process.stdout.write(`${record.acpxRecordId}\n`);
		return;
	}
	if (replaced) {
		process.stdout.write(`${record.acpxRecordId}\t(replaced ${replaced.acpxRecordId})\n`);
		return;
	}
	process.stdout.write(`${record.acpxRecordId}\n`);
}
function printEnsuredSessionByFormat(record, created, format) {
	if (emitJsonResult(format, {
		action: "session_ensured",
		created,
		acpxRecordId: record.acpxRecordId,
		acpxSessionId: record.acpSessionId,
		agentSessionId: record.agentSessionId,
		name: record.name
	})) return;
	if (format === "quiet") {
		process.stdout.write(`${record.acpxRecordId}\n`);
		return;
	}
	const action = created ? "created" : "existing";
	process.stdout.write(`${record.acpxRecordId}\t(${action})\n`);
}
function printQueuedPromptByFormat(result, format) {
	if (emitJsonResult(format, {
		action: "prompt_queued",
		acpxRecordId: result.sessionId,
		requestId: result.requestId
	})) return;
	if (format === "quiet") return;
	process.stdout.write(`[queued] ${result.requestId}\n`);
}
function formatPromptSessionBannerLine(record, currentCwd, connectionStatus = "starting") {
	const label = formatSessionLabel(record);
	const normalizedSessionCwd = path.resolve(record.cwd);
	const normalizedCurrentCwd = path.resolve(currentCwd);
	const routedFrom = normalizedSessionCwd === normalizedCurrentCwd ? void 0 : formatRoutedFrom(normalizedSessionCwd, normalizedCurrentCwd);
	const status = connectionStatus;
	if (routedFrom) return `[acpx] session ${label} (${record.acpxRecordId}) · ${normalizedSessionCwd} (routed from ${routedFrom}) · agent ${status}`;
	return `[acpx] session ${label} (${record.acpxRecordId}) · ${normalizedSessionCwd} · agent ${status}`;
}
async function printPromptSessionBanner(record, currentCwd, format, jsonStrict = false) {
	if (format === "quiet" || jsonStrict && format === "json") return;
	const status = await resolveSessionConnectionStatus(record);
	process.stderr.write(`${formatPromptSessionBannerLine(record, currentCwd, status)}\n`);
}
function printCreatedSessionBanner(record, agentName, format, jsonStrict = false) {
	if (format === "quiet" || jsonStrict && format === "json") return;
	const label = formatSessionLabel(record);
	process.stderr.write(`[acpx] created session ${label} (${record.acpxRecordId})\n`);
	process.stderr.write(`[acpx] agent: ${agentName}\n`);
	process.stderr.write(`[acpx] cwd: ${record.cwd}\n`);
}
function formatBytes(bytes) {
	if (bytes >= 1073741824) return `${(bytes / 1073741824).toFixed(1)} GB`;
	if (bytes >= 1048576) return `${(bytes / 1048576).toFixed(1)} MB`;
	if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)} KB`;
	return `${bytes} B`;
}
function printPruneResultByFormat(result, format) {
	const count = result.pruned.length;
	if (emitPruneJsonResult(result, format, count)) return;
	if (format === "quiet") {
		printQuietPruneResult(result.pruned);
		return;
	}
	if (count === 0) {
		process.stdout.write(result.dryRun ? "[DRY RUN] No sessions to prune\n" : "No sessions pruned\n");
		return;
	}
	process.stdout.write(`${formatPruneSummaryLine(result, count)}\n`);
	for (const record of result.pruned) {
		const label = record.name ? ` (${record.name})` : "";
		process.stdout.write(`  ${record.acpxRecordId}${label}\t${record.closedAt ?? record.lastUsedAt}\n`);
	}
}
function emitPruneJsonResult(result, format, count) {
	return emitJsonResult(format, {
		action: result.dryRun ? "sessions_prune_dry_run" : "sessions_pruned",
		dryRun: result.dryRun,
		count,
		bytesFreed: result.bytesFreed,
		pruned: result.pruned.map((r) => r.acpxRecordId)
	});
}
function printQuietPruneResult(pruned) {
	for (const record of pruned) process.stdout.write(`${record.acpxRecordId}\n`);
}
function formatPruneSummaryLine(result, count) {
	const prefix = result.dryRun ? "[DRY RUN] Would prune" : "Pruned";
	const bytesSuffix = !result.dryRun && result.bytesFreed > 0 ? `, freed ${formatBytes(result.bytesFreed)}` : "";
	return `${prefix} ${count} session${count === 1 ? "" : "s"}${bytesSuffix}`;
}
function agentSessionIdPayload(agentSessionId) {
	const normalized = normalizeAgentSessionId(agentSessionId);
	if (!normalized) return {};
	return { agentSessionId: normalized };
}
// src/cli/status-command.ts
function formatUptime(startedAt) {
	if (!startedAt) return;
	const startedMs = Date.parse(startedAt);
	if (!Number.isFinite(startedMs)) return;
	const elapsedMs = Math.max(0, Date.now() - startedMs);
	const seconds = Math.floor(elapsedMs / 1e3);
	const hours = Math.floor(seconds / 3600);
	const minutes = Math.floor(seconds % 3600 / 60);
	const remSeconds = seconds % 60;
	return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${remSeconds.toString().padStart(2, "0")}`;
}
function resolveStatusState(record, health) {
	if (health.healthy) return "running";
	if (health.hasLease) return "dead";
	if (record.lastAgentExitSignal || (record.lastAgentExitCode ?? 0) !== 0) return "dead";
	return "idle";
}
function statusSummary(state) {
	switch (state) {
		case "running": return "queue owner healthy";
		case "idle": return "session idle; queue owner will start on next prompt";
		case "dead": return "queue owner unavailable";
	}
	return "queue owner unavailable";
}
async function handleStatus(explicitAgentName, flags, command, config) {
	const globalFlags = resolveGlobalFlags(command, config);
	const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
	const record = await findSession({
		agentCommand: agent.agentCommand,
		cwd: agent.cwd,
		name: resolveSessionNameFromFlags(flags, command)
	});
	if (!record) {
		printMissingStatus(globalFlags.format, agent.agentCommand);
		return;
	}
	await printSessionStatus(record, globalFlags.format);
}
function printMissingStatus(format, agentCommand) {
	if (emitJsonResult(format, {
		action: "status_snapshot",
		status: "no-session",
		summary: "no active session"
	})) return;
	if (format === "quiet") {
		process.stdout.write("no-session\n");
		return;
	}
	process.stdout.write("session: -\n");
	process.stdout.write(`agent: ${agentCommand}\n`);
	process.stdout.write("pid: -\n");
	process.stdout.write("status: no-session\n");
	process.stdout.write("model: -\n");
	process.stdout.write("mode: -\n");
	process.stdout.write("uptime: -\n");
	process.stdout.write("lastPromptTime: -\n");
}
async function printSessionStatus(record, format) {
	const health = await probeQueueOwnerHealth(record.acpxRecordId);
	const statusState = resolveStatusState(record, health);
	const payload = createStatusPayload(record, health, statusState);
	const running = statusState === "running";
	const dead = statusState === "dead";
	if (emitJsonResult(format, statusJsonPayload(record, payload, statusState, running, dead))) return;
	if (format === "quiet") {
		process.stdout.write(`${payload.status}\n`);
		return;
	}
	printTextStatus(payload, dead);
}
function createStatusPayload(record, health, statusState) {
	const running = statusState === "running";
	const acpx = statusAcpxFields(record);
	return {
		sessionId: record.acpxRecordId,
		agentCommand: record.agentCommand,
		pid: statusPid(health),
		status: statusState,
		model: acpx.model,
		mode: acpx.mode,
		availableModels: acpx.availableModels,
		uptime: running ? formatUptime(record.agentStartedAt) ?? null : null,
		lastPromptTime: record.lastPromptAt ?? null,
		exitCode: running ? null : record.lastAgentExitCode ?? null,
		signal: running ? null : record.lastAgentExitSignal ?? null,
		...agentSessionIdPayload(record.agentSessionId)
	};
}
function statusAcpxFields(record) {
	return {
		model: record.acpx?.current_model_id ?? null,
		mode: record.acpx?.current_mode_id ?? null,
		availableModels: record.acpx?.available_models ?? null
	};
}
function statusPid(health) {
	if (health.pidAlive) return health.pid ?? null;
	return null;
}
function statusJsonPayload(record, payload, statusState, running, dead) {
	const result = {
		action: "status_snapshot",
		status: running ? "alive" : statusState,
		summary: statusSummary(statusState),
		acpxRecordId: record.acpxRecordId,
		acpxSessionId: record.acpSessionId,
		agentSessionId: record.agentSessionId
	};
	assignDefinedJsonField(result, "pid", payload.pid);
	assignDefinedJsonField(result, "model", payload.model);
	assignDefinedJsonField(result, "mode", payload.mode);
	assignDefinedJsonField(result, "availableModels", payload.availableModels);
	assignDefinedJsonField(result, "uptime", payload.uptime);
	assignDefinedJsonField(result, "lastPromptTime", payload.lastPromptTime);
	if (dead) {
		assignDefinedJsonField(result, "exitCode", payload.exitCode);
		assignDefinedJsonField(result, "signal", payload.signal);
	}
	return result;
}
function assignDefinedJsonField(target, key, value) {
	if (value !== null && value !== void 0) target[key] = value;
}
function printTextStatus(payload, dead) {
	process.stdout.write(`session: ${payload.sessionId}\n`);
	if ("agentSessionId" in payload) process.stdout.write(`agentSessionId: ${payload.agentSessionId}\n`);
	process.stdout.write(`agent: ${payload.agentCommand}\n`);
	process.stdout.write(`pid: ${payload.pid ?? "-"}\n`);
	process.stdout.write(`status: ${payload.status}\n`);
	process.stdout.write(`model: ${payload.model ?? "-"}\n`);
	process.stdout.write(`mode: ${payload.mode ?? "-"}\n`);
	process.stdout.write(`uptime: ${payload.uptime ?? "-"}\n`);
	process.stdout.write(`lastPromptTime: ${payload.lastPromptTime ?? "-"}\n`);
	if (dead) printDeadStatusDetails(payload);
}
function printDeadStatusDetails(payload) {
	process.stdout.write(`exitCode: ${payload.exitCode ?? "-"}\n`);
	process.stdout.write(`signal: ${payload.signal ?? "-"}\n`);
}
function registerStatusCommand(parent, explicitAgentName, config, description) {
	const statusCommand = parent.command("status").description(description);
	addSessionNameOption(statusCommand);
	statusCommand.action(async function(flags) {
		await handleStatus(explicitAgentName, flags, this, config);
	});
}
// src/cli/command-registration.ts
var LocalAttributeOption = class extends Option {
	localAttributeName;
	constructor(flags, description, localAttributeName) {
		super(flags, description);
		this.localAttributeName = localAttributeName;
	}
	attributeName() {
		return this.localAttributeName;
	}
};
function addSessionsListOptions(command) {
	return command.option("--local", "List local acpx session records instead of agent protocol sessions").option("--cursor <cursor>", "Opaque ACP session/list cursor", (value) => parseNonEmptyValue("Cursor", value)).option("--filter-cwd <dir>", "Filter agent sessions by working directory", (value) => parseNonEmptyValue("Filter cwd", value));
}
function registerSessionsCommand(parent, explicitAgentName, config) {
	const sessionsCommand = parent.command("sessions").description("List, ensure, create, or close sessions for this agent");
	addSessionsListOptions(sessionsCommand);
	sessionsCommand.action(async function(flags) {
		await handleSessionsList(explicitAgentName, flags, this, config);
	});
	addSessionsListOptions(sessionsCommand.command("list")).description("List sessions").action(async function(flags) {
		await handleSessionsList(explicitAgentName, flags, this, config);
	});
	sessionsCommand.command("new").description("Create a fresh session for current cwd").option("-s, --name <name>", "Session name", parseSessionName).option("--resume-session <id>", "Resume existing ACP session id", (value) => parseNonEmptyValue("Resume session id", value)).action(async function(flags) {
		await handleSessionsNew(explicitAgentName, flags, this, config);
	});
	sessionsCommand.command("ensure").description("Ensure a session exists for current cwd or ancestor").option("-s, --name <name>", "Session name", parseSessionName).option("--resume-session <id>", "Resume existing ACP session id", (value) => parseNonEmptyValue("Resume session id", value)).action(async function(flags) {
		await handleSessionsEnsure(explicitAgentName, flags, this, config);
	});
	sessionsCommand.command("close").description("Close session for current cwd").argument("[name]", "Session name", parseSessionName).action(async function(name) {
		await handleSessionsClose(explicitAgentName, name, this, config);
	});
	sessionsCommand.command("show").description("Show session metadata for current cwd").argument("[name]", "Session name", parseSessionName).action(async function(name) {
		await handleSessionsShow(explicitAgentName, name, this, config);
	});
	sessionsCommand.command("history").description("Show recent session history entries").argument("[name]", "Session name", parseSessionName).option("--limit <count>", `Maximum number of entries to show (default: 20)`, parseHistoryLimit, 20).action(async function(name, flags) {
		await handleSessionsHistory(explicitAgentName, name, flags, this, config);
	});
	sessionsCommand.command("read").description("Read full session history").argument("[name]", "Session name", parseSessionName).option("--tail <count>", "Show only the last N entries instead of all history", parseHistoryLimit).action(async function(name, flags) {
		await handleSessionsHistory(explicitAgentName, name, { limit: flags.tail ?? 0 }, this, config);
	});
	sessionsCommand.command("watch").description("Replay and follow session events without affecting the active turn").option("-s, --name <name>", "Session name", parseSessionName).option("--cursor <cursor>", "Resume after a session watch cursor", (value) => parseNonEmptyValue("Cursor", value)).action(async function(flags) {
		await handleSessionsWatch(explicitAgentName, flags, this, config);
	});
	sessionsCommand.command("export").description("Export a portable session archive").argument("[name]", "Session name", parseSessionName).requiredOption("--output <path>", "Output archive path", (value) => parseNonEmptyValue("Output path", value)).addOption(new LocalAttributeOption("--cwd <cwd>", "Session cwd to export", "sourceCwd").argParser((value) => parseNonEmptyValue("Session cwd", value))).action(async function(name, flags) {
		await handleSessionsExport(explicitAgentName, name, flags, this, config);
	});
	sessionsCommand.command("import").description("Import a portable session archive").argument("<archive-path>", "Archive path", (value) => parseNonEmptyValue("Archive path", value)).option("--name <name>", "Imported session name", parseSessionName).addOption(new LocalAttributeOption("--cwd <cwd>", "Imported session cwd", "destinationCwd").argParser((value) => parseNonEmptyValue("Imported session cwd", value))).action(async function(archivePath, flags) {
		await handleSessionsImport(explicitAgentName, archivePath, flags, this, config);
	});
	sessionsCommand.command("prune").description("Delete closed sessions and free disk space").option("--dry-run", "Preview what would be pruned without deleting anything").option("--before <date>", "Prune sessions closed before this date", parsePruneBeforeDate).option("--older-than <days>", "Prune sessions closed more than N days ago", parseDaysOlderThan).option("--include-history", "Also delete event stream files (.stream.ndjson)").action(async function(flags) {
		await handleSessionsPrune(explicitAgentName, flags, this, config);
	});
}
function registerSharedAgentSubcommands(parent, explicitAgentName, config, descriptions) {
	const promptCommand = parent.command("prompt").description(descriptions.prompt).argument("[prompt...]", "Prompt text").showHelpAfterError();
	addSessionOption(promptCommand);
	addPromptInputOption(promptCommand);
	promptCommand.action(async function(promptParts, flags) {
		await handlePrompt(explicitAgentName, promptParts, flags, this, config);
	});
	const execCommand = parent.command("exec").description(descriptions.exec).argument("[prompt...]", "Prompt text").showHelpAfterError();
	addPromptInputOption(execCommand);
	addExecConfigOption(execCommand);
	execCommand.action(async function(promptParts, flags) {
		await handleExec(explicitAgentName, promptParts, flags, this, config);
	});
	const cancelCommand = parent.command("cancel").description(descriptions.cancel);
	addSessionNameOption(cancelCommand);
	cancelCommand.action(async function(flags) {
		await handleCancel(explicitAgentName, flags, this, config);
	});
	const setModeCommand = parent.command("set-mode").description(descriptions.setMode).argument("<mode>", "Mode id", (value) => parseNonEmptyValue("Mode", value));
	addSessionNameOption(setModeCommand);
	setModeCommand.action(async function(modeId, flags) {
		await handleSetMode(explicitAgentName, modeId, flags, this, config);
	});
	const setConfigCommand = parent.command("set").description(descriptions.setConfig).argument("<key>", "Config option id", (value) => parseNonEmptyValue("Config option key", value)).argument("<value>", "Config option value", (value) => parseNonEmptyValue("Config option value", value));
	addSessionNameOption(setConfigCommand);
	setConfigCommand.action(async function(key, value, flags) {
		await handleSetConfigOption(explicitAgentName, key, value, flags, this, config);
	});
	registerStatusCommand(parent, explicitAgentName, config, descriptions.status);
}
function registerAgentCommand(program, agentName, config) {
	const agentCommand = program.command(agentName).description(`Use ${agentName} agent`).argument("[prompt...]", "Prompt text").enablePositionalOptions().passThroughOptions().showHelpAfterError();
	addSessionOption(agentCommand);
	addPromptInputOption(agentCommand);
	agentCommand.action(async function(promptParts, flags) {
		await handlePrompt(agentName, promptParts, flags, this, config);
	});
	registerSharedAgentSubcommands(agentCommand, agentName, config, {
		prompt: "Prompt using persistent session",
		exec: "One-shot prompt without saved session",
		cancel: "Cooperatively cancel current in-flight prompt",
		setMode: "Set session mode",
		setConfig: "Set session config option",
		status: "Show local status of current session agent process"
	});
	registerSessionsCommand(agentCommand, agentName, config);
}
function registerFlowCommand(program, config) {
	const runCommand = program.command("flow").description("Run multi-step ACP workflows from flow files").command("run").description("Run a flow file").argument("<file>", "Flow module path").option("--input-json <json>", "Flow input as JSON").option("--input-file <path>", "Read flow input JSON from file").option("--default-agent <name>", "Default agent profile for ACP nodes without profile", (value) => parseNonEmptyValue("Default agent", value)).action(async function(file, flags) {
		const { handleFlowRun } = await import("./cli-DeYZhjw6.js");
		await handleFlowRun(file, flags, this, config);
	});
	addExecConfigOption(runCommand, "Default ACP session config option for flow nodes (repeatable)");
}
function registerDefaultCommands(program, config) {
	registerSharedAgentSubcommands(program, void 0, config, {
		prompt: `Prompt using ${config.defaultAgent} by default`,
		exec: `One-shot prompt using ${config.defaultAgent} by default`,
		cancel: `Cancel active prompt for ${config.defaultAgent} by default`,
		setMode: `Set session mode for ${config.defaultAgent} by default`,
		setConfig: `Set session config option for ${config.defaultAgent} by default`,
		status: `Show local status for ${config.defaultAgent} by default`
	});
	registerSessionsCommand(program, void 0, config);
	registerConfigCommand(program, config);
	registerCompareCommand(program, config);
	registerFlowCommand(program, config);
}
// src/session/queue/owner-input.ts
function asRecord(value) {
	if (!value || typeof value !== "object" || Array.isArray(value)) return;
	return value;
}
function parseQueueOwnerPayload(raw) {
	let parsed;
	try {
		parsed = JSON.parse(raw);
	} catch {
		throw new Error("queue owner payload must be valid JSON");
	}
	const record = asRecord(parsed);
	if (!record) throw new Error("queue owner payload must be an object");
	if (typeof record.sessionId !== "string" || record.sessionId.trim().length === 0) throw new Error("queue owner payload missing sessionId");
	if (record.permissionMode !== "approve-all" && record.permissionMode !== "approve-reads" && record.permissionMode !== "deny-all") throw new Error("queue owner payload has invalid permissionMode");
	const options = {
		sessionId: record.sessionId,
		permissionMode: record.permissionMode
	};
	assignQueueOwnerTransportOptions(options, record);
	assignQueueOwnerScalarOptions(options, record);
	assignQueueOwnerSessionOptions(options, record.sessionOptions);
	return options;
}
function assignQueueOwnerTransportOptions(options, record) {
	const parsedMcpServers = parseOptionalMcpServers(record.mcpServers, "queue owner payload");
	if (parsedMcpServers) options.mcpServers = parsedMcpServers;
	if (typeof record.mcpConfigPath === "string" && record.mcpConfigPath.length > 0) options.mcpConfigPath = record.mcpConfigPath;
	if (typeof record.mcpConfigFingerprint === "string" && record.mcpConfigFingerprint.length > 0) options.mcpConfigFingerprint = record.mcpConfigFingerprint;
	if (record.authCredentials && typeof record.authCredentials === "object") {
		const entries = Object.entries(record.authCredentials).filter(([, value]) => typeof value === "string");
		options.authCredentials = Object.fromEntries(entries);
	}
}
function assignQueueOwnerScalarOptions(options, record) {
	if (record.nonInteractivePermissions === "deny" || record.nonInteractivePermissions === "fail") options.nonInteractivePermissions = record.nonInteractivePermissions;
	if (record.authPolicy === "skip" || record.authPolicy === "fail") options.authPolicy = record.authPolicy;
	assignBooleanOption(options, "fs", record.fs);
	assignBooleanOption(options, "terminal", record.terminal);
	assignBooleanOption(options, "suppressSdkConsoleErrors", record.suppressSdkConsoleErrors);
	assignBooleanOption(options, "verbose", record.verbose);
	assignFiniteNumberOption(options, "ttlMs", record.ttlMs);
	assignRoundedNumberOption(options, "maxQueueDepth", record.maxQueueDepth, 1);
	assignRoundedNumberOption(options, "promptRetries", record.promptRetries, 0);
}
function assignBooleanOption(options, key, value) {
	if (typeof value === "boolean") options[key] = value;
}
function assignFiniteNumberOption(options, key, value) {
	if (typeof value === "number" && Number.isFinite(value)) options[key] = value;
}
function assignRoundedNumberOption(options, key, value, min) {
	if (typeof value === "number" && Number.isFinite(value)) options[key] = Math.max(min, Math.round(value));
}
function assignQueueOwnerSessionOptions(options, rawSessionOptions) {
	const sessionOpts = asRecord(rawSessionOptions);
	if (!sessionOpts) return;
	options.sessionOptions = {};
	assignSessionModel(options.sessionOptions, sessionOpts.model);
	assignSessionAllowedTools(options.sessionOptions, sessionOpts.allowedTools);
	assignSessionMaxTurns(options.sessionOptions, sessionOpts.maxTurns);
	assignSessionSystemPrompt(options.sessionOptions, sessionOpts.systemPrompt);
	assignSessionEnv(options.sessionOptions, sessionOpts.env);
}
function assignSessionModel(options, value) {
	if (typeof value === "string" && value.trim().length > 0) options.model = value;
}
function assignSessionAllowedTools(options, value) {
	if (Array.isArray(value)) options.allowedTools = value.filter((tool) => typeof tool === "string");
}
function assignSessionMaxTurns(options, value) {
	if (typeof value === "number" && Number.isFinite(value)) options.maxTurns = Math.max(1, Math.round(value));
}
function assignSessionSystemPrompt(options, value) {
	if (typeof value === "string") {
		options.systemPrompt = value;
		return;
	}
	const systemPrompt = asRecord(value);
	if (typeof systemPrompt?.append === "string") options.systemPrompt = { append: systemPrompt.append };
}
function assignSessionEnv(options, value) {
	const env = asRecord(value);
	if (!env) return;
	const entries = Object.entries(env).filter((entry) => typeof entry[1] === "string");
	if (entries.length > 0) options.env = Object.fromEntries(entries);
}
async function runQueueOwnerFromStdin() {
	const payload = await text(process.stdin);
	if (!payload) throw new Error("queue owner startup payload is empty");
	await runSessionQueueOwner(parseQueueOwnerPayload(payload));
}
// src/cli-core.ts
const TOP_LEVEL_VERBS = /* @__PURE__ */ new Set([
	"prompt",
	"exec",
	"cancel",
	"compare",
	"flow",
	"set-mode",
	"set",
	"sessions",
	"status",
	"config",
	"help"
]);
const TOP_LEVEL_VERSION_VALUE_FLAG_VALUES = [
	"--agent",
	"--cwd",
	"--auth-policy",
	"--non-interactive-permissions",
	"--permission-policy",
	"--policy",
	"--format",
	"--model",
	"--allowed-tools",
	"--max-turns",
	"--system-prompt",
	"--append-system-prompt",
	"--prompt-retries",
	"--timeout",
	"--ttl",
	"--mcp-config"
];
const TOP_LEVEL_VERSION_VALUE_FLAGS = new Set(TOP_LEVEL_VERSION_VALUE_FLAG_VALUES);
const TOP_LEVEL_VERSION_BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
	"--approve-all",
	"--approve-reads",
	"--deny-all",
	"--suppress-reads",
	"--json-strict",
	"--no-fs",
	"--no-terminal",
	"--verbose"
]);
const AGENT_SCAN_VALUE_FLAG_VALUES = [...TOP_LEVEL_VERSION_VALUE_FLAG_VALUES, "--file"];
const AGENT_SCAN_VALUE_FLAGS = new Set(AGENT_SCAN_VALUE_FLAG_VALUES);
const AGENT_SCAN_BOOLEAN_FLAGS = new Set(TOP_LEVEL_VERSION_BOOLEAN_FLAGS);
let skillflagModulePromise;
function loadSkillflagModule() {
	skillflagModulePromise ??= import("skillflag");
	return skillflagModulePromise;
}
function shouldMaybeHandleSkillflag(argv) {
	return argv.some((token) => token === "--skill" || token.startsWith("--skill="));
}
function matchesLongFlagValue(token, flags) {
	for (const flag of flags) if (token.startsWith(`${flag}=`)) return true;
	return false;
}
function classifyAgentTokenFlag(token) {
	if (token === "--agent" || token.startsWith("--agent=")) return "agent-value";
	if (AGENT_SCAN_VALUE_FLAGS.has(token)) return "skip-next";
	if (AGENT_SCAN_BOOLEAN_FLAGS.has(token) || matchesLongFlagValue(token, AGENT_SCAN_VALUE_FLAGS) || token.startsWith("--json-strict=")) return "skip";
	return "unknown";
}
function scanAgentTokenStep(token, hasAgentOverride) {
	if (token === "--") return {
		result: { hasAgentOverride },
		indexDelta: 0
	};
	if (!token.startsWith("-") || token === "-") return {
		result: {
			token,
			hasAgentOverride
		},
		indexDelta: 0
	};
	const flagScan = classifyAgentTokenFlag(token);
	if (flagScan === "agent-value") return {
		indexDelta: token === "--agent" ? 1 : 0,
		hasAgentOverride: true
	};
	if (flagScan === "skip-next") return { indexDelta: 1 };
	if (flagScan === "skip") return { indexDelta: 0 };
	return {
		result: { hasAgentOverride },
		indexDelta: 0
	};
}
function detectAgentToken(argv) {
	let hasAgentOverride = false;
	for (let index = 0; index < argv.length; index += 1) {
		const token = argv[index];
		const step = scanAgentTokenStep(token, hasAgentOverride);
		if (step.result) return {
			...step.result,
			index
		};
		if (step.hasAgentOverride) hasAgentOverride = true;
		index += step.indexDelta;
	}
	return { hasAgentOverride };
}
function lastTopLevelFlagValue(argv, flag) {
	let value;
	for (let index = 0; index < argv.length; index += 1) {
		const token = argv[index];
		const scan = classifyTopLevelFlagScan(token);
		if (scan.stop) break;
		if (token === flag) value = argv[index + 1];
		else if (token.startsWith(`${flag}=`)) value = token.slice(flag.length + 1);
		if (scan.skipNext) index += 1;
	}
	return value;
}
function detectInitialCwd(argv) {
	const command = detectAgentToken(argv);
	if (command.token === "compare" && command.index !== void 0) {
		const { cwd } = scanCompareArgs(argv.slice(command.index + 1));
		if (cwd !== void 0) return path.resolve(cwd);
	}
	return path.resolve(lastTopLevelFlagValue(argv, "--cwd") ?? process.cwd());
}
function detectMcpConfigPath(argv, cwd) {
	const value = lastTopLevelFlagValue(argv, "--mcp-config");
	return value ? path.resolve(cwd, value) : void 0;
}
function detectRequestedOutputFormat(argv, fallback) {
	let detectedFormat;
	for (let index = 0; index < argv.length; index += 1) {
		const token = argv[index];
		const scan = classifyTopLevelFlagScan(token);
		if (scan.stop) break;
		if (isJsonStrictToken(token)) return "json";
		const format = readFormatFlagValue(token, argv[index + 1]);
		if (format) detectedFormat = format;
		if (scan.skipNext) index += 1;
	}
	return detectCompareOutputFormat(argv, detectedFormat, fallback);
}
function detectCompareOutputFormat(argv, rootFormat, fallback) {
	const command = detectAgentToken(argv);
	if (command.token !== "compare" || command.index === void 0) return rootFormat ?? fallback;
	const local = scanCompareArgs(argv.slice(command.index + 1));
	const localFormat = isOutputFormat(local.format) ? local.format : void 0;
	return local.json ? "json" : rootFormat ?? localFormat ?? fallback;
}
function classifyTopLevelFlagScan(token) {
	if (token === "--" || !token.startsWith("-") || token === "-") return {
		stop: true,
		skipNext: false
	};
	return {
		stop: false,
		skipNext: TOP_LEVEL_VERSION_VALUE_FLAGS.has(token)
	};
}
function readFormatFlagValue(token, nextToken) {
	const raw = token === "--format" ? nextToken : readInlineFlagValue(token, "--format");
	return isOutputFormat(raw) ? raw : void 0;
}
function readInlineFlagValue(token, flag) {
	if (!token.startsWith(`${flag}=`)) return;
	return token.slice(flag.length + 1).trim();
}
function isOutputFormat(value) {
	return typeof value === "string" && OUTPUT_FORMATS.includes(value);
}
function isJsonStrictToken(token) {
	return token === "--json-strict" || token.startsWith("--json-strict=");
}
function detectJsonStrict(argv) {
	for (let index = 0; index < argv.length; index += 1) {
		const token = argv[index];
		const scan = classifyTopLevelFlagScan(token);
		if (scan.stop) break;
		if (isJsonStrictToken(token)) return true;
		if (scan.skipNext) index += 1;
	}
	return false;
}
function shouldSkipTopLevelVersionToken(token) {
	return matchesLongFlagValue(token, TOP_LEVEL_VERSION_VALUE_FLAG_VALUES) || TOP_LEVEL_VERSION_BOOLEAN_FLAGS.has(token);
}
function topLevelVersionTokenDecision(token) {
	if (token === "--version" || token === "-V") return "version";
	if (!token.startsWith("-") || token === "-") return "stop";
	if (TOP_LEVEL_VERSION_VALUE_FLAGS.has(token)) return "skip-next";
	if (shouldSkipTopLevelVersionToken(token)) return "skip";
	return "stop";
}
function isTopLevelVersionRequest(argv) {
	for (let index = 0; index < argv.length; index += 1) {
		const token = argv[index];
		if (token === "--") return false;
		const decision = topLevelVersionTokenDecision(token);
		if (decision === "version") return true;
		if (decision === "stop") return false;
		if (decision === "skip-next") index += 1;
	}
	return false;
}
async function emitJsonErrorEvent(error) {
	const formatter = createOutputFormatter("json", {
		jsonContext: { sessionId: "unknown" },
		suppressReads: false
	});
	formatter.onError(error);
	formatter.flush();
}
function isOutputAlreadyEmitted(error) {
	if (!error || typeof error !== "object") return false;
	return error.outputAlreadyEmitted === true;
}
async function emitRequestedError(error, normalized, outputPolicy) {
	if (isOutputAlreadyEmitted(error)) return;
	if (outputPolicy.format === "json") {
		await emitJsonErrorEvent(normalized);
		return;
	}
	if (outputPolicy.format === "quiet") {
		const formatter = createOutputFormatter("quiet");
		formatter.onError(normalized);
		formatter.flush();
		return;
	}
	if (!outputPolicy.suppressNonJsonStderr) {
		process.stderr.write(`${normalized.message}\n`);
		if (outputPolicy.format === "text") for (const hint of getTextErrorRemediationHints(normalized)) process.stderr.write(`${hint}\n`);
	}
}
async function handleQueueOwnerCommand(argv) {
	installPerfMetricsCapture({
		argv: argv.slice(2),
		role: argv[2] === "__queue-owner" ? "queue_owner" : "cli"
	});
	if (argv[2] !== "__queue-owner") return false;
	try {
		await runQueueOwnerFromStdin();
		return true;
	} catch (error) {
		const message = error instanceof Error ? error.message : String(error);
		process.stderr.write(`[acpx] queue owner failed: ${message}\n`);
		process.exit(EXIT_CODES.ERROR);
		return true;
	}
}
async function maybeHandleSkillflag(argv) {
	if (!shouldMaybeHandleSkillflag(argv)) return;
	const { findSkillsRoot, maybeHandleSkillflag } = await loadSkillflagModule();
	await maybeHandleSkillflag(argv, {
		skillsRoot: findSkillsRoot(import.meta.url),
		includeBundledSkill: false
	});
}
function createProgram(requestedJsonStrict) {
	const program = new Command();
	program.name("acpx").description("Headless CLI client for the Agent Client Protocol").version(getAcpxVersion()).enablePositionalOptions().showHelpAfterError();
	if (requestedJsonStrict) program.configureOutput({
		writeOut: () => {},
		writeErr: () => {}
	});
	return program;
}
async function handleProgramParseError(error, requestedOutputPolicy) {
	if (error instanceof CommanderError) {
		if (error.code === "commander.helpDisplayed" || error.code === "commander.version" || error.code === "commander.help" && error.exitCode === 0) process.exit(EXIT_CODES.SUCCESS);
		const normalized = normalizeOutputError(error, {
			defaultCode: "USAGE",
			origin: "cli"
		});
		await emitRequestedError(error, normalized, requestedOutputPolicy);
		process.exit(exitCodeForOutputErrorCode(normalized.code));
	}
	if (error instanceof InterruptedError) process.exit(EXIT_CODES.INTERRUPTED);
	const normalized = normalizeOutputError(error, { origin: "cli" });
	await emitRequestedError(error, normalized, requestedOutputPolicy);
	process.exit(exitCodeForOutputErrorCode(normalized.code));
}
async function main(argv = process.argv) {
	const rawArgs = normalizeLifecycleScriptArgs(argv.slice(2));
	const normalizedArgv = [
		argv[0] ?? "node",
		argv[1] ?? "acpx",
		...rawArgs
	];
	if (await handleQueueOwnerCommand(argv)) return;
	if (isTopLevelVersionRequest(rawArgs)) {
		process.stdout.write(`${getAcpxVersion()}\n`);
		return;
	}
	await maybeHandleSkillflag(normalizedArgv);
	const requestedJsonStrict = detectJsonStrict(rawArgs);
	let requestedOutputPolicy = {
		...resolveOutputPolicy(detectRequestedOutputFormat(rawArgs, "text"), requestedJsonStrict),
		suppressReads: rawArgs.some((token) => token === "--suppress-reads")
	};
	try {
		const initialCwd = detectInitialCwd(rawArgs);
		const config = await loadResolvedConfig(initialCwd, { mcpConfigPath: detectMcpConfigPath(rawArgs, initialCwd) });
		requestedOutputPolicy = {
			...resolveOutputPolicy(detectRequestedOutputFormat(rawArgs, config.format), requestedJsonStrict),
			suppressReads: requestedOutputPolicy.suppressReads
		};
		const program = createProgram(requestedJsonStrict);
		addGlobalFlags(program);
		program.exitOverride((error) => {
			throw error;
		});
		configurePublicCli({
			program,
			argv: rawArgs,
			config,
			requestedJsonStrict,
			topLevelVerbs: TOP_LEVEL_VERBS,
			listBuiltInAgents,
			detectAgentToken,
			registerAgentCommand,
			registerDefaultCommands,
			handlePromptAction: async (command, promptParts) => {
				await handlePrompt(void 0, promptParts, {}, command, config);
			}
		});
		await program.parseAsync(normalizedArgv);
	} catch (error) {
		await handleProgramParseError(error, requestedOutputPolicy);
	} finally {
		flushPerfMetricsCapture();
	}
}
function normalizeLifecycleScriptArgs(rawArgs) {
	if (rawArgs[0] === "--" && (process.env.npm_lifecycle_event || process.env.npm_lifecycle_script)) return rawArgs.slice(1);
	return rawArgs;
}
// src/cli/broken-pipe.ts
function handleBrokenPipeError(error, mode, exit = (code) => process.exit(code)) {
	if (error.code !== "EPIPE") throw error;
	if (mode === "exit") exit(0);
}
function installBrokenPipeHandler(stream, mode) {
	stream.on("error", (error) => {
		handleBrokenPipeError(error, mode);
	});
}
// src/cli.ts
function isCliEntrypoint(argv) {
	const entry = argv[1];
	if (!entry) return false;
	try {
		const resolved = pathToFileURL(realpathSync(entry)).href;
		return import.meta.url === resolved;
	} catch {
		return false;
	}
}
if (isCliEntrypoint(process.argv)) {
	const isQueueOwner = process.argv[2] === "__queue-owner";
	installBrokenPipeHandler(process.stdout, "exit");
	installBrokenPipeHandler(process.stderr, isQueueOwner ? "ignore" : "exit");
	const queueOwnerArgOverride = buildQueueOwnerArgOverride(fileURLToPath(import.meta.url));
	if (queueOwnerArgOverride) process.env.ACPX_QUEUE_OWNER_ARGS ??= queueOwnerArgOverride;
	main(process.argv);
}
export { formatPromptSessionBannerLine, parseAllowedTools, parseMaxTurns, parseTtlSeconds };

//# sourceMappingURL=cli.js.map