acpx 0.9.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/README.md +23 -19
  2. package/dist/{cli-Bf3yjqzE.js → cli-CC2w0U-A.js} +4 -4
  3. package/dist/{cli-Bf3yjqzE.js.map → cli-CC2w0U-A.js.map} +1 -1
  4. package/dist/cli.d.ts +1 -1
  5. package/dist/cli.d.ts.map +1 -1
  6. package/dist/cli.js +685 -67
  7. package/dist/cli.js.map +1 -1
  8. package/dist/{client-BssohYqM.d.ts → client-j3sLnpcM.d.ts} +27 -4
  9. package/dist/client-j3sLnpcM.d.ts.map +1 -0
  10. package/dist/{flags-C-rwARqg.js → flags-BKjjl3tF.js} +4 -4
  11. package/dist/flags-BKjjl3tF.js.map +1 -0
  12. package/dist/{flows-WLs26_5Y.js → flows-BabqiU0u.js} +5 -4
  13. package/dist/flows-BabqiU0u.js.map +1 -0
  14. package/dist/flows.d.ts +1 -1
  15. package/dist/flows.d.ts.map +1 -1
  16. package/dist/flows.js +1 -1
  17. package/dist/{live-checkpoint-D5d-K9s1.js → live-checkpoint-BZrk9Mjz.js} +894 -384
  18. package/dist/live-checkpoint-BZrk9Mjz.js.map +1 -0
  19. package/dist/{output-DPg20dvn.js → output-D_BGt1YI.js} +180 -98
  20. package/dist/output-D_BGt1YI.js.map +1 -0
  21. package/dist/runtime.d.ts +71 -5
  22. package/dist/runtime.d.ts.map +1 -1
  23. package/dist/runtime.js +188 -32
  24. package/dist/runtime.js.map +1 -1
  25. package/dist/{session-options-CFudjdkU.d.ts → session-options-Co1oGEK8.d.ts} +22 -2
  26. package/dist/{session-options-CFudjdkU.d.ts.map → session-options-Co1oGEK8.d.ts.map} +1 -1
  27. package/package.json +23 -17
  28. package/skills/acpx/SKILL.md +66 -5
  29. package/dist/client-BssohYqM.d.ts.map +0 -1
  30. package/dist/flags-C-rwARqg.js.map +0 -1
  31. package/dist/flows-WLs26_5Y.js.map +0 -1
  32. package/dist/live-checkpoint-D5d-K9s1.js.map +0 -1
  33. package/dist/output-DPg20dvn.js.map +0 -1
package/dist/cli.js CHANGED
@@ -1,13 +1,16 @@
1
1
  #!/usr/bin/env node
2
- import { a as runSessionQueueOwner, c as buildQueueOwnerArgOverride, h as __exportAll, l as flushPerfMetricsCapture, n as getTextErrorRemediationHints, p as probeQueueOwnerHealth, t as createOutputFormatter, u as installPerfMetricsCapture } from "./output-DPg20dvn.js";
3
- import { At as EXIT_CODES, F as findSession, I as findSessionByDirectoryWalk, P as findGitRepositoryRoot, Pt as OUTPUT_FORMATS, Rt as AgentSpawnError, St as exitCodeForOutputErrorCode, Tt as normalizeOutputError, bt as normalizeAgentName, ct as PromptInputValidationError, dt as parsePromptSource, mt as InterruptedError, pt as textPrompt, st as normalizeRuntimeSessionId, ut as mergePromptSourceWithText, vt as DEFAULT_AGENT_NAME, yt as listBuiltInAgents } from "./live-checkpoint-D5d-K9s1.js";
4
- import { _ as resolveOutputPolicy, b as loadPermissionPolicySpec, c as parseHistoryLimit, d as parseOutputFormat$1, f as parsePruneBeforeDate, g as resolveGlobalFlags, h as resolveAgentInvocation, i as addSessionOption, l as parseMaxTurns, m as parseTtlSeconds, n as addPromptInputOption, o as parseAllowedTools, p as parseSessionName, r as addSessionNameOption, s as parseDaysOlderThan, t as addGlobalFlags, u as parseNonEmptyValue, v as resolvePermissionMode, y as resolveSessionNameFromFlags } from "./flags-C-rwARqg.js";
5
- import { readFileSync, realpathSync } from "node:fs";
2
+ import { a as runSessionQueueOwner, c as buildQueueOwnerArgOverride, g as __exportAll, h as isProcessAlive, l as flushPerfMetricsCapture, n as getTextErrorRemediationHints, o as runOnce, p as probeQueueOwnerHealth, t as createOutputFormatter, u as installPerfMetricsCapture } from "./output-D_BGt1YI.js";
3
+ import { B as listSessions, Bt as OUTPUT_FORMATS, Et as normalizeAgentName$1, G as writeSessionRecord, Gt as AgentSpawnError, H as normalizeName, I as findGitRepositoryRoot, It as EXIT_CODES, L as findSession, M as getAcpxVersion, Ot as exitCodeForOutputErrorCode, R as findSessionByDirectoryWalk, Tt as listBuiltInAgents, Wt as AcpxOperationalError, _t as parsePromptSource, at as defaultSessionEventLog, bt as InterruptedError, ct as sessionEventLockPath, dt as isAcpJsonRpcMessage, gt as mergePromptSourceWithText, jt as normalizeOutputError, lt as sessionEventSegmentPath, mt as PromptInputValidationError, nt as serializeSessionRecordForDisk, rt as normalizeRuntimeSessionId, st as sessionEventActivePath, tt as parseSessionRecord, wt as DEFAULT_AGENT_NAME, xt as TimeoutError, yt as textPrompt } from "./live-checkpoint-BZrk9Mjz.js";
4
+ import { _ as resolveGlobalFlags, b as resolveSessionNameFromFlags, c as parseHistoryLimit, d as parseOutputFormat$1, f as parsePruneBeforeDate, g as resolveAgentInvocation, h as parseTtlSeconds, i as addSessionOption, l as parseMaxTurns, m as parseTimeoutSeconds, n as addPromptInputOption, o as parseAllowedTools, p as parseSessionName, r as addSessionNameOption, s as parseDaysOlderThan, t as addGlobalFlags, u as parseNonEmptyValue, v as resolveOutputPolicy, x as loadPermissionPolicySpec, y as resolvePermissionMode } from "./flags-BKjjl3tF.js";
5
+ import { realpathSync } from "node:fs";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
7
7
  import path from "node:path";
8
- import { Command, CommanderError, InvalidArgumentError } from "commander";
8
+ import { Command, CommanderError, InvalidArgumentError, Option } from "commander";
9
9
  import fs$1 from "node:fs/promises";
10
10
  import os from "node:os";
11
+ import { randomUUID } from "node:crypto";
12
+ import { ZodError, z } from "zod";
13
+ import { performance } from "node:perf_hooks";
11
14
  //#region src/cli-public.ts
12
15
  function configurePublicCli(options) {
13
16
  const builtInAgents = options.listBuiltInAgents(options.config.agents);
@@ -53,6 +56,319 @@ function isLegacyZedCodexAcpInvocation(agentCommand) {
53
56
  return /@zed-industries\/codex-acp\b/u.test(agentCommand);
54
57
  }
55
58
  //#endregion
59
+ //#region src/session/export.ts
60
+ var SessionExportError = class extends AcpxOperationalError {
61
+ code;
62
+ exitCode = 2;
63
+ constructor(message, code) {
64
+ super(message, {
65
+ outputCode: "USAGE",
66
+ detailCode: code,
67
+ origin: "cli"
68
+ });
69
+ this.code = code;
70
+ }
71
+ };
72
+ function sessionLookupError(message, code) {
73
+ return new SessionExportError(message, code);
74
+ }
75
+ async function loadSessionRecord(sessionLookup) {
76
+ const cwd = path.resolve(sessionLookup.cwd ?? process.cwd());
77
+ const name = normalizeName(sessionLookup.name);
78
+ if (sessionLookup.agentCommand) {
79
+ const active = await findSession({
80
+ agentCommand: sessionLookup.agentCommand,
81
+ cwd,
82
+ name
83
+ });
84
+ if (active) return active;
85
+ return (await listSessions()).find((session) => {
86
+ if (session.agentCommand !== sessionLookup.agentCommand || session.cwd !== cwd) return false;
87
+ if (name == null) return session.name == null;
88
+ return session.name === name;
89
+ });
90
+ }
91
+ const matches = (await listSessions()).filter((session) => {
92
+ if (session.cwd !== cwd) return false;
93
+ if (name == null) return session.name == null;
94
+ return session.name === name;
95
+ });
96
+ if (matches.length > 1) throw sessionLookupError("multiple sessions match export lookup", "ambiguous-session");
97
+ return matches[0];
98
+ }
99
+ function parseEventLockPayload(raw) {
100
+ try {
101
+ const parsed = JSON.parse(raw);
102
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
103
+ const record = parsed;
104
+ return { pid: typeof record.pid === "number" ? record.pid : void 0 };
105
+ } catch {
106
+ return {};
107
+ }
108
+ }
109
+ async function hasLiveEventLock(recordId) {
110
+ try {
111
+ return isProcessAlive(parseEventLockPayload(await fs$1.readFile(sessionEventLockPath(recordId), "utf8")).pid);
112
+ } catch (error) {
113
+ if (error.code === "ENOENT") return false;
114
+ throw error;
115
+ }
116
+ }
117
+ async function isSessionActive(record) {
118
+ if (record.closed) return false;
119
+ return isProcessAlive(record.pid) || await hasLiveEventLock(record.acpxRecordId);
120
+ }
121
+ async function readHistoryFile(filePath) {
122
+ const payload = await fs$1.readFile(filePath, "utf8").catch((error) => {
123
+ if (error.code === "ENOENT") return "";
124
+ throw error;
125
+ });
126
+ const history = [];
127
+ for (const line of payload.split("\n").filter((entry) => entry.trim().length > 0)) try {
128
+ const parsed = JSON.parse(line);
129
+ if (isAcpJsonRpcMessage(parsed)) history.push(parsed);
130
+ } catch {}
131
+ return history;
132
+ }
133
+ async function readSessionHistory(record) {
134
+ const history = [];
135
+ const maxSegments = Number.isInteger(record.eventLog.max_segments) ? record.eventLog.max_segments : 0;
136
+ for (let segment = maxSegments; segment >= 1; segment -= 1) history.push(...await readHistoryFile(sessionEventSegmentPath(record.acpxRecordId, segment)));
137
+ history.push(...await readHistoryFile(sessionEventActivePath(record.acpxRecordId)));
138
+ return history;
139
+ }
140
+ function cwdRelativeToHome(cwd, home) {
141
+ const relative = path.relative(home, cwd);
142
+ if (relative.length === 0) return ".";
143
+ if (relative.length > 0 && !relative.startsWith("..") && !path.isAbsolute(relative)) return relative;
144
+ return cwd;
145
+ }
146
+ function serializeSessionRecordForArchive(record, cwdRelative) {
147
+ const state = serializeSessionRecordForDisk(record);
148
+ state.cwd = cwdRelative;
149
+ if (state.event_log && typeof state.event_log === "object" && !Array.isArray(state.event_log)) state.event_log = {
150
+ ...state.event_log,
151
+ active_path: ".stream.ndjson"
152
+ };
153
+ return state;
154
+ }
155
+ async function exportSession(sessionLookup, outputPath) {
156
+ const record = await loadSessionRecord(sessionLookup);
157
+ if (!record) throw sessionLookupError("session not found", "not-found");
158
+ if (await isSessionActive(record)) throw sessionLookupError("session is currently locked by a running queue owner; close it first with `acpx sessions close`", "session-locked");
159
+ const home = os.homedir();
160
+ const cwdRelative = cwdRelativeToHome(record.cwd, home);
161
+ const exported = {
162
+ format_version: 1,
163
+ exported_at: (/* @__PURE__ */ new Date()).toISOString(),
164
+ exported_by: "acpx",
165
+ session: {
166
+ record_id: record.acpxRecordId,
167
+ name: record.name ?? null,
168
+ agent: record.agentCommand,
169
+ agent_name: normalizeAgentName(sessionLookup.agentName),
170
+ cwd_relative: cwdRelative,
171
+ cwd_original: cwdRelative,
172
+ created_at: record.createdAt,
173
+ updated_at: record.lastUsedAt,
174
+ state: serializeSessionRecordForArchive(record, cwdRelative)
175
+ },
176
+ history: await readSessionHistory(record)
177
+ };
178
+ await fs$1.mkdir(path.dirname(path.resolve(outputPath)), { recursive: true });
179
+ await fs$1.writeFile(outputPath, `${JSON.stringify(exported, null, 2)}\n`, "utf8");
180
+ }
181
+ function normalizeAgentName(agentName) {
182
+ const normalized = agentName?.trim().toLowerCase();
183
+ if (!normalized || normalized.length === 0) return;
184
+ return normalized === "factory-droid" || normalized === "factorydroid" ? "droid" : normalized;
185
+ }
186
+ //#endregion
187
+ //#region src/session/import.ts
188
+ const SUPPORTED_FORMAT_VERSION = 1;
189
+ const exportedSessionSchema = z.object({
190
+ format_version: z.literal(SUPPORTED_FORMAT_VERSION),
191
+ exported_at: z.string(),
192
+ exported_by: z.string(),
193
+ session: z.object({
194
+ record_id: z.string(),
195
+ name: z.string().nullable(),
196
+ agent: z.string(),
197
+ agent_name: z.string().optional(),
198
+ cwd_relative: z.string(),
199
+ cwd_original: z.string().optional(),
200
+ cwd_absolute_original: z.string().optional(),
201
+ created_at: z.string(),
202
+ updated_at: z.string(),
203
+ state: z.unknown()
204
+ }),
205
+ history: z.array(z.unknown())
206
+ });
207
+ var SessionImportError = class extends AcpxOperationalError {
208
+ code;
209
+ exitCode = 2;
210
+ constructor(message, code) {
211
+ super(message, {
212
+ outputCode: "USAGE",
213
+ detailCode: code,
214
+ origin: "cli"
215
+ });
216
+ this.code = code;
217
+ }
218
+ };
219
+ function importError(message, code) {
220
+ return new SessionImportError(message, code);
221
+ }
222
+ function parseArchiveJson(raw) {
223
+ try {
224
+ return JSON.parse(raw);
225
+ } catch (error) {
226
+ throw importError(`Invalid session export archive JSON: ${error instanceof Error ? error.message : String(error)}`, "invalid-archive");
227
+ }
228
+ }
229
+ function assertSupportedFormatVersion(parsed) {
230
+ const record = parsed && typeof parsed === "object" ? parsed : {};
231
+ 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");
232
+ }
233
+ function parseArchive(raw) {
234
+ const parsed = parseArchiveJson(raw);
235
+ assertSupportedFormatVersion(parsed);
236
+ try {
237
+ return exportedSessionSchema.parse(parsed);
238
+ } catch (error) {
239
+ if (error instanceof ZodError) throw importError(`Invalid session export archive: ${error.issues[0]?.message}`, "invalid-archive");
240
+ throw error;
241
+ }
242
+ }
243
+ async function generateRecordId(sessionsDir) {
244
+ for (;;) {
245
+ const recordId = randomUUID();
246
+ const filePath = path.join(sessionsDir, `${encodeURIComponent(recordId)}.json`);
247
+ try {
248
+ await fs$1.access(filePath);
249
+ } catch (error) {
250
+ if (error.code === "ENOENT") return recordId;
251
+ throw error;
252
+ }
253
+ }
254
+ }
255
+ function resolveImportedCwd(cwdRelative, newCwd) {
256
+ if (newCwd) return path.resolve(newCwd);
257
+ if (path.isAbsolute(cwdRelative)) return cwdRelative;
258
+ return path.join(os.homedir(), cwdRelative);
259
+ }
260
+ function resolveImportedName(parsed, requestedName) {
261
+ return requestedName ?? parsed.session.name ?? void 0;
262
+ }
263
+ function assertExpectedAgentCommand(parsed, sourceRecord, options) {
264
+ const expectedAgentCommand = options.expectedAgentCommand;
265
+ if (!expectedAgentCommand) return;
266
+ const expectedAgentName = normalizeAgentIdentity(options.expectedAgentName);
267
+ const archiveAgentName = normalizeAgentIdentity(parsed.session.agent_name);
268
+ const archiveCommandMatches = agentCommandMatchesExpected(parsed.session.agent, expectedAgentCommand, expectedAgentName);
269
+ const stateCommandMatches = agentCommandMatchesExpected(sourceRecord.agentCommand, expectedAgentCommand, expectedAgentName);
270
+ if (archiveCommandMatches && stateCommandMatches && archiveAgentNameMatches({
271
+ archiveAgentName,
272
+ expectedAgentName,
273
+ archiveCommand: parsed.session.agent,
274
+ stateCommand: sourceRecord.agentCommand,
275
+ expectedAgentCommand
276
+ })) {
277
+ sourceRecord.agentCommand = expectedAgentCommand;
278
+ return;
279
+ }
280
+ throw importError("Session export archive agent does not match the requested agent", "agent-mismatch");
281
+ }
282
+ function archiveAgentNameMatches(params) {
283
+ if (params.archiveCommand === params.expectedAgentCommand && params.stateCommand === params.expectedAgentCommand) return true;
284
+ return params.archiveAgentName == null || params.expectedAgentName == null || params.archiveAgentName === params.expectedAgentName;
285
+ }
286
+ function normalizeAgentIdentity(agentName) {
287
+ const normalized = agentName?.trim().toLowerCase();
288
+ if (!normalized || normalized.length === 0) return;
289
+ return normalized === "factory-droid" || normalized === "factorydroid" ? "droid" : normalized;
290
+ }
291
+ function agentCommandMatchesExpected(archivedCommand, expectedAgentCommand, expectedAgentName) {
292
+ if (archivedCommand === expectedAgentCommand) return true;
293
+ return expectedAgentName ? commandLooksLikeBuiltInAgent(archivedCommand, expectedAgentName) : false;
294
+ }
295
+ function commandLooksLikeBuiltInAgent(command, agentName) {
296
+ const normalized = command.trim();
297
+ switch (agentName) {
298
+ case "pi": return /(?:^|\s)pi-acp(?:@|\s|$)/.test(normalized);
299
+ case "codex": return /(?:^|\s)@agentclientprotocol\/codex-acp(?:@|\s|$)/.test(normalized);
300
+ case "claude": return /(?:^|\s)@agentclientprotocol\/claude-agent-acp(?:@|\s|$)/.test(normalized);
301
+ default: return false;
302
+ }
303
+ }
304
+ async function assertDestinationScopeAvailable(record) {
305
+ if (!await findSession({
306
+ agentCommand: record.agentCommand,
307
+ cwd: record.cwd,
308
+ name: record.name
309
+ })) return;
310
+ throw importError("A session already exists for the import destination scope; pass --name or --cwd to import a separate copy", "session-scope-exists");
311
+ }
312
+ async function assertProviderSessionAvailable(record) {
313
+ if (!(await listSessions()).find((session) => session.acpSessionId === record.acpSessionId)) return;
314
+ throw importError("A local session already uses this provider session id; prune or remove the existing record before importing this archive", "session-provider-exists");
315
+ }
316
+ function buildImportedRecord(parsed, sourceRecord, options) {
317
+ const eventLog = {
318
+ ...defaultSessionEventLog(options.newRecordId),
319
+ max_segment_bytes: sourceRecord.eventLog.max_segment_bytes,
320
+ max_segments: sourceRecord.eventLog.max_segments,
321
+ segment_count: parsed.history.length > 0 ? 1 : sourceRecord.eventLog.segment_count
322
+ };
323
+ return {
324
+ ...sourceRecord,
325
+ acpxRecordId: options.newRecordId,
326
+ cwd: options.cwd,
327
+ name: resolveImportedName(parsed, options.name),
328
+ closed: false,
329
+ closedAt: void 0,
330
+ pid: void 0,
331
+ agentStartedAt: void 0,
332
+ lastAgentExitCode: void 0,
333
+ lastAgentExitSignal: void 0,
334
+ lastAgentExitAt: void 0,
335
+ lastAgentDisconnectReason: void 0,
336
+ eventLog,
337
+ importedFrom: {
338
+ recordId: parsed.session.record_id,
339
+ cwdOriginal: parsed.session.cwd_original ?? parsed.session.cwd_relative,
340
+ exportedBy: parsed.exported_by,
341
+ exportedAt: parsed.exported_at
342
+ }
343
+ };
344
+ }
345
+ async function importSession(archivePath, options = {}) {
346
+ const parsed = parseArchive(await fs$1.readFile(archivePath, "utf8"));
347
+ const sourceRecord = parseSessionRecord(parsed.session.state);
348
+ if (!sourceRecord) throw importError("Invalid session export archive: session.state is not a session record", "invalid-archive");
349
+ assertExpectedAgentCommand(parsed, sourceRecord, options);
350
+ const sessionsDir = path.join(os.homedir(), ".acpx", "sessions");
351
+ await fs$1.mkdir(sessionsDir, { recursive: true });
352
+ const cwd = resolveImportedCwd(parsed.session.cwd_relative, options.newCwd);
353
+ const newRecordId = await generateRecordId(sessionsDir);
354
+ const newRecord = buildImportedRecord(parsed, sourceRecord, {
355
+ newRecordId,
356
+ cwd,
357
+ name: options.name
358
+ });
359
+ await assertDestinationScopeAvailable(newRecord);
360
+ await assertProviderSessionAvailable(newRecord);
361
+ await writeSessionRecord(newRecord);
362
+ if (parsed.history.length > 0) {
363
+ const history = parsed.history;
364
+ await fs$1.writeFile(sessionEventActivePath(newRecordId), `${history.map((entry) => JSON.stringify(entry)).join("\n")}\n`, "utf8");
365
+ }
366
+ return {
367
+ record_id: newRecordId,
368
+ cwd
369
+ };
370
+ }
371
+ //#endregion
56
372
  //#region src/cli/output/json-output.ts
57
373
  function emitJsonResult(format, payload) {
58
374
  if (format !== "json") return false;
@@ -71,11 +387,11 @@ let sessionModulePromise;
71
387
  let outputModulePromise;
72
388
  let outputRenderModulePromise;
73
389
  function loadSessionModule() {
74
- sessionModulePromise ??= import("./output-DPg20dvn.js").then((n) => n.i);
390
+ sessionModulePromise ??= import("./output-D_BGt1YI.js").then((n) => n.i);
75
391
  return sessionModulePromise;
76
392
  }
77
393
  function loadOutputModule() {
78
- outputModulePromise ??= import("./output-DPg20dvn.js").then((n) => n.r);
394
+ outputModulePromise ??= import("./output-D_BGt1YI.js").then((n) => n.r);
79
395
  return outputModulePromise;
80
396
  }
81
397
  function loadOutputRenderModule() {
@@ -121,7 +437,7 @@ function resolveRequestedOutputPolicy(globalFlags) {
121
437
  suppressReads: globalFlags.suppressReads === true
122
438
  };
123
439
  }
124
- function sessionOptionsFromGlobalFlags(globalFlags) {
440
+ function sessionOptionsFromGlobalFlags$1(globalFlags) {
125
441
  return {
126
442
  model: globalFlags.model,
127
443
  allowedTools: globalFlags.allowedTools,
@@ -129,7 +445,7 @@ function sessionOptionsFromGlobalFlags(globalFlags) {
129
445
  systemPrompt: globalFlags.systemPrompt
130
446
  };
131
447
  }
132
- async function resolvePermissionPolicyFromFlags(globalFlags) {
448
+ async function resolvePermissionPolicyFromFlags$1(globalFlags) {
133
449
  try {
134
450
  return await loadPermissionPolicySpec(globalFlags.permissionPolicy, globalFlags.cwd);
135
451
  } catch (error) {
@@ -151,7 +467,8 @@ function buildSessionStartOptions(params) {
151
467
  terminal: params.globalFlags.terminal,
152
468
  timeoutMs: params.globalFlags.timeout,
153
469
  verbose: params.globalFlags.verbose,
154
- sessionOptions: sessionOptionsFromGlobalFlags(params.globalFlags)
470
+ sessionOptions: sessionOptionsFromGlobalFlags$1(params.globalFlags),
471
+ onModelWarning: params.globalFlags.jsonStrict ? void 0 : (message) => process.stderr.write(`[acpx] warning: ${message}\n`)
155
472
  };
156
473
  }
157
474
  function resolveSessionListFilterCwd(flags, agentCwd) {
@@ -190,7 +507,7 @@ async function handlePrompt(explicitAgentName, promptParts, flags, command, conf
190
507
  const globalFlags = resolveGlobalFlags(command, config);
191
508
  const outputPolicy = resolveRequestedOutputPolicy(globalFlags);
192
509
  const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
193
- const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
510
+ const permissionPolicy = await resolvePermissionPolicyFromFlags$1(globalFlags);
194
511
  const prompt = await readPrompt(promptParts, flags.file, globalFlags.cwd);
195
512
  const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
196
513
  const [{ createOutputFormatter }, { printPromptSessionBanner, printQueuedPromptByFormat }, { sendSession }] = await Promise.all([
@@ -198,7 +515,7 @@ async function handlePrompt(explicitAgentName, promptParts, flags, command, conf
198
515
  loadOutputRenderModule(),
199
516
  loadSessionModule()
200
517
  ]);
201
- const record = await findRoutedSessionOrThrow(agent.agentCommand, agent.agentName, agent.cwd, flags.session);
518
+ const record = await findRoutedSessionOrThrow(agent.agentCommand, agent.agentName, agent.cwd, resolveSessionNameFromFlags(flags, command));
202
519
  const outputFormatter = createOutputFormatter(outputPolicy.format, {
203
520
  jsonContext: { sessionId: record.acpxRecordId },
204
521
  suppressReads: outputPolicy.suppressReads
@@ -254,7 +571,7 @@ async function handleExec(explicitAgentName, promptParts, flags, command, config
254
571
  const globalFlags = resolveGlobalFlags(command, config);
255
572
  const outputPolicy = resolveRequestedOutputPolicy(globalFlags);
256
573
  const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
257
- const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
574
+ const permissionPolicy = await resolvePermissionPolicyFromFlags$1(globalFlags);
258
575
  const prompt = await readPrompt(promptParts, flags.file, globalFlags.cwd);
259
576
  const [{ createOutputFormatter }, { runOnce }] = await Promise.all([loadOutputModule(), loadSessionModule()]);
260
577
  const outputFormatter = createOutputFormatter(outputPolicy.format, { suppressReads: outputPolicy.suppressReads });
@@ -411,7 +728,7 @@ async function handleSetConfigOption(explicitAgentName, configId, value, flags,
411
728
  }
412
729
  async function tryListAgentSessions(agent, flags, globalFlags, config) {
413
730
  const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
414
- const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
731
+ const permissionPolicy = await resolvePermissionPolicyFromFlags$1(globalFlags);
415
732
  const { listAgentSessions } = await loadSessionModule();
416
733
  try {
417
734
  return await listAgentSessions({
@@ -466,7 +783,7 @@ async function handleSessionsClose(explicitAgentName, sessionName, command, conf
466
783
  async function handleSessionsNew(explicitAgentName, flags, command, config) {
467
784
  const globalFlags = resolveGlobalFlags(command, config);
468
785
  const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
469
- const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
786
+ const permissionPolicy = await resolvePermissionPolicyFromFlags$1(globalFlags);
470
787
  const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
471
788
  const [{ createSession, closeSession }, { printCreatedSessionBanner, printNewSessionByFormat }] = await Promise.all([loadSessionModule(), loadOutputRenderModule()]);
472
789
  const replaced = await findSession({
@@ -496,7 +813,7 @@ async function handleSessionsNew(explicitAgentName, flags, command, config) {
496
813
  async function handleSessionsEnsure(explicitAgentName, flags, command, config) {
497
814
  const globalFlags = resolveGlobalFlags(command, config);
498
815
  const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
499
- const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
816
+ const permissionPolicy = await resolvePermissionPolicyFromFlags$1(globalFlags);
500
817
  const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
501
818
  const [{ ensureSession }, { printCreatedSessionBanner, printEnsuredSessionByFormat }] = await Promise.all([loadSessionModule(), loadOutputRenderModule()]);
502
819
  const result = await ensureSession(buildSessionStartOptions({
@@ -618,6 +935,46 @@ async function handleSessionsHistory(explicitAgentName, sessionName, flags, comm
618
935
  const globalFlags = resolveGlobalFlags(command, config);
619
936
  printSessionHistoryByFormat(await findScopedSessionOrThrow(resolveAgentInvocation(explicitAgentName, globalFlags, config), sessionName), flags.limit, globalFlags.format);
620
937
  }
938
+ async function handleSessionsExport(explicitAgentName, sessionName, flags, command, config) {
939
+ const globalFlags = resolveGlobalFlags(command, config);
940
+ const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
941
+ const cwd = flags.sourceCwd ? path.resolve(agent.cwd, flags.sourceCwd) : agent.cwd;
942
+ await exportSession({
943
+ agentName: globalFlags.agent ? void 0 : agent.agentName,
944
+ agentCommand: agent.agentCommand,
945
+ cwd,
946
+ name: sessionName
947
+ }, flags.output);
948
+ if (emitJsonResult(globalFlags.format, {
949
+ action: "session_exported",
950
+ output: flags.output
951
+ })) return;
952
+ if (globalFlags.format === "quiet") {
953
+ process.stdout.write(`${flags.output}\n`);
954
+ return;
955
+ }
956
+ process.stdout.write(`exported session to ${flags.output}\n`);
957
+ }
958
+ async function handleSessionsImport(explicitAgentName, archivePath, flags, command, config) {
959
+ const globalFlags = resolveGlobalFlags(command, config);
960
+ const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
961
+ const result = await importSession(archivePath, {
962
+ name: flags.name,
963
+ newCwd: flags.destinationCwd ? path.resolve(globalFlags.cwd, flags.destinationCwd) : void 0,
964
+ expectedAgentName: globalFlags.agent ? void 0 : agent.agentName,
965
+ expectedAgentCommand: agent.agentCommand
966
+ });
967
+ if (emitJsonResult(globalFlags.format, {
968
+ action: "session_imported",
969
+ record_id: result.record_id,
970
+ cwd: result.cwd
971
+ })) return;
972
+ if (globalFlags.format === "quiet") {
973
+ process.stdout.write(`${result.record_id}\n`);
974
+ return;
975
+ }
976
+ process.stdout.write(`imported session ${result.record_id} at ${result.cwd}\n`);
977
+ }
621
978
  async function handleSessionsPrune(explicitAgentName, flags, command, config) {
622
979
  const globalFlags = resolveGlobalFlags(command, config);
623
980
  const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
@@ -632,6 +989,285 @@ async function handleSessionsPrune(explicitAgentName, flags, command, config) {
632
989
  }), globalFlags.format);
633
990
  }
634
991
  //#endregion
992
+ //#region src/cli/compare-command.ts
993
+ const DEFAULT_COMPARE_TIMEOUT_MS = 3e5;
994
+ const FINAL_MESSAGE_PREVIEW_CHARS = 200;
995
+ var CaptureFormatter = class {
996
+ setContext(_context) {}
997
+ onAcpMessage(_message) {}
998
+ onError(params) {}
999
+ onPermissionEscalation(_event) {}
1000
+ flush() {}
1001
+ };
1002
+ function asRecord$2(value) {
1003
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1004
+ }
1005
+ function numberField(source, keys) {
1006
+ if (!source) return null;
1007
+ for (const key of keys) {
1008
+ const value = source[key];
1009
+ if (typeof value === "number" && Number.isFinite(value)) return value;
1010
+ }
1011
+ return null;
1012
+ }
1013
+ function collapseWhitespace(value) {
1014
+ return value.replace(/\s+/g, " ").trim();
1015
+ }
1016
+ function truncate(value, maxChars) {
1017
+ if (value.length <= maxChars) return value;
1018
+ return `${value.slice(0, Math.max(0, maxChars - 3))}...`;
1019
+ }
1020
+ async function readStdin() {
1021
+ let data = "";
1022
+ for await (const chunk of process.stdin) data += String(chunk);
1023
+ return data;
1024
+ }
1025
+ async function readPromptFile(filePath, promptText, cwd) {
1026
+ const prompt = mergePromptSourceWithText(filePath === "-" ? await readStdin() : await fs$1.readFile(path.resolve(cwd, filePath), "utf8"), promptText);
1027
+ if (prompt.length === 0) throw new InvalidArgumentError("Prompt from --file is empty");
1028
+ return prompt;
1029
+ }
1030
+ async function readPromptFromStdin() {
1031
+ if (process.stdin.isTTY) throw new InvalidArgumentError("Prompt is required (pass as final argument, --file, or pipe via stdin)");
1032
+ const prompt = parsePromptSource(await readStdin());
1033
+ if (prompt.length === 0) throw new InvalidArgumentError("Prompt from stdin is empty");
1034
+ return prompt;
1035
+ }
1036
+ async function readPromptInput(filePath, promptText, cwd) {
1037
+ try {
1038
+ if (filePath) return await readPromptFile(filePath, promptText, cwd);
1039
+ const joined = promptText.trim();
1040
+ if (joined.length > 0) return textPrompt(joined);
1041
+ return await readPromptFromStdin();
1042
+ } catch (error) {
1043
+ if (error instanceof PromptInputValidationError) throw new InvalidArgumentError(error.message);
1044
+ throw error;
1045
+ }
1046
+ }
1047
+ function promptTokensAfterDoubleDash(command) {
1048
+ const commandName = command.name();
1049
+ const commandIndex = process.argv.findIndex((token, index) => index >= 2 && token === commandName);
1050
+ if (commandIndex < 0) return [];
1051
+ const delimiterIndex = process.argv.findIndex((token, index) => index > commandIndex && token === "--");
1052
+ return delimiterIndex < 0 ? [] : process.argv.slice(delimiterIndex + 1);
1053
+ }
1054
+ function splitCompareArgs(args, filePath, command) {
1055
+ if (filePath) {
1056
+ if (args.length === 0) throw new InvalidArgumentError("At least one agent is required");
1057
+ return {
1058
+ agents: args,
1059
+ promptText: ""
1060
+ };
1061
+ }
1062
+ const promptTokens = promptTokensAfterDoubleDash(command);
1063
+ if (promptTokens.length > 0) {
1064
+ const agents = args.slice(0, -promptTokens.length);
1065
+ if (agents.length === 0) throw new InvalidArgumentError("At least one agent is required");
1066
+ return {
1067
+ agents,
1068
+ promptText: promptTokens.join(" ")
1069
+ };
1070
+ }
1071
+ if (args.length < 2) throw new InvalidArgumentError("Usage: acpx compare <agent>... '<prompt>'");
1072
+ return {
1073
+ agents: args.slice(0, -1),
1074
+ promptText: args[args.length - 1] ?? ""
1075
+ };
1076
+ }
1077
+ function captureUsage(update, capture) {
1078
+ const source = asRecord$2(asRecord$2(update._meta)?.usage) ?? update;
1079
+ capture.usage = {
1080
+ input_tokens: numberField(source, ["input_tokens", "inputTokens"]) ?? void 0,
1081
+ output_tokens: numberField(source, ["output_tokens", "outputTokens"]) ?? void 0,
1082
+ total_tokens: numberField(source, [
1083
+ "total_tokens",
1084
+ "totalTokens",
1085
+ "size",
1086
+ "used"
1087
+ ]) ?? void 0
1088
+ };
1089
+ }
1090
+ function captureSessionUpdate(notification, capture) {
1091
+ const update = asRecord$2(notification.update);
1092
+ if (!update) return;
1093
+ if (update.sessionUpdate === "agent_message_chunk") {
1094
+ const content = asRecord$2(update.content);
1095
+ if (content?.type === "text" && typeof content.text === "string") capture.finalMessage += content.text;
1096
+ return;
1097
+ }
1098
+ if (update.sessionUpdate === "usage_update") captureUsage(update, capture);
1099
+ }
1100
+ function rowStatusFromPermissionStats(stats) {
1101
+ return stats.denied + stats.cancelled > 0 ? "permission_denied" : "ok";
1102
+ }
1103
+ function sessionOptionsFromGlobalFlags(globalFlags) {
1104
+ return {
1105
+ model: globalFlags.model,
1106
+ allowedTools: globalFlags.allowedTools,
1107
+ maxTurns: globalFlags.maxTurns,
1108
+ systemPrompt: globalFlags.systemPrompt
1109
+ };
1110
+ }
1111
+ async function resolvePermissionPolicyFromFlags(globalFlags) {
1112
+ try {
1113
+ return await loadPermissionPolicySpec(globalFlags.permissionPolicy, globalFlags.cwd);
1114
+ } catch (error) {
1115
+ throw new InvalidArgumentError(`Invalid permission policy: ${error instanceof Error ? error.message : String(error)}`);
1116
+ }
1117
+ }
1118
+ function buildSuccessRow(agentName, result, capture, startedAt) {
1119
+ const permissionStats = result.permissionStats;
1120
+ return {
1121
+ agent: agentName,
1122
+ status: result.stopReason === "cancelled" ? "cancelled" : rowStatusFromPermissionStats(permissionStats),
1123
+ stop_reason: result.stopReason,
1124
+ wall_ms: Math.round(performance.now() - startedAt),
1125
+ input_tokens: capture.usage.input_tokens ?? null,
1126
+ output_tokens: capture.usage.output_tokens ?? null,
1127
+ total_tokens: capture.usage.total_tokens ?? null,
1128
+ final_message: truncate(collapseWhitespace(capture.finalMessage), FINAL_MESSAGE_PREVIEW_CHARS),
1129
+ error: null,
1130
+ permission_requests: permissionStats.requested,
1131
+ permission_denied: permissionStats.denied + permissionStats.cancelled
1132
+ };
1133
+ }
1134
+ function buildErrorRow(agentName, caught, capture, startedAt) {
1135
+ return {
1136
+ agent: agentName,
1137
+ status: caught instanceof TimeoutError ? "cancelled" : "error",
1138
+ stop_reason: null,
1139
+ wall_ms: Math.round(performance.now() - startedAt),
1140
+ input_tokens: capture.usage.input_tokens ?? null,
1141
+ output_tokens: capture.usage.output_tokens ?? null,
1142
+ total_tokens: capture.usage.total_tokens ?? null,
1143
+ final_message: truncate(collapseWhitespace(capture.finalMessage), FINAL_MESSAGE_PREVIEW_CHARS),
1144
+ error: truncate(collapseWhitespace(caught instanceof Error ? caught.message : String(caught)), FINAL_MESSAGE_PREVIEW_CHARS),
1145
+ permission_requests: 0,
1146
+ permission_denied: 0
1147
+ };
1148
+ }
1149
+ async function runAgentForCompare(params) {
1150
+ const capture = {
1151
+ finalMessage: "",
1152
+ usage: {},
1153
+ errors: []
1154
+ };
1155
+ const formatter = new CaptureFormatter();
1156
+ const t0 = performance.now();
1157
+ try {
1158
+ const agent = resolveAgentInvocation(params.agentName, params.globalFlags, params.config);
1159
+ const result = await runOnce({
1160
+ agentCommand: agent.agentCommand,
1161
+ cwd: agent.cwd,
1162
+ prompt: params.prompt,
1163
+ mcpServers: params.config.mcpServers,
1164
+ permissionMode: resolvePermissionMode(params.globalFlags, params.config.defaultPermissions),
1165
+ nonInteractivePermissions: params.globalFlags.nonInteractivePermissions,
1166
+ permissionPolicy: params.permissionPolicy,
1167
+ authCredentials: params.config.auth,
1168
+ authPolicy: params.globalFlags.authPolicy,
1169
+ terminal: params.globalFlags.terminal,
1170
+ outputFormatter: formatter,
1171
+ suppressSdkConsoleErrors: true,
1172
+ timeoutMs: params.globalFlags.timeout ?? DEFAULT_COMPARE_TIMEOUT_MS,
1173
+ verbose: params.globalFlags.verbose,
1174
+ promptRetries: params.globalFlags.promptRetries,
1175
+ sessionOptions: sessionOptionsFromGlobalFlags(params.globalFlags),
1176
+ onSessionUpdate: (notification) => captureSessionUpdate(notification, capture)
1177
+ });
1178
+ return buildSuccessRow(params.agentName, result, capture, t0);
1179
+ } catch (caught) {
1180
+ return buildErrorRow(params.agentName, caught, capture, t0);
1181
+ }
1182
+ }
1183
+ function formatCell(value) {
1184
+ if (value == null || value === "") return "-";
1185
+ if (typeof value === "string") return collapseWhitespace(value);
1186
+ if (typeof value === "number" || typeof value === "boolean") return String(value);
1187
+ return collapseWhitespace(JSON.stringify(value));
1188
+ }
1189
+ function renderTable(rows) {
1190
+ const headers = [
1191
+ "agent",
1192
+ "status",
1193
+ "wall_ms",
1194
+ "input",
1195
+ "output",
1196
+ "total",
1197
+ "permissions",
1198
+ "stop_reason",
1199
+ "final_message",
1200
+ "error"
1201
+ ];
1202
+ const body = rows.map((row) => [
1203
+ row.agent,
1204
+ row.status,
1205
+ row.wall_ms,
1206
+ row.input_tokens,
1207
+ row.output_tokens,
1208
+ row.total_tokens,
1209
+ `${row.permission_denied}/${row.permission_requests}`,
1210
+ row.stop_reason,
1211
+ row.final_message,
1212
+ row.error
1213
+ ]);
1214
+ const widths = headers.map((header, index) => Math.max(header.length, ...body.map((cells) => formatCell(cells[index]).length)));
1215
+ const formatRow = (cells) => cells.map((cell, index) => truncate(formatCell(cell), widths[index] ?? 24).padEnd(widths[index] ?? 24)).join(" ").trimEnd();
1216
+ return [
1217
+ formatRow(headers),
1218
+ widths.map((width) => "-".repeat(width)).join(" "),
1219
+ ...body.map(formatRow)
1220
+ ].join("\n");
1221
+ }
1222
+ function printRows(rows, format) {
1223
+ if (format === "json") {
1224
+ process.stdout.write(`${JSON.stringify(rows)}\n`);
1225
+ return;
1226
+ }
1227
+ if (format === "quiet") {
1228
+ for (const row of rows) process.stdout.write(`${row.agent}\t${row.status}\n`);
1229
+ return;
1230
+ }
1231
+ process.stdout.write(`${renderTable(rows)}\n`);
1232
+ }
1233
+ function updateCompareExitCode(rows) {
1234
+ if (rows.some((row) => row.status === "error")) {
1235
+ process.exitCode = EXIT_CODES.ERROR;
1236
+ return;
1237
+ }
1238
+ if (rows.some((row) => row.status === "permission_denied")) {
1239
+ process.exitCode = EXIT_CODES.PERMISSION_DENIED;
1240
+ return;
1241
+ }
1242
+ if (rows.some((row) => row.status === "cancelled")) process.exitCode = EXIT_CODES.TIMEOUT;
1243
+ }
1244
+ function resolvePromptFile(flags) {
1245
+ if (flags.file && flags.promptFile && flags.file !== flags.promptFile) throw new InvalidArgumentError("Use only one prompt file flag: --file or --prompt-file");
1246
+ return flags.file ?? flags.promptFile;
1247
+ }
1248
+ function registerCompareCommand(program, config) {
1249
+ 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").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)).action(async function(args, flags) {
1250
+ if (config.disableExec) throw new Error("compare subcommand is disabled by configuration (disableExec: true)");
1251
+ const globalFlags = resolveGlobalFlags(this, config);
1252
+ if (globalFlags.agent) throw new InvalidArgumentError("Do not combine compare with --agent; pass agent names");
1253
+ const outputPolicy = resolveOutputPolicy(flags.json === true ? "json" : globalFlags.format, globalFlags.jsonStrict === true);
1254
+ const promptFile = resolvePromptFile(flags);
1255
+ const { agents, promptText } = splitCompareArgs(args, promptFile, this);
1256
+ const prompt = await readPromptInput(promptFile, promptText, globalFlags.cwd);
1257
+ const permissionPolicy = await resolvePermissionPolicyFromFlags(globalFlags);
1258
+ const rows = [];
1259
+ for (const agentName of agents) rows.push(await runAgentForCompare({
1260
+ agentName,
1261
+ prompt,
1262
+ config,
1263
+ globalFlags,
1264
+ permissionPolicy
1265
+ }));
1266
+ printRows(rows, outputPolicy.format);
1267
+ updateCompareExitCode(rows);
1268
+ });
1269
+ }
1270
+ //#endregion
635
1271
  //#region src/mcp-servers.ts
636
1272
  function asRecord$1(value) {
637
1273
  if (!value || typeof value !== "object" || Array.isArray(value)) return;
@@ -735,7 +1371,6 @@ function parseOptionalMcpServers(value, sourcePath, fieldName = "mcpServers") {
735
1371
  }
736
1372
  //#endregion
737
1373
  //#region src/cli/config.ts
738
- const DEFAULT_TIMEOUT_MS = void 0;
739
1374
  const DEFAULT_TTL_MS = 3e5;
740
1375
  const DEFAULT_PERMISSION_MODE = "approve-reads";
741
1376
  const DEFAULT_NON_INTERACTIVE_PERMISSION_POLICY = "deny";
@@ -802,7 +1437,7 @@ function parseOutputFormat(value, sourcePath) {
802
1437
  function parseDefaultAgent(value, sourcePath) {
803
1438
  if (value == null) return;
804
1439
  if (typeof value !== "string" || value.trim().length === 0) throw new Error(`Invalid config defaultAgent in ${sourcePath}: expected non-empty string`);
805
- return normalizeAgentName(value);
1440
+ return normalizeAgentName$1(value);
806
1441
  }
807
1442
  function parseAgents(value, sourcePath) {
808
1443
  if (value == null) return;
@@ -813,7 +1448,7 @@ function parseAgents(value, sourcePath) {
813
1448
  const command = raw.command;
814
1449
  if (typeof command !== "string" || command.trim().length === 0) throw new Error(`Invalid config agents.${name}.command in ${sourcePath}: expected non-empty string`);
815
1450
  const args = parseAgentArgs(raw.args, name, sourcePath);
816
- parsed[normalizeAgentName(name)] = args.length > 0 ? `${command.trim()} ${args.map(quoteCommandArg).join(" ")}` : command.trim();
1451
+ parsed[normalizeAgentName$1(name)] = args.length > 0 ? `${command.trim()} ${args.map(quoteCommandArg).join(" ")}` : command.trim();
817
1452
  }
818
1453
  return parsed;
819
1454
  }
@@ -937,7 +1572,6 @@ function hasConfigKey(config, key) {
937
1572
  function resolveTimeoutMs(projectConfig, projectPath, globalConfig, globalPath) {
938
1573
  if (hasConfigKey(projectConfig, "timeout")) return parseTimeoutMs(projectConfig?.timeout, projectPath);
939
1574
  if (hasConfigKey(globalConfig, "timeout")) return parseTimeoutMs(globalConfig?.timeout, globalPath);
940
- return DEFAULT_TIMEOUT_MS;
941
1575
  }
942
1576
  function resolveMcpServers(projectConfig, projectPath, globalConfig, globalPath) {
943
1577
  if (hasConfigKey(projectConfig, "mcpServers")) return parseMcpServers(projectConfig?.mcpServers, projectPath);
@@ -1409,6 +2043,16 @@ function registerStatusCommand(parent, explicitAgentName, config, description) {
1409
2043
  }
1410
2044
  //#endregion
1411
2045
  //#region src/cli/command-registration.ts
2046
+ var LocalAttributeOption = class extends Option {
2047
+ localAttributeName;
2048
+ constructor(flags, description, localAttributeName) {
2049
+ super(flags, description);
2050
+ this.localAttributeName = localAttributeName;
2051
+ }
2052
+ attributeName() {
2053
+ return this.localAttributeName;
2054
+ }
2055
+ };
1412
2056
  function addSessionsListOptions(command) {
1413
2057
  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));
1414
2058
  }
@@ -1439,6 +2083,12 @@ function registerSessionsCommand(parent, explicitAgentName, config) {
1439
2083
  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) {
1440
2084
  await handleSessionsHistory(explicitAgentName, name, { limit: flags.tail ?? 0 }, this, config);
1441
2085
  });
2086
+ 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) {
2087
+ await handleSessionsExport(explicitAgentName, name, flags, this, config);
2088
+ });
2089
+ 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) {
2090
+ await handleSessionsImport(explicitAgentName, archivePath, flags, this, config);
2091
+ });
1442
2092
  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) {
1443
2093
  await handleSessionsPrune(explicitAgentName, flags, this, config);
1444
2094
  });
@@ -1491,7 +2141,7 @@ function registerAgentCommand(program, agentName, config) {
1491
2141
  }
1492
2142
  function registerFlowCommand(program, config) {
1493
2143
  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) {
1494
- const { handleFlowRun } = await import("./cli-Bf3yjqzE.js");
2144
+ const { handleFlowRun } = await import("./cli-CC2w0U-A.js");
1495
2145
  await handleFlowRun(file, flags, this, config);
1496
2146
  });
1497
2147
  }
@@ -1506,6 +2156,7 @@ function registerDefaultCommands(program, config) {
1506
2156
  });
1507
2157
  registerSessionsCommand(program, void 0, config);
1508
2158
  registerConfigCommand(program, config);
2159
+ registerCompareCommand(program, config);
1509
2160
  registerFlowCommand(program, config);
1510
2161
  }
1511
2162
  //#endregion
@@ -1587,54 +2238,12 @@ async function runQueueOwnerFromEnv(env) {
1587
2238
  await runSessionQueueOwner(parseQueueOwnerPayload(payload));
1588
2239
  }
1589
2240
  //#endregion
1590
- //#region src/version.ts
1591
- const UNKNOWN_VERSION = "0.0.0-unknown";
1592
- const MODULE_DIR = path.dirname(fileURLToPath(import.meta.url));
1593
- let cachedVersion = null;
1594
- function parseVersion(value) {
1595
- if (typeof value !== "string") return null;
1596
- const trimmed = value.trim();
1597
- return trimmed.length > 0 ? trimmed : null;
1598
- }
1599
- function readPackageVersion(packageJsonPath) {
1600
- try {
1601
- return parseVersion(JSON.parse(readFileSync(packageJsonPath, "utf8")).version);
1602
- } catch {
1603
- return null;
1604
- }
1605
- }
1606
- function resolveVersionFromAncestors(startDir) {
1607
- let current = startDir;
1608
- while (true) {
1609
- const packageVersion = readPackageVersion(path.join(current, "package.json"));
1610
- if (packageVersion) return packageVersion;
1611
- const parent = path.dirname(current);
1612
- if (parent === current) return null;
1613
- current = parent;
1614
- }
1615
- }
1616
- function resolveAcpxVersion(params) {
1617
- const envVersion = resolvePackageEnvVersion(params?.env ?? process.env);
1618
- if (envVersion) return envVersion;
1619
- if (params?.packageJsonPath) return readPackageVersion(params.packageJsonPath) ?? UNKNOWN_VERSION;
1620
- return resolveVersionFromAncestors(MODULE_DIR) ?? UNKNOWN_VERSION;
1621
- }
1622
- function resolvePackageEnvVersion(env) {
1623
- const envPackageName = parseVersion(env.npm_package_name);
1624
- const envVersion = parseVersion(env.npm_package_version);
1625
- return envPackageName === "acpx" ? envVersion : null;
1626
- }
1627
- function getAcpxVersion() {
1628
- if (cachedVersion) return cachedVersion;
1629
- cachedVersion = resolveAcpxVersion();
1630
- return cachedVersion;
1631
- }
1632
- //#endregion
1633
2241
  //#region src/cli-core.ts
1634
2242
  const TOP_LEVEL_VERBS = new Set([
1635
2243
  "prompt",
1636
2244
  "exec",
1637
2245
  "cancel",
2246
+ "compare",
1638
2247
  "flow",
1639
2248
  "set-mode",
1640
2249
  "set",
@@ -1872,13 +2481,18 @@ async function handleProgramParseError(error, requestedOutputPolicy) {
1872
2481
  process.exit(exitCodeForOutputErrorCode(normalized.code));
1873
2482
  }
1874
2483
  async function main(argv = process.argv) {
1875
- const rawArgs = argv.slice(2);
2484
+ const rawArgs = normalizeLifecycleScriptArgs(argv.slice(2));
2485
+ const normalizedArgv = [
2486
+ argv[0] ?? "node",
2487
+ argv[1] ?? "acpx",
2488
+ ...rawArgs
2489
+ ];
1876
2490
  if (await handleQueueOwnerCommand(argv)) return;
1877
2491
  if (isTopLevelVersionRequest(rawArgs)) {
1878
2492
  process.stdout.write(`${getAcpxVersion()}\n`);
1879
2493
  return;
1880
2494
  }
1881
- await maybeHandleSkillflag(argv);
2495
+ await maybeHandleSkillflag(normalizedArgv);
1882
2496
  const config = await loadResolvedConfig(detectInitialCwd(rawArgs));
1883
2497
  const requestedJsonStrict = detectJsonStrict(rawArgs);
1884
2498
  const requestedOutputPolicy = {
@@ -1907,7 +2521,7 @@ async function main(argv = process.argv) {
1907
2521
  try {
1908
2522
  await runWithOutputPolicy(requestedOutputPolicy, async () => {
1909
2523
  try {
1910
- await program.parseAsync(argv);
2524
+ await program.parseAsync(normalizedArgv);
1911
2525
  } catch (error) {
1912
2526
  await handleProgramParseError(error, requestedOutputPolicy);
1913
2527
  }
@@ -1916,6 +2530,10 @@ async function main(argv = process.argv) {
1916
2530
  flushPerfMetricsCapture();
1917
2531
  }
1918
2532
  }
2533
+ function normalizeLifecycleScriptArgs(rawArgs) {
2534
+ if (rawArgs[0] === "--" && (process.env.npm_lifecycle_event || process.env.npm_lifecycle_script)) return rawArgs.slice(1);
2535
+ return rawArgs;
2536
+ }
1919
2537
  //#endregion
1920
2538
  //#region src/cli.ts
1921
2539
  function installBrokenPipeHandler(stream) {