acpx 0.1.6 → 0.1.8

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.
package/dist/cli.js CHANGED
@@ -50,6 +50,7 @@ import path from "path";
50
50
  var DEFAULT_TIMEOUT_MS = void 0;
51
51
  var DEFAULT_TTL_MS = 3e5;
52
52
  var DEFAULT_PERMISSION_MODE = "approve-reads";
53
+ var DEFAULT_NON_INTERACTIVE_PERMISSION_POLICY = "deny";
53
54
  var DEFAULT_AUTH_POLICY = "skip";
54
55
  var DEFAULT_OUTPUT_FORMAT = "text";
55
56
  var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set([
@@ -57,6 +58,7 @@ var VALID_PERMISSION_MODES = /* @__PURE__ */ new Set([
57
58
  "approve-reads",
58
59
  "deny-all"
59
60
  ]);
61
+ var VALID_NON_INTERACTIVE_PERMISSION_POLICIES = /* @__PURE__ */ new Set(["deny", "fail"]);
60
62
  var VALID_AUTH_POLICIES = /* @__PURE__ */ new Set(["skip", "fail"]);
61
63
  var VALID_OUTPUT_FORMATS = /* @__PURE__ */ new Set(["text", "json", "quiet"]);
62
64
  function defaultGlobalConfigPath() {
@@ -101,6 +103,19 @@ function parsePermissionMode(value, sourcePath) {
101
103
  }
102
104
  return value;
103
105
  }
106
+ function parseNonInteractivePermissionPolicy(value, sourcePath) {
107
+ if (value == null) {
108
+ return void 0;
109
+ }
110
+ if (typeof value !== "string" || !VALID_NON_INTERACTIVE_PERMISSION_POLICIES.has(
111
+ value
112
+ )) {
113
+ throw new Error(
114
+ `Invalid config nonInteractivePermissions in ${sourcePath}: expected deny or fail`
115
+ );
116
+ }
117
+ return value;
118
+ }
104
119
  function parseAuthPolicy(value, sourcePath) {
105
120
  if (value == null) {
106
121
  return void 0;
@@ -225,6 +240,13 @@ async function loadResolvedConfig(cwd) {
225
240
  const projectConfig = projectResult.config;
226
241
  const defaultAgent = parseDefaultAgent(projectConfig?.defaultAgent, projectPath) ?? parseDefaultAgent(globalConfig?.defaultAgent, globalPath) ?? DEFAULT_AGENT_NAME;
227
242
  const defaultPermissions = parsePermissionMode(projectConfig?.defaultPermissions, projectPath) ?? parsePermissionMode(globalConfig?.defaultPermissions, globalPath) ?? DEFAULT_PERMISSION_MODE;
243
+ const nonInteractivePermissions = parseNonInteractivePermissionPolicy(
244
+ projectConfig?.nonInteractivePermissions,
245
+ projectPath
246
+ ) ?? parseNonInteractivePermissionPolicy(
247
+ globalConfig?.nonInteractivePermissions,
248
+ globalPath
249
+ ) ?? DEFAULT_NON_INTERACTIVE_PERMISSION_POLICY;
228
250
  const authPolicy = parseAuthPolicy(projectConfig?.authPolicy, projectPath) ?? parseAuthPolicy(globalConfig?.authPolicy, globalPath) ?? DEFAULT_AUTH_POLICY;
229
251
  const ttlMs = parseTtlMs(projectConfig?.ttl, projectPath) ?? parseTtlMs(globalConfig?.ttl, globalPath) ?? DEFAULT_TTL_MS;
230
252
  const timeoutConfiguredInProject = projectConfig != null && Object.prototype.hasOwnProperty.call(projectConfig, "timeout");
@@ -247,6 +269,7 @@ async function loadResolvedConfig(cwd) {
247
269
  return {
248
270
  defaultAgent,
249
271
  defaultPermissions,
272
+ nonInteractivePermissions,
250
273
  authPolicy,
251
274
  ttlMs,
252
275
  timeoutMs,
@@ -267,6 +290,7 @@ function toConfigDisplay(config) {
267
290
  return {
268
291
  defaultAgent: config.defaultAgent,
269
292
  defaultPermissions: config.defaultPermissions,
293
+ nonInteractivePermissions: config.nonInteractivePermissions,
270
294
  authPolicy: config.authPolicy,
271
295
  ttl: Math.round(config.ttlMs / 1e3),
272
296
  timeout: config.timeoutMs == null ? null : config.timeoutMs / 1e3,
@@ -289,6 +313,7 @@ async function initGlobalConfigFile() {
289
313
  const payload = {
290
314
  defaultAgent: DEFAULT_AGENT_NAME,
291
315
  defaultPermissions: "approve-all",
316
+ nonInteractivePermissions: "deny",
292
317
  authPolicy: "skip",
293
318
  ttl: 300,
294
319
  timeout: null,
@@ -304,6 +329,293 @@ async function initGlobalConfigFile() {
304
329
  };
305
330
  }
306
331
 
332
+ // src/errors.ts
333
+ var AcpxOperationalError = class extends Error {
334
+ outputCode;
335
+ detailCode;
336
+ origin;
337
+ retryable;
338
+ acp;
339
+ outputAlreadyEmitted;
340
+ constructor(message, options) {
341
+ super(message, options);
342
+ this.name = new.target.name;
343
+ this.outputCode = options?.outputCode;
344
+ this.detailCode = options?.detailCode;
345
+ this.origin = options?.origin;
346
+ this.retryable = options?.retryable;
347
+ this.acp = options?.acp;
348
+ this.outputAlreadyEmitted = options?.outputAlreadyEmitted;
349
+ }
350
+ };
351
+ var SessionNotFoundError = class extends AcpxOperationalError {
352
+ sessionId;
353
+ constructor(sessionId) {
354
+ super(`Session not found: ${sessionId}`);
355
+ this.sessionId = sessionId;
356
+ }
357
+ };
358
+ var SessionResolutionError = class extends AcpxOperationalError {
359
+ };
360
+ var AgentSpawnError = class extends AcpxOperationalError {
361
+ agentCommand;
362
+ constructor(agentCommand, cause) {
363
+ super(`Failed to spawn agent command: ${agentCommand}`, {
364
+ cause: cause instanceof Error ? cause : void 0
365
+ });
366
+ this.agentCommand = agentCommand;
367
+ }
368
+ };
369
+ var AuthPolicyError = class extends AcpxOperationalError {
370
+ constructor(message, options) {
371
+ super(message, {
372
+ outputCode: "RUNTIME",
373
+ detailCode: "AUTH_REQUIRED",
374
+ origin: "acp",
375
+ ...options
376
+ });
377
+ }
378
+ };
379
+ var QueueConnectionError = class extends AcpxOperationalError {
380
+ };
381
+ var QueueProtocolError = class extends AcpxOperationalError {
382
+ };
383
+ var PermissionDeniedError = class extends AcpxOperationalError {
384
+ };
385
+ var PermissionPromptUnavailableError = class extends AcpxOperationalError {
386
+ constructor() {
387
+ super("Permission prompt unavailable in non-interactive mode");
388
+ }
389
+ };
390
+
391
+ // src/types.ts
392
+ var EXIT_CODES = {
393
+ SUCCESS: 0,
394
+ ERROR: 1,
395
+ USAGE: 2,
396
+ TIMEOUT: 3,
397
+ NO_SESSION: 4,
398
+ PERMISSION_DENIED: 5,
399
+ INTERRUPTED: 130
400
+ };
401
+ var OUTPUT_FORMATS = ["text", "json", "quiet"];
402
+ var AUTH_POLICIES = ["skip", "fail"];
403
+ var NON_INTERACTIVE_PERMISSION_POLICIES = ["deny", "fail"];
404
+ var OUTPUT_ERROR_CODES = [
405
+ "NO_SESSION",
406
+ "TIMEOUT",
407
+ "PERMISSION_DENIED",
408
+ "PERMISSION_PROMPT_UNAVAILABLE",
409
+ "RUNTIME",
410
+ "USAGE"
411
+ ];
412
+ var OUTPUT_ERROR_ORIGINS = ["cli", "runtime", "queue", "acp"];
413
+
414
+ // src/error-normalization.ts
415
+ var RESOURCE_NOT_FOUND_ACP_CODES = /* @__PURE__ */ new Set([-32001, -32002]);
416
+ var AUTH_REQUIRED_ACP_CODES = /* @__PURE__ */ new Set([-32e3]);
417
+ function asRecord(value) {
418
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
419
+ return void 0;
420
+ }
421
+ return value;
422
+ }
423
+ function isAuthRequiredMessage(value) {
424
+ if (!value) {
425
+ return false;
426
+ }
427
+ const normalized = value.toLowerCase();
428
+ return normalized.includes("auth required") || normalized.includes("authentication required") || normalized.includes("authorization required") || normalized.includes("credential required") || normalized.includes("credentials required") || normalized.includes("token required") || normalized.includes("login required");
429
+ }
430
+ function isAcpAuthRequiredPayload(acp) {
431
+ if (!acp) {
432
+ return false;
433
+ }
434
+ if (!AUTH_REQUIRED_ACP_CODES.has(acp.code)) {
435
+ return false;
436
+ }
437
+ if (isAuthRequiredMessage(acp.message)) {
438
+ return true;
439
+ }
440
+ const data = asRecord(acp.data);
441
+ if (!data) {
442
+ return false;
443
+ }
444
+ if (data.authRequired === true) {
445
+ return true;
446
+ }
447
+ const methodId = data.methodId;
448
+ if (typeof methodId === "string" && methodId.trim().length > 0) {
449
+ return true;
450
+ }
451
+ const methods = data.methods;
452
+ if (Array.isArray(methods) && methods.length > 0) {
453
+ return true;
454
+ }
455
+ return false;
456
+ }
457
+ function isOutputErrorCode(value) {
458
+ return typeof value === "string" && OUTPUT_ERROR_CODES.includes(value);
459
+ }
460
+ function isOutputErrorOrigin(value) {
461
+ return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
462
+ }
463
+ function readOutputErrorMeta(error) {
464
+ const record = asRecord(error);
465
+ if (!record) {
466
+ return {};
467
+ }
468
+ const outputCode = isOutputErrorCode(record.outputCode) ? record.outputCode : void 0;
469
+ const detailCode = typeof record.detailCode === "string" && record.detailCode.trim().length > 0 ? record.detailCode : void 0;
470
+ const origin = isOutputErrorOrigin(record.origin) ? record.origin : void 0;
471
+ const retryable = typeof record.retryable === "boolean" ? record.retryable : void 0;
472
+ const acp = toAcpErrorPayload(record.acp);
473
+ return {
474
+ outputCode,
475
+ detailCode,
476
+ origin,
477
+ retryable,
478
+ acp
479
+ };
480
+ }
481
+ function toAcpErrorPayload(value) {
482
+ const record = asRecord(value);
483
+ if (!record) {
484
+ return void 0;
485
+ }
486
+ if (typeof record.code !== "number" || !Number.isFinite(record.code)) {
487
+ return void 0;
488
+ }
489
+ if (typeof record.message !== "string" || record.message.length === 0) {
490
+ return void 0;
491
+ }
492
+ return {
493
+ code: record.code,
494
+ message: record.message,
495
+ data: record.data
496
+ };
497
+ }
498
+ function extractAcpErrorInternal(value, depth) {
499
+ if (depth > 5) {
500
+ return void 0;
501
+ }
502
+ const direct = toAcpErrorPayload(value);
503
+ if (direct) {
504
+ return direct;
505
+ }
506
+ const record = asRecord(value);
507
+ if (!record) {
508
+ return void 0;
509
+ }
510
+ if ("error" in record) {
511
+ const nested = extractAcpErrorInternal(record.error, depth + 1);
512
+ if (nested) {
513
+ return nested;
514
+ }
515
+ }
516
+ if ("cause" in record) {
517
+ const nested = extractAcpErrorInternal(record.cause, depth + 1);
518
+ if (nested) {
519
+ return nested;
520
+ }
521
+ }
522
+ return void 0;
523
+ }
524
+ function isTimeoutLike(error) {
525
+ return error instanceof Error && error.name === "TimeoutError";
526
+ }
527
+ function isNoSessionLike(error) {
528
+ return error instanceof Error && error.name === "NoSessionError";
529
+ }
530
+ function isUsageLike(error) {
531
+ if (!(error instanceof Error)) {
532
+ return false;
533
+ }
534
+ return error.name === "CommanderError" || error.name === "InvalidArgumentError" || asRecord(error)?.code === "commander.invalidArgument";
535
+ }
536
+ function formatErrorMessage(error) {
537
+ if (error instanceof Error) {
538
+ return error.message;
539
+ }
540
+ if (error && typeof error === "object") {
541
+ const maybeMessage = error.message;
542
+ if (typeof maybeMessage === "string" && maybeMessage.length > 0) {
543
+ return maybeMessage;
544
+ }
545
+ try {
546
+ return JSON.stringify(error);
547
+ } catch {
548
+ }
549
+ }
550
+ return String(error);
551
+ }
552
+ function extractAcpError(error) {
553
+ return extractAcpErrorInternal(error, 0);
554
+ }
555
+ function isAcpResourceNotFoundError(error) {
556
+ const acp = extractAcpError(error);
557
+ if (acp && RESOURCE_NOT_FOUND_ACP_CODES.has(acp.code)) {
558
+ return true;
559
+ }
560
+ const message = formatErrorMessage(error).toLowerCase();
561
+ return message.includes("resource_not_found") || message.includes("resource not found") || message.includes("session not found") || message.includes("unknown session");
562
+ }
563
+ function mapErrorCode(error) {
564
+ if (error instanceof PermissionPromptUnavailableError) {
565
+ return "PERMISSION_PROMPT_UNAVAILABLE";
566
+ }
567
+ if (error instanceof PermissionDeniedError) {
568
+ return "PERMISSION_DENIED";
569
+ }
570
+ if (isTimeoutLike(error)) {
571
+ return "TIMEOUT";
572
+ }
573
+ if (isNoSessionLike(error) || isAcpResourceNotFoundError(error)) {
574
+ return "NO_SESSION";
575
+ }
576
+ if (isUsageLike(error)) {
577
+ return "USAGE";
578
+ }
579
+ return void 0;
580
+ }
581
+ function normalizeOutputError(error, options = {}) {
582
+ const meta = readOutputErrorMeta(error);
583
+ const mapped = mapErrorCode(error);
584
+ let code = mapped ?? options.defaultCode ?? "RUNTIME";
585
+ if (meta.outputCode) {
586
+ code = meta.outputCode;
587
+ }
588
+ if (code === "RUNTIME" && isAcpResourceNotFoundError(error)) {
589
+ code = "NO_SESSION";
590
+ }
591
+ const acp = options.acp ?? meta.acp ?? extractAcpError(error);
592
+ const detailCode = meta.detailCode ?? options.detailCode ?? (error instanceof AuthPolicyError || isAcpAuthRequiredPayload(acp) ? "AUTH_REQUIRED" : void 0);
593
+ return {
594
+ code,
595
+ message: formatErrorMessage(error),
596
+ detailCode,
597
+ origin: meta.origin ?? options.origin,
598
+ retryable: meta.retryable ?? options.retryable,
599
+ acp
600
+ };
601
+ }
602
+ function exitCodeForOutputErrorCode(code) {
603
+ switch (code) {
604
+ case "USAGE":
605
+ return EXIT_CODES.USAGE;
606
+ case "TIMEOUT":
607
+ return EXIT_CODES.TIMEOUT;
608
+ case "NO_SESSION":
609
+ return EXIT_CODES.NO_SESSION;
610
+ case "PERMISSION_DENIED":
611
+ case "PERMISSION_PROMPT_UNAVAILABLE":
612
+ return EXIT_CODES.PERMISSION_DENIED;
613
+ case "RUNTIME":
614
+ default:
615
+ return EXIT_CODES.ERROR;
616
+ }
617
+ }
618
+
307
619
  // src/output.ts
308
620
  var MAX_THOUGHT_CHARS = 900;
309
621
  var MAX_INLINE_CHARS = 220;
@@ -324,6 +636,8 @@ var OUTPUT_PRIORITY_KEYS = [
324
636
  function nowIso() {
325
637
  return (/* @__PURE__ */ new Date()).toISOString();
326
638
  }
639
+ var DEFAULT_JSON_SESSION_ID = "unknown";
640
+ var DEFAULT_JSON_STREAM = "prompt";
327
641
  function asStatus(status) {
328
642
  return status ?? "unknown";
329
643
  }
@@ -344,7 +658,7 @@ function toStatusLabel(status) {
344
658
  return "running";
345
659
  }
346
660
  }
347
- function asRecord(value) {
661
+ function asRecord2(value) {
348
662
  if (!value || typeof value !== "object" || Array.isArray(value)) {
349
663
  return void 0;
350
664
  }
@@ -438,7 +752,7 @@ function summarizeToolInput(rawInput) {
438
752
  if (typeof rawInput === "string" || typeof rawInput === "number" || typeof rawInput === "boolean") {
439
753
  return toInline(String(rawInput));
440
754
  }
441
- const record = asRecord(rawInput);
755
+ const record = asRecord2(rawInput);
442
756
  if (record) {
443
757
  const command = readFirstString(record, ["command", "cmd", "program"]);
444
758
  const args = readFirstStringArray(record, ["args", "arguments"]);
@@ -572,7 +886,7 @@ function extractOutputText(value, depth = 0, seen = /* @__PURE__ */ new Set()) {
572
886
  }
573
887
  return dedupeStrings(parts).join("\n");
574
888
  }
575
- const record = asRecord(value);
889
+ const record = asRecord2(value);
576
890
  if (!record) {
577
891
  return void 0;
578
892
  }
@@ -641,6 +955,8 @@ var TextOutputFormatter = class {
641
955
  this.stdout = stdout;
642
956
  this.useColor = Boolean(stdout.isTTY);
643
957
  }
958
+ setContext(_context) {
959
+ }
644
960
  onSessionUpdate(notification) {
645
961
  const update = notification.update;
646
962
  if (update.sessionUpdate !== "agent_thought_chunk") {
@@ -684,6 +1000,11 @@ var TextOutputFormatter = class {
684
1000
  this.beginSection("done");
685
1001
  this.writeLine(this.dim(`[done] ${stopReason}`));
686
1002
  }
1003
+ onError(params) {
1004
+ this.flushThoughtBuffer();
1005
+ this.beginSection("done");
1006
+ this.writeLine(this.formatAnsi(`[error] ${params.code}: ${params.message}`, "31"));
1007
+ }
687
1008
  onClientOperation(operation) {
688
1009
  this.flushThoughtBuffer();
689
1010
  this.beginSection("client");
@@ -870,8 +1191,29 @@ var TextOutputFormatter = class {
870
1191
  };
871
1192
  var JsonOutputFormatter = class {
872
1193
  stdout;
873
- constructor(stdout) {
1194
+ sessionId;
1195
+ requestId;
1196
+ stream;
1197
+ sequence = 0;
1198
+ constructor(stdout, context) {
874
1199
  this.stdout = stdout;
1200
+ this.sessionId = context?.sessionId?.trim() || DEFAULT_JSON_SESSION_ID;
1201
+ this.requestId = context?.requestId?.trim() || void 0;
1202
+ this.stream = context?.stream ?? DEFAULT_JSON_STREAM;
1203
+ }
1204
+ setContext(context) {
1205
+ const nextSessionId = context.sessionId?.trim() || this.sessionId || DEFAULT_JSON_SESSION_ID;
1206
+ const nextRequestId = context.requestId?.trim() || void 0;
1207
+ const nextStream = context.stream ?? this.stream ?? DEFAULT_JSON_STREAM;
1208
+ const sessionChanged = nextSessionId !== this.sessionId;
1209
+ const requestChanged = nextRequestId !== this.requestId;
1210
+ const streamChanged = nextStream !== this.stream;
1211
+ this.sessionId = nextSessionId;
1212
+ this.requestId = nextRequestId;
1213
+ this.stream = nextStream;
1214
+ if (sessionChanged || requestChanged || streamChanged) {
1215
+ this.sequence = 0;
1216
+ }
875
1217
  }
876
1218
  onSessionUpdate(notification) {
877
1219
  const update = notification.update;
@@ -945,6 +1287,18 @@ var JsonOutputFormatter = class {
945
1287
  timestamp: nowIso()
946
1288
  });
947
1289
  }
1290
+ onError(params) {
1291
+ this.emit({
1292
+ type: "error",
1293
+ code: params.code,
1294
+ detailCode: params.detailCode,
1295
+ origin: params.origin,
1296
+ message: params.message,
1297
+ retryable: params.retryable,
1298
+ acp: params.acp,
1299
+ timestamp: params.timestamp ?? nowIso()
1300
+ });
1301
+ }
948
1302
  onClientOperation(operation) {
949
1303
  this.emit({
950
1304
  type: "client_operation",
@@ -958,7 +1312,15 @@ var JsonOutputFormatter = class {
958
1312
  flush() {
959
1313
  }
960
1314
  emit(event) {
961
- this.stdout.write(`${JSON.stringify(event)}
1315
+ const payload = {
1316
+ eventVersion: 1,
1317
+ sessionId: this.sessionId || DEFAULT_JSON_SESSION_ID,
1318
+ requestId: this.requestId,
1319
+ seq: this.sequence++,
1320
+ stream: this.stream ?? DEFAULT_JSON_STREAM,
1321
+ ...event
1322
+ };
1323
+ this.stdout.write(`${JSON.stringify(payload)}
962
1324
  `);
963
1325
  }
964
1326
  };
@@ -968,6 +1330,8 @@ var QuietOutputFormatter = class {
968
1330
  constructor(stdout) {
969
1331
  this.stdout = stdout;
970
1332
  }
1333
+ setContext(_context) {
1334
+ }
971
1335
  onSessionUpdate(notification) {
972
1336
  const update = notification.update;
973
1337
  if (update.sessionUpdate !== "agent_message_chunk") {
@@ -982,6 +1346,8 @@ var QuietOutputFormatter = class {
982
1346
  const text = this.chunks.join("");
983
1347
  this.stdout.write(text.endsWith("\n") ? text : `${text}
984
1348
  `);
1349
+ }
1350
+ onError(_params) {
985
1351
  }
986
1352
  onClientOperation(_operation) {
987
1353
  }
@@ -994,7 +1360,7 @@ function createOutputFormatter(format, options = {}) {
994
1360
  case "text":
995
1361
  return new TextOutputFormatter(stdout);
996
1362
  case "json":
997
- return new JsonOutputFormatter(stdout);
1363
+ return new JsonOutputFormatter(stdout, options.jsonContext);
998
1364
  case "quiet":
999
1365
  return new QuietOutputFormatter(stdout);
1000
1366
  default: {
@@ -1018,40 +1384,6 @@ import { spawn as spawn2 } from "child_process";
1018
1384
  import path3 from "path";
1019
1385
  import { Readable, Writable } from "stream";
1020
1386
 
1021
- // src/errors.ts
1022
- var AcpxOperationalError = class extends Error {
1023
- constructor(message, options) {
1024
- super(message, options);
1025
- this.name = new.target.name;
1026
- }
1027
- };
1028
- var SessionNotFoundError = class extends AcpxOperationalError {
1029
- sessionId;
1030
- constructor(sessionId) {
1031
- super(`Session not found: ${sessionId}`);
1032
- this.sessionId = sessionId;
1033
- }
1034
- };
1035
- var SessionResolutionError = class extends AcpxOperationalError {
1036
- };
1037
- var AgentSpawnError = class extends AcpxOperationalError {
1038
- agentCommand;
1039
- constructor(agentCommand, cause) {
1040
- super(`Failed to spawn agent command: ${agentCommand}`, {
1041
- cause: cause instanceof Error ? cause : void 0
1042
- });
1043
- this.agentCommand = agentCommand;
1044
- }
1045
- };
1046
- var AuthPolicyError = class extends AcpxOperationalError {
1047
- };
1048
- var QueueConnectionError = class extends AcpxOperationalError {
1049
- };
1050
- var QueueProtocolError = class extends AcpxOperationalError {
1051
- };
1052
- var PermissionDeniedError = class extends AcpxOperationalError {
1053
- };
1054
-
1055
1387
  // src/filesystem.ts
1056
1388
  import fs2 from "fs/promises";
1057
1389
  import path2 from "path";
@@ -1115,15 +1447,22 @@ async function defaultConfirmWrite(filePath, preview) {
1115
1447
  prompt: "Allow write? (y/N) "
1116
1448
  });
1117
1449
  }
1450
+ function canPromptForPermission() {
1451
+ return Boolean(process.stdin.isTTY && process.stderr.isTTY);
1452
+ }
1118
1453
  var FileSystemHandlers = class {
1119
1454
  rootDir;
1120
1455
  permissionMode;
1456
+ nonInteractivePermissions;
1121
1457
  onOperation;
1458
+ usesDefaultConfirmWrite;
1122
1459
  confirmWrite;
1123
1460
  constructor(options) {
1124
1461
  this.rootDir = path2.resolve(options.cwd);
1125
1462
  this.permissionMode = options.permissionMode;
1463
+ this.nonInteractivePermissions = options.nonInteractivePermissions ?? "deny";
1126
1464
  this.onOperation = options.onOperation;
1465
+ this.usesDefaultConfirmWrite = options.confirmWrite == null;
1127
1466
  this.confirmWrite = options.confirmWrite ?? defaultConfirmWrite;
1128
1467
  }
1129
1468
  async readTextFile(params) {
@@ -1208,6 +1547,9 @@ var FileSystemHandlers = class {
1208
1547
  if (this.permissionMode === "deny-all") {
1209
1548
  return false;
1210
1549
  }
1550
+ if (this.usesDefaultConfirmWrite && this.nonInteractivePermissions === "fail" && !canPromptForPermission()) {
1551
+ throw new PermissionPromptUnavailableError();
1552
+ }
1211
1553
  return await this.confirmWrite(filePath, preview);
1212
1554
  }
1213
1555
  resolvePathWithinRoot(rawPath) {
@@ -1312,7 +1654,10 @@ async function promptForToolPermission(params) {
1312
1654
  [permission] Allow ${toolName} [${toolKind}]? (y/N) `
1313
1655
  });
1314
1656
  }
1315
- async function resolvePermissionRequest(params, mode) {
1657
+ function canPromptForPermission2() {
1658
+ return Boolean(process.stdin.isTTY && process.stderr.isTTY);
1659
+ }
1660
+ async function resolvePermissionRequest(params, mode, nonInteractivePolicy = "deny") {
1316
1661
  const options = params.options ?? [];
1317
1662
  if (options.length === 0) {
1318
1663
  return cancelled();
@@ -1335,6 +1680,15 @@ async function resolvePermissionRequest(params, mode) {
1335
1680
  if (isAutoApprovedReadKind(kind) && allowOption) {
1336
1681
  return selected(allowOption.optionId);
1337
1682
  }
1683
+ if (!canPromptForPermission2()) {
1684
+ if (nonInteractivePolicy === "fail") {
1685
+ throw new PermissionPromptUnavailableError();
1686
+ }
1687
+ if (rejectOption) {
1688
+ return selected(rejectOption.optionId);
1689
+ }
1690
+ return cancelled();
1691
+ }
1338
1692
  const approved = await promptForToolPermission(params);
1339
1693
  if (approved && allowOption) {
1340
1694
  return selected(allowOption.optionId);
@@ -1361,6 +1715,40 @@ function classifyPermissionDecision(params, response) {
1361
1715
  return "denied";
1362
1716
  }
1363
1717
 
1718
+ // src/runtime-session-id.ts
1719
+ var RUNTIME_SESSION_ID_META_KEYS = [
1720
+ "runtimeSessionId",
1721
+ "providerSessionId",
1722
+ "codexSessionId",
1723
+ "claudeSessionId"
1724
+ ];
1725
+ function normalizeRuntimeSessionId(value) {
1726
+ if (typeof value !== "string") {
1727
+ return void 0;
1728
+ }
1729
+ const trimmed = value.trim();
1730
+ return trimmed.length > 0 ? trimmed : void 0;
1731
+ }
1732
+ function asMetaRecord(meta) {
1733
+ if (!meta || typeof meta !== "object" || Array.isArray(meta)) {
1734
+ return void 0;
1735
+ }
1736
+ return meta;
1737
+ }
1738
+ function extractRuntimeSessionId(meta) {
1739
+ const record = asMetaRecord(meta);
1740
+ if (!record) {
1741
+ return void 0;
1742
+ }
1743
+ for (const key of RUNTIME_SESSION_ID_META_KEYS) {
1744
+ const normalized = normalizeRuntimeSessionId(record[key]);
1745
+ if (normalized) {
1746
+ return normalized;
1747
+ }
1748
+ }
1749
+ return void 0;
1750
+ }
1751
+
1364
1752
  // src/terminal.ts
1365
1753
  import { spawn } from "child_process";
1366
1754
  import { randomUUID } from "crypto";
@@ -1419,6 +1807,9 @@ async function defaultConfirmExecute(commandLine) {
1419
1807
  [permission] Allow terminal command "${commandLine}"? (y/N) `
1420
1808
  });
1421
1809
  }
1810
+ function canPromptForPermission3() {
1811
+ return Boolean(process.stdin.isTTY && process.stderr.isTTY);
1812
+ }
1422
1813
  function waitMs(ms) {
1423
1814
  return new Promise((resolve) => {
1424
1815
  setTimeout(resolve, Math.max(0, ms));
@@ -1427,14 +1818,18 @@ function waitMs(ms) {
1427
1818
  var TerminalManager = class {
1428
1819
  cwd;
1429
1820
  permissionMode;
1821
+ nonInteractivePermissions;
1430
1822
  onOperation;
1823
+ usesDefaultConfirmExecute;
1431
1824
  confirmExecute;
1432
1825
  killGraceMs;
1433
1826
  terminals = /* @__PURE__ */ new Map();
1434
1827
  constructor(options) {
1435
1828
  this.cwd = options.cwd;
1436
1829
  this.permissionMode = options.permissionMode;
1830
+ this.nonInteractivePermissions = options.nonInteractivePermissions ?? "deny";
1437
1831
  this.onOperation = options.onOperation;
1832
+ this.usesDefaultConfirmExecute = options.confirmExecute == null;
1438
1833
  this.confirmExecute = options.confirmExecute ?? defaultConfirmExecute;
1439
1834
  this.killGraceMs = Math.max(
1440
1835
  0,
@@ -1656,6 +2051,9 @@ var TerminalManager = class {
1656
2051
  if (this.permissionMode === "deny-all") {
1657
2052
  return false;
1658
2053
  }
2054
+ if (this.usesDefaultConfirmExecute && this.nonInteractivePermissions === "fail" && !canPromptForPermission3()) {
2055
+ throw new PermissionPromptUnavailableError();
2056
+ }
1659
2057
  return await this.confirmExecute(commandLine);
1660
2058
  }
1661
2059
  isRunning(terminal) {
@@ -1693,6 +2091,24 @@ var TerminalManager = class {
1693
2091
  var REPLAY_IDLE_MS = 80;
1694
2092
  var REPLAY_DRAIN_TIMEOUT_MS = 5e3;
1695
2093
  var DRAIN_POLL_INTERVAL_MS = 20;
2094
+ function shouldSuppressSdkConsoleError(args) {
2095
+ if (args.length === 0) {
2096
+ return false;
2097
+ }
2098
+ return typeof args[0] === "string" && args[0] === "Error handling request";
2099
+ }
2100
+ function installSdkConsoleErrorSuppression() {
2101
+ const originalConsoleError = console.error;
2102
+ console.error = (...args) => {
2103
+ if (shouldSuppressSdkConsoleError(args)) {
2104
+ return;
2105
+ }
2106
+ originalConsoleError(...args);
2107
+ };
2108
+ return () => {
2109
+ console.error = originalConsoleError;
2110
+ };
2111
+ }
1696
2112
  function isoNow() {
1697
2113
  return (/* @__PURE__ */ new Date()).toISOString();
1698
2114
  }
@@ -1835,6 +2251,7 @@ var AcpClient = class {
1835
2251
  agentStartedAt;
1836
2252
  lastAgentExit;
1837
2253
  lastKnownPid;
2254
+ promptPermissionFailures = /* @__PURE__ */ new Map();
1838
2255
  constructor(options) {
1839
2256
  this.options = {
1840
2257
  ...options,
@@ -1845,11 +2262,13 @@ var AcpClient = class {
1845
2262
  this.filesystem = new FileSystemHandlers({
1846
2263
  cwd: this.options.cwd,
1847
2264
  permissionMode: this.options.permissionMode,
2265
+ nonInteractivePermissions: this.options.nonInteractivePermissions,
1848
2266
  onOperation: emitOperation
1849
2267
  });
1850
2268
  this.terminalManager = new TerminalManager({
1851
2269
  cwd: this.options.cwd,
1852
2270
  permissionMode: this.options.permissionMode,
2271
+ nonInteractivePermissions: this.options.nonInteractivePermissions,
1853
2272
  onOperation: emitOperation
1854
2273
  });
1855
2274
  }
@@ -1988,18 +2407,22 @@ var AcpClient = class {
1988
2407
  cwd: asAbsoluteCwd(cwd),
1989
2408
  mcpServers: []
1990
2409
  });
1991
- return result.sessionId;
2410
+ return {
2411
+ sessionId: result.sessionId,
2412
+ runtimeSessionId: extractRuntimeSessionId(result._meta)
2413
+ };
1992
2414
  }
1993
2415
  async loadSession(sessionId, cwd = this.options.cwd) {
1994
2416
  this.getConnection();
1995
- await this.loadSessionWithOptions(sessionId, cwd, {});
2417
+ return await this.loadSessionWithOptions(sessionId, cwd, {});
1996
2418
  }
1997
2419
  async loadSessionWithOptions(sessionId, cwd = this.options.cwd, options = {}) {
1998
2420
  const connection = this.getConnection();
1999
2421
  const previousSuppression = this.suppressSessionUpdates;
2000
2422
  this.suppressSessionUpdates = previousSuppression || Boolean(options.suppressReplayUpdates);
2423
+ let response;
2001
2424
  try {
2002
- await connection.loadSession({
2425
+ response = await connection.loadSession({
2003
2426
  sessionId,
2004
2427
  cwd: asAbsoluteCwd(cwd),
2005
2428
  mcpServers: []
@@ -2011,29 +2434,52 @@ var AcpClient = class {
2011
2434
  } finally {
2012
2435
  this.suppressSessionUpdates = previousSuppression;
2013
2436
  }
2437
+ return {
2438
+ runtimeSessionId: extractRuntimeSessionId(response?._meta)
2439
+ };
2014
2440
  }
2015
2441
  async prompt(sessionId, text) {
2016
2442
  const connection = this.getConnection();
2017
- const promptPromise = connection.prompt({
2018
- sessionId,
2019
- prompt: [
2020
- {
2021
- type: "text",
2022
- text
2023
- }
2024
- ]
2025
- });
2443
+ const restoreConsoleError = this.options.suppressSdkConsoleErrors ? installSdkConsoleErrorSuppression() : void 0;
2444
+ let promptPromise;
2445
+ try {
2446
+ promptPromise = connection.prompt({
2447
+ sessionId,
2448
+ prompt: [
2449
+ {
2450
+ type: "text",
2451
+ text
2452
+ }
2453
+ ]
2454
+ });
2455
+ } catch (error) {
2456
+ restoreConsoleError?.();
2457
+ throw error;
2458
+ }
2026
2459
  this.activePrompt = {
2027
2460
  sessionId,
2028
2461
  promise: promptPromise
2029
2462
  };
2030
2463
  try {
2031
- return await promptPromise;
2464
+ const response = await promptPromise;
2465
+ const permissionFailure = this.consumePromptPermissionFailure(sessionId);
2466
+ if (permissionFailure) {
2467
+ throw permissionFailure;
2468
+ }
2469
+ return response;
2470
+ } catch (error) {
2471
+ const permissionFailure = this.consumePromptPermissionFailure(sessionId);
2472
+ if (permissionFailure) {
2473
+ throw permissionFailure;
2474
+ }
2475
+ throw error;
2032
2476
  } finally {
2477
+ restoreConsoleError?.();
2033
2478
  if (this.activePrompt?.promise === promptPromise) {
2034
2479
  this.activePrompt = void 0;
2035
2480
  }
2036
2481
  this.cancellingSessionIds.delete(sessionId);
2482
+ this.promptPermissionFailures.delete(sessionId);
2037
2483
  }
2038
2484
  }
2039
2485
  async setSessionMode(sessionId, modeId) {
@@ -2110,6 +2556,7 @@ var AcpClient = class {
2110
2556
  this.suppressSessionUpdates = false;
2111
2557
  this.activePrompt = void 0;
2112
2558
  this.cancellingSessionIds.clear();
2559
+ this.promptPermissionFailures.clear();
2113
2560
  this.connection = void 0;
2114
2561
  this.agent = void 0;
2115
2562
  }
@@ -2177,10 +2624,26 @@ var AcpClient = class {
2177
2624
  }
2178
2625
  };
2179
2626
  }
2180
- const response = await resolvePermissionRequest(
2181
- params,
2182
- this.options.permissionMode
2183
- );
2627
+ let response;
2628
+ try {
2629
+ response = await resolvePermissionRequest(
2630
+ params,
2631
+ this.options.permissionMode,
2632
+ this.options.nonInteractivePermissions ?? "deny"
2633
+ );
2634
+ } catch (error) {
2635
+ if (error instanceof PermissionPromptUnavailableError) {
2636
+ this.notePromptPermissionFailure(params.sessionId, error);
2637
+ this.permissionStats.requested += 1;
2638
+ this.permissionStats.cancelled += 1;
2639
+ return {
2640
+ outcome: {
2641
+ outcome: "cancelled"
2642
+ }
2643
+ };
2644
+ }
2645
+ throw error;
2646
+ }
2184
2647
  const decision = classifyPermissionDecision(params, response);
2185
2648
  this.permissionStats.requested += 1;
2186
2649
  if (decision === "approved") {
@@ -2219,14 +2682,40 @@ var AcpClient = class {
2219
2682
  unexpectedDuringPrompt: !this.closing && Boolean(this.activePrompt)
2220
2683
  };
2221
2684
  }
2685
+ notePromptPermissionFailure(sessionId, error) {
2686
+ if (!this.promptPermissionFailures.has(sessionId)) {
2687
+ this.promptPermissionFailures.set(sessionId, error);
2688
+ }
2689
+ }
2690
+ consumePromptPermissionFailure(sessionId) {
2691
+ const error = this.promptPermissionFailures.get(sessionId);
2692
+ if (error) {
2693
+ this.promptPermissionFailures.delete(sessionId);
2694
+ }
2695
+ return error;
2696
+ }
2222
2697
  async handleReadTextFile(params) {
2223
2698
  return await this.filesystem.readTextFile(params);
2224
2699
  }
2225
2700
  async handleWriteTextFile(params) {
2226
- return await this.filesystem.writeTextFile(params);
2701
+ try {
2702
+ return await this.filesystem.writeTextFile(params);
2703
+ } catch (error) {
2704
+ if (error instanceof PermissionPromptUnavailableError) {
2705
+ this.notePromptPermissionFailure(params.sessionId, error);
2706
+ }
2707
+ throw error;
2708
+ }
2227
2709
  }
2228
2710
  async handleCreateTerminal(params) {
2229
- return await this.terminalManager.createTerminal(params);
2711
+ try {
2712
+ return await this.terminalManager.createTerminal(params);
2713
+ } catch (error) {
2714
+ if (error instanceof PermissionPromptUnavailableError) {
2715
+ this.notePromptPermissionFailure(params.sessionId, error);
2716
+ }
2717
+ throw error;
2718
+ }
2230
2719
  }
2231
2720
  async handleTerminalOutput(params) {
2232
2721
  return await this.terminalManager.terminalOutput(params);
@@ -2325,7 +2814,11 @@ var QueueOwnerTurnController = class {
2325
2814
  }
2326
2815
  assertCanHandleControlRequest() {
2327
2816
  if (this.state === "closing") {
2328
- throw new QueueConnectionError("Queue owner is closing");
2817
+ throw new QueueConnectionError("Queue owner is closing", {
2818
+ detailCode: "QUEUE_OWNER_SHUTTING_DOWN",
2819
+ origin: "queue",
2820
+ retryable: true
2821
+ });
2329
2822
  }
2330
2823
  }
2331
2824
  async requestCancel() {
@@ -2391,7 +2884,7 @@ import os2 from "os";
2391
2884
  import path4 from "path";
2392
2885
 
2393
2886
  // src/queue-messages.ts
2394
- function asRecord2(value) {
2887
+ function asRecord3(value) {
2395
2888
  if (!value || typeof value !== "object" || Array.isArray(value)) {
2396
2889
  return void 0;
2397
2890
  }
@@ -2400,8 +2893,34 @@ function asRecord2(value) {
2400
2893
  function isPermissionMode(value) {
2401
2894
  return value === "approve-all" || value === "approve-reads" || value === "deny-all";
2402
2895
  }
2896
+ function isNonInteractivePermissionPolicy(value) {
2897
+ return value === "deny" || value === "fail";
2898
+ }
2899
+ function isOutputErrorCode2(value) {
2900
+ return typeof value === "string" && OUTPUT_ERROR_CODES.includes(value);
2901
+ }
2902
+ function isOutputErrorOrigin2(value) {
2903
+ return typeof value === "string" && OUTPUT_ERROR_ORIGINS.includes(value);
2904
+ }
2905
+ function parseAcpError(value) {
2906
+ const record = asRecord3(value);
2907
+ if (!record) {
2908
+ return void 0;
2909
+ }
2910
+ if (typeof record.code !== "number" || !Number.isFinite(record.code)) {
2911
+ return void 0;
2912
+ }
2913
+ if (typeof record.message !== "string" || record.message.length === 0) {
2914
+ return void 0;
2915
+ }
2916
+ return {
2917
+ code: record.code,
2918
+ message: record.message,
2919
+ data: record.data
2920
+ };
2921
+ }
2403
2922
  function parseQueueRequest(raw) {
2404
- const request = asRecord2(raw);
2923
+ const request = asRecord3(raw);
2405
2924
  if (!request) {
2406
2925
  return null;
2407
2926
  }
@@ -2411,7 +2930,9 @@ function parseQueueRequest(raw) {
2411
2930
  const timeoutRaw = request.timeoutMs;
2412
2931
  const timeoutMs = typeof timeoutRaw === "number" && Number.isFinite(timeoutRaw) && timeoutRaw > 0 ? Math.round(timeoutRaw) : void 0;
2413
2932
  if (request.type === "submit_prompt") {
2414
- if (typeof request.message !== "string" || !isPermissionMode(request.permissionMode) || typeof request.waitForCompletion !== "boolean") {
2933
+ const nonInteractivePermissions = request.nonInteractivePermissions == null ? void 0 : isNonInteractivePermissionPolicy(request.nonInteractivePermissions) ? request.nonInteractivePermissions : null;
2934
+ const suppressSdkConsoleErrors = request.suppressSdkConsoleErrors == null ? void 0 : typeof request.suppressSdkConsoleErrors === "boolean" ? request.suppressSdkConsoleErrors : null;
2935
+ if (typeof request.message !== "string" || !isPermissionMode(request.permissionMode) || nonInteractivePermissions === null || suppressSdkConsoleErrors === null || typeof request.waitForCompletion !== "boolean") {
2415
2936
  return null;
2416
2937
  }
2417
2938
  return {
@@ -2419,7 +2940,9 @@ function parseQueueRequest(raw) {
2419
2940
  requestId: request.requestId,
2420
2941
  message: request.message,
2421
2942
  permissionMode: request.permissionMode,
2943
+ nonInteractivePermissions,
2422
2944
  timeoutMs,
2945
+ ...suppressSdkConsoleErrors !== void 0 ? { suppressSdkConsoleErrors } : {},
2423
2946
  waitForCompletion: request.waitForCompletion
2424
2947
  };
2425
2948
  }
@@ -2455,15 +2978,15 @@ function parseQueueRequest(raw) {
2455
2978
  return null;
2456
2979
  }
2457
2980
  function parseSessionSendResult(raw) {
2458
- const result = asRecord2(raw);
2981
+ const result = asRecord3(raw);
2459
2982
  if (!result) {
2460
2983
  return null;
2461
2984
  }
2462
2985
  if (typeof result.stopReason !== "string" || typeof result.sessionId !== "string" || typeof result.resumed !== "boolean") {
2463
2986
  return null;
2464
2987
  }
2465
- const permissionStats = asRecord2(result.permissionStats);
2466
- const record = asRecord2(result.record);
2988
+ const permissionStats = asRecord3(result.permissionStats);
2989
+ const record = asRecord3(result.record);
2467
2990
  if (!permissionStats || !record) {
2468
2991
  return null;
2469
2992
  }
@@ -2478,7 +3001,7 @@ function parseSessionSendResult(raw) {
2478
3001
  return result;
2479
3002
  }
2480
3003
  function parseQueueOwnerMessage(raw) {
2481
- const message = asRecord2(raw);
3004
+ const message = asRecord3(raw);
2482
3005
  if (!message || typeof message.type !== "string") {
2483
3006
  return null;
2484
3007
  }
@@ -2503,7 +3026,7 @@ function parseQueueOwnerMessage(raw) {
2503
3026
  };
2504
3027
  }
2505
3028
  if (message.type === "client_operation") {
2506
- const operation = asRecord2(message.operation);
3029
+ const operation = asRecord3(message.operation);
2507
3030
  if (!operation || typeof operation.method !== "string" || typeof operation.status !== "string" || typeof operation.summary !== "string" || typeof operation.timestamp !== "string") {
2508
3031
  return null;
2509
3032
  }
@@ -2558,7 +3081,7 @@ function parseQueueOwnerMessage(raw) {
2558
3081
  };
2559
3082
  }
2560
3083
  if (message.type === "set_config_option_result") {
2561
- const response = asRecord2(message.response);
3084
+ const response = asRecord3(message.response);
2562
3085
  if (!response || !Array.isArray(response.configOptions)) {
2563
3086
  return null;
2564
3087
  }
@@ -2572,10 +3095,20 @@ function parseQueueOwnerMessage(raw) {
2572
3095
  if (typeof message.message !== "string") {
2573
3096
  return null;
2574
3097
  }
3098
+ const code = isOutputErrorCode2(message.code) ? message.code : void 0;
3099
+ const detailCode = typeof message.detailCode === "string" && message.detailCode.trim().length > 0 ? message.detailCode : void 0;
3100
+ const origin = isOutputErrorOrigin2(message.origin) ? message.origin : void 0;
3101
+ const retryable = typeof message.retryable === "boolean" ? message.retryable : void 0;
3102
+ const acp = parseAcpError(message.acp);
2575
3103
  return {
2576
3104
  type: "error",
2577
3105
  requestId: message.requestId,
2578
- message: message.message
3106
+ code,
3107
+ detailCode,
3108
+ origin,
3109
+ message: message.message,
3110
+ retryable,
3111
+ acp
2579
3112
  };
2580
3113
  }
2581
3114
  return null;
@@ -2589,21 +3122,34 @@ var QUEUE_CONNECT_RETRY_MS = 50;
2589
3122
  function queueBaseDir() {
2590
3123
  return path4.join(os2.homedir(), ".acpx", "queues");
2591
3124
  }
2592
- function formatError(error) {
2593
- if (error instanceof Error) {
2594
- return error.message;
2595
- }
2596
- if (error && typeof error === "object") {
2597
- const maybeMessage = error.message;
2598
- if (typeof maybeMessage === "string" && maybeMessage.length > 0) {
2599
- return maybeMessage;
2600
- }
2601
- try {
2602
- return JSON.stringify(error);
2603
- } catch {
2604
- }
2605
- }
2606
- return String(error);
3125
+ function makeQueueOwnerError(requestId, message, detailCode, options = {}) {
3126
+ return {
3127
+ type: "error",
3128
+ requestId,
3129
+ code: "RUNTIME",
3130
+ detailCode,
3131
+ origin: "queue",
3132
+ retryable: options.retryable,
3133
+ message
3134
+ };
3135
+ }
3136
+ function makeQueueOwnerErrorFromUnknown(requestId, error, detailCode, options = {}) {
3137
+ const normalized = normalizeOutputError(error, {
3138
+ defaultCode: "RUNTIME",
3139
+ origin: "queue",
3140
+ detailCode,
3141
+ retryable: options.retryable
3142
+ });
3143
+ return {
3144
+ type: "error",
3145
+ requestId,
3146
+ code: normalized.code,
3147
+ detailCode: normalized.detailCode,
3148
+ origin: normalized.origin,
3149
+ message: normalized.message,
3150
+ retryable: normalized.retryable,
3151
+ acp: normalized.acp
3152
+ };
2607
3153
  }
2608
3154
  function isProcessAlive(pid) {
2609
3155
  if (!pid || !Number.isInteger(pid) || pid <= 0 || pid === process.pid) {
@@ -2847,11 +3393,16 @@ var SessionQueueOwner = class _SessionQueueOwner {
2847
3393
  }
2848
3394
  for (const task of this.pending.splice(0)) {
2849
3395
  if (task.waitForCompletion) {
2850
- task.send({
2851
- type: "error",
2852
- requestId: task.requestId,
2853
- message: "Queue owner shutting down before prompt execution"
2854
- });
3396
+ task.send(
3397
+ makeQueueOwnerError(
3398
+ task.requestId,
3399
+ "Queue owner shutting down before prompt execution",
3400
+ "QUEUE_OWNER_SHUTTING_DOWN",
3401
+ {
3402
+ retryable: true
3403
+ }
3404
+ )
3405
+ );
2855
3406
  }
2856
3407
  task.close();
2857
3408
  }
@@ -2890,11 +3441,16 @@ var SessionQueueOwner = class _SessionQueueOwner {
2890
3441
  enqueue(task) {
2891
3442
  if (this.closed) {
2892
3443
  if (task.waitForCompletion) {
2893
- task.send({
2894
- type: "error",
2895
- requestId: task.requestId,
2896
- message: "Queue owner is shutting down"
2897
- });
3444
+ task.send(
3445
+ makeQueueOwnerError(
3446
+ task.requestId,
3447
+ "Queue owner is shutting down",
3448
+ "QUEUE_OWNER_SHUTTING_DOWN",
3449
+ {
3450
+ retryable: true
3451
+ }
3452
+ )
3453
+ );
2898
3454
  }
2899
3455
  task.close();
2900
3456
  return;
@@ -2909,22 +3465,24 @@ var SessionQueueOwner = class _SessionQueueOwner {
2909
3465
  handleConnection(socket) {
2910
3466
  socket.setEncoding("utf8");
2911
3467
  if (this.closed) {
2912
- writeQueueMessage(socket, {
2913
- type: "error",
2914
- requestId: "unknown",
2915
- message: "Queue owner is closed"
2916
- });
3468
+ writeQueueMessage(
3469
+ socket,
3470
+ makeQueueOwnerError("unknown", "Queue owner is closed", "QUEUE_OWNER_CLOSED", {
3471
+ retryable: true
3472
+ })
3473
+ );
2917
3474
  socket.end();
2918
3475
  return;
2919
3476
  }
2920
3477
  let buffer = "";
2921
3478
  let handled = false;
2922
- const fail = (requestId, message) => {
2923
- writeQueueMessage(socket, {
2924
- type: "error",
2925
- requestId,
2926
- message
2927
- });
3479
+ const fail = (requestId, message, detailCode) => {
3480
+ writeQueueMessage(
3481
+ socket,
3482
+ makeQueueOwnerError(requestId, message, detailCode, {
3483
+ retryable: false
3484
+ })
3485
+ );
2928
3486
  socket.end();
2929
3487
  };
2930
3488
  const processLine = (line) => {
@@ -2936,12 +3494,16 @@ var SessionQueueOwner = class _SessionQueueOwner {
2936
3494
  try {
2937
3495
  parsed = JSON.parse(line);
2938
3496
  } catch {
2939
- fail("unknown", "Invalid queue request payload");
3497
+ fail(
3498
+ "unknown",
3499
+ "Invalid queue request payload",
3500
+ "QUEUE_REQUEST_PAYLOAD_INVALID_JSON"
3501
+ );
2940
3502
  return;
2941
3503
  }
2942
3504
  const request = parseQueueRequest(parsed);
2943
3505
  if (!request) {
2944
- fail("unknown", "Invalid queue request");
3506
+ fail("unknown", "Invalid queue request", "QUEUE_REQUEST_INVALID");
2945
3507
  return;
2946
3508
  }
2947
3509
  if (request.type === "cancel_prompt") {
@@ -2956,12 +3518,14 @@ var SessionQueueOwner = class _SessionQueueOwner {
2956
3518
  cancelled: cancelled2
2957
3519
  });
2958
3520
  }).catch((error) => {
2959
- const message = formatError(error);
2960
- writeQueueMessage(socket, {
2961
- type: "error",
2962
- requestId: request.requestId,
2963
- message
2964
- });
3521
+ writeQueueMessage(
3522
+ socket,
3523
+ makeQueueOwnerErrorFromUnknown(
3524
+ request.requestId,
3525
+ error,
3526
+ "QUEUE_CONTROL_REQUEST_FAILED"
3527
+ )
3528
+ );
2965
3529
  }).finally(() => {
2966
3530
  if (!socket.destroyed) {
2967
3531
  socket.end();
@@ -2981,12 +3545,14 @@ var SessionQueueOwner = class _SessionQueueOwner {
2981
3545
  modeId: request.modeId
2982
3546
  });
2983
3547
  }).catch((error) => {
2984
- const message = formatError(error);
2985
- writeQueueMessage(socket, {
2986
- type: "error",
2987
- requestId: request.requestId,
2988
- message
2989
- });
3548
+ writeQueueMessage(
3549
+ socket,
3550
+ makeQueueOwnerErrorFromUnknown(
3551
+ request.requestId,
3552
+ error,
3553
+ "QUEUE_CONTROL_REQUEST_FAILED"
3554
+ )
3555
+ );
2990
3556
  }).finally(() => {
2991
3557
  if (!socket.destroyed) {
2992
3558
  socket.end();
@@ -3006,12 +3572,14 @@ var SessionQueueOwner = class _SessionQueueOwner {
3006
3572
  response
3007
3573
  });
3008
3574
  }).catch((error) => {
3009
- const message = formatError(error);
3010
- writeQueueMessage(socket, {
3011
- type: "error",
3012
- requestId: request.requestId,
3013
- message
3014
- });
3575
+ writeQueueMessage(
3576
+ socket,
3577
+ makeQueueOwnerErrorFromUnknown(
3578
+ request.requestId,
3579
+ error,
3580
+ "QUEUE_CONTROL_REQUEST_FAILED"
3581
+ )
3582
+ );
3015
3583
  }).finally(() => {
3016
3584
  if (!socket.destroyed) {
3017
3585
  socket.end();
@@ -3023,7 +3591,9 @@ var SessionQueueOwner = class _SessionQueueOwner {
3023
3591
  requestId: request.requestId,
3024
3592
  message: request.message,
3025
3593
  permissionMode: request.permissionMode,
3594
+ nonInteractivePermissions: request.nonInteractivePermissions,
3026
3595
  timeoutMs: request.timeoutMs,
3596
+ suppressSdkConsoleErrors: request.suppressSdkConsoleErrors,
3027
3597
  waitForCompletion: request.waitForCompletion,
3028
3598
  send: (message) => {
3029
3599
  writeQueueMessage(socket, message);
@@ -3071,9 +3641,16 @@ async function submitToQueueOwner(owner, options) {
3071
3641
  requestId,
3072
3642
  message: options.message,
3073
3643
  permissionMode: options.permissionMode,
3644
+ nonInteractivePermissions: options.nonInteractivePermissions,
3074
3645
  timeoutMs: options.timeoutMs,
3646
+ suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
3075
3647
  waitForCompletion: options.waitForCompletion
3076
3648
  };
3649
+ options.outputFormatter.setContext({
3650
+ sessionId: options.sessionId,
3651
+ requestId,
3652
+ stream: "prompt"
3653
+ });
3077
3654
  return await new Promise((resolve, reject) => {
3078
3655
  let settled = false;
3079
3656
  let acknowledged = false;
@@ -3106,16 +3683,33 @@ async function submitToQueueOwner(owner, options) {
3106
3683
  try {
3107
3684
  parsed = JSON.parse(line);
3108
3685
  } catch {
3109
- finishReject(new QueueProtocolError("Queue owner sent invalid JSON payload"));
3686
+ finishReject(
3687
+ new QueueProtocolError("Queue owner sent invalid JSON payload", {
3688
+ detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
3689
+ origin: "queue",
3690
+ retryable: true
3691
+ })
3692
+ );
3110
3693
  return;
3111
3694
  }
3112
3695
  const message = parseQueueOwnerMessage(parsed);
3113
3696
  if (!message || message.requestId !== requestId) {
3114
- finishReject(new QueueProtocolError("Queue owner sent malformed message"));
3697
+ finishReject(
3698
+ new QueueProtocolError("Queue owner sent malformed message", {
3699
+ detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
3700
+ origin: "queue",
3701
+ retryable: true
3702
+ })
3703
+ );
3115
3704
  return;
3116
3705
  }
3117
3706
  if (message.type === "accepted") {
3118
3707
  acknowledged = true;
3708
+ options.outputFormatter.setContext({
3709
+ sessionId: options.sessionId,
3710
+ requestId: message.requestId,
3711
+ stream: "prompt"
3712
+ });
3119
3713
  if (!options.waitForCompletion) {
3120
3714
  const queued = {
3121
3715
  queued: true,
@@ -3126,9 +3720,41 @@ async function submitToQueueOwner(owner, options) {
3126
3720
  }
3127
3721
  return;
3128
3722
  }
3723
+ if (message.type === "error") {
3724
+ options.outputFormatter.setContext({
3725
+ sessionId: options.sessionId,
3726
+ requestId: message.requestId,
3727
+ stream: "prompt"
3728
+ });
3729
+ options.outputFormatter.onError({
3730
+ code: message.code ?? "RUNTIME",
3731
+ detailCode: message.detailCode,
3732
+ origin: message.origin ?? "queue",
3733
+ message: message.message,
3734
+ retryable: message.retryable,
3735
+ acp: message.acp
3736
+ });
3737
+ options.outputFormatter.flush();
3738
+ const queueErrorAlreadyEmitted = options.errorEmissionPolicy?.queueErrorAlreadyEmitted ?? true;
3739
+ finishReject(
3740
+ new QueueConnectionError(message.message, {
3741
+ outputCode: message.code,
3742
+ detailCode: message.detailCode,
3743
+ origin: message.origin ?? "queue",
3744
+ retryable: message.retryable,
3745
+ acp: message.acp,
3746
+ ...queueErrorAlreadyEmitted ? { outputAlreadyEmitted: true } : {}
3747
+ })
3748
+ );
3749
+ return;
3750
+ }
3129
3751
  if (!acknowledged) {
3130
3752
  finishReject(
3131
- new QueueConnectionError("Queue owner did not acknowledge request")
3753
+ new QueueConnectionError("Queue owner did not acknowledge request", {
3754
+ detailCode: "QUEUE_ACK_MISSING",
3755
+ origin: "queue",
3756
+ retryable: true
3757
+ })
3132
3758
  );
3133
3759
  return;
3134
3760
  }
@@ -3153,11 +3779,13 @@ async function submitToQueueOwner(owner, options) {
3153
3779
  finishResolve(message.result);
3154
3780
  return;
3155
3781
  }
3156
- if (message.type === "error") {
3157
- finishReject(new QueueConnectionError(message.message));
3158
- return;
3159
- }
3160
- finishReject(new QueueProtocolError("Queue owner returned unexpected response"));
3782
+ finishReject(
3783
+ new QueueProtocolError("Queue owner returned unexpected response", {
3784
+ detailCode: "QUEUE_PROTOCOL_UNEXPECTED_RESPONSE",
3785
+ origin: "queue",
3786
+ retryable: true
3787
+ })
3788
+ );
3161
3789
  };
3162
3790
  socket.on("data", (chunk) => {
3163
3791
  buffer += chunk;
@@ -3181,7 +3809,12 @@ async function submitToQueueOwner(owner, options) {
3181
3809
  if (!acknowledged) {
3182
3810
  finishReject(
3183
3811
  new QueueConnectionError(
3184
- "Queue owner disconnected before acknowledging request"
3812
+ "Queue owner disconnected before acknowledging request",
3813
+ {
3814
+ detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
3815
+ origin: "queue",
3816
+ retryable: true
3817
+ }
3185
3818
  )
3186
3819
  );
3187
3820
  return;
@@ -3196,7 +3829,11 @@ async function submitToQueueOwner(owner, options) {
3196
3829
  return;
3197
3830
  }
3198
3831
  finishReject(
3199
- new QueueConnectionError("Queue owner disconnected before prompt completion")
3832
+ new QueueConnectionError("Queue owner disconnected before prompt completion", {
3833
+ detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
3834
+ origin: "queue",
3835
+ retryable: true
3836
+ })
3200
3837
  );
3201
3838
  });
3202
3839
  socket.write(`${JSON.stringify(request)}
@@ -3240,31 +3877,59 @@ async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
3240
3877
  try {
3241
3878
  parsed = JSON.parse(line);
3242
3879
  } catch {
3243
- finishReject(new QueueProtocolError("Queue owner sent invalid JSON payload"));
3880
+ finishReject(
3881
+ new QueueProtocolError("Queue owner sent invalid JSON payload", {
3882
+ detailCode: "QUEUE_PROTOCOL_INVALID_JSON",
3883
+ origin: "queue",
3884
+ retryable: true
3885
+ })
3886
+ );
3244
3887
  return;
3245
3888
  }
3246
3889
  const message = parseQueueOwnerMessage(parsed);
3247
3890
  if (!message || message.requestId !== request.requestId) {
3248
- finishReject(new QueueProtocolError("Queue owner sent malformed message"));
3891
+ finishReject(
3892
+ new QueueProtocolError("Queue owner sent malformed message", {
3893
+ detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
3894
+ origin: "queue",
3895
+ retryable: true
3896
+ })
3897
+ );
3249
3898
  return;
3250
3899
  }
3251
3900
  if (message.type === "accepted") {
3252
3901
  acknowledged = true;
3253
3902
  return;
3254
3903
  }
3255
- if (!acknowledged) {
3904
+ if (message.type === "error") {
3256
3905
  finishReject(
3257
- new QueueConnectionError("Queue owner did not acknowledge request")
3906
+ new QueueConnectionError(message.message, {
3907
+ outputCode: message.code,
3908
+ detailCode: message.detailCode,
3909
+ origin: message.origin ?? "queue",
3910
+ retryable: message.retryable,
3911
+ acp: message.acp
3912
+ })
3258
3913
  );
3259
3914
  return;
3260
3915
  }
3261
- if (message.type === "error") {
3262
- finishReject(new QueueConnectionError(message.message));
3916
+ if (!acknowledged) {
3917
+ finishReject(
3918
+ new QueueConnectionError("Queue owner did not acknowledge request", {
3919
+ detailCode: "QUEUE_ACK_MISSING",
3920
+ origin: "queue",
3921
+ retryable: true
3922
+ })
3923
+ );
3263
3924
  return;
3264
3925
  }
3265
3926
  if (!isExpectedResponse(message)) {
3266
3927
  finishReject(
3267
- new QueueProtocolError("Queue owner returned unexpected response")
3928
+ new QueueProtocolError("Queue owner returned unexpected response", {
3929
+ detailCode: "QUEUE_PROTOCOL_UNEXPECTED_RESPONSE",
3930
+ origin: "queue",
3931
+ retryable: true
3932
+ })
3268
3933
  );
3269
3934
  return;
3270
3935
  }
@@ -3292,13 +3957,22 @@ async function submitControlToQueueOwner(owner, request, isExpectedResponse) {
3292
3957
  if (!acknowledged) {
3293
3958
  finishReject(
3294
3959
  new QueueConnectionError(
3295
- "Queue owner disconnected before acknowledging request"
3960
+ "Queue owner disconnected before acknowledging request",
3961
+ {
3962
+ detailCode: "QUEUE_DISCONNECTED_BEFORE_ACK",
3963
+ origin: "queue",
3964
+ retryable: true
3965
+ }
3296
3966
  )
3297
3967
  );
3298
3968
  return;
3299
3969
  }
3300
3970
  finishReject(
3301
- new QueueConnectionError("Queue owner disconnected before responding")
3971
+ new QueueConnectionError("Queue owner disconnected before responding", {
3972
+ detailCode: "QUEUE_DISCONNECTED_BEFORE_COMPLETION",
3973
+ origin: "queue",
3974
+ retryable: true
3975
+ })
3302
3976
  );
3303
3977
  });
3304
3978
  socket.write(`${JSON.stringify(request)}
@@ -3319,7 +3993,11 @@ async function submitCancelToQueueOwner(owner) {
3319
3993
  return void 0;
3320
3994
  }
3321
3995
  if (response.requestId !== request.requestId) {
3322
- throw new QueueProtocolError("Queue owner returned mismatched cancel response");
3996
+ throw new QueueProtocolError("Queue owner returned mismatched cancel response", {
3997
+ detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
3998
+ origin: "queue",
3999
+ retryable: true
4000
+ });
3323
4001
  }
3324
4002
  return response.cancelled;
3325
4003
  }
@@ -3339,7 +4017,11 @@ async function submitSetModeToQueueOwner(owner, modeId, timeoutMs) {
3339
4017
  return void 0;
3340
4018
  }
3341
4019
  if (response.requestId !== request.requestId) {
3342
- throw new QueueProtocolError("Queue owner returned mismatched set_mode response");
4020
+ throw new QueueProtocolError("Queue owner returned mismatched set_mode response", {
4021
+ detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
4022
+ origin: "queue",
4023
+ retryable: true
4024
+ });
3343
4025
  }
3344
4026
  return true;
3345
4027
  }
@@ -3361,7 +4043,12 @@ async function submitSetConfigOptionToQueueOwner(owner, configId, value, timeout
3361
4043
  }
3362
4044
  if (response.requestId !== request.requestId) {
3363
4045
  throw new QueueProtocolError(
3364
- "Queue owner returned mismatched set_config_option response"
4046
+ "Queue owner returned mismatched set_config_option response",
4047
+ {
4048
+ detailCode: "QUEUE_PROTOCOL_MALFORMED_MESSAGE",
4049
+ origin: "queue",
4050
+ retryable: true
4051
+ }
3365
4052
  );
3366
4053
  }
3367
4054
  return response.response;
@@ -3390,7 +4077,12 @@ async function trySubmitToRunningOwner(options) {
3390
4077
  return void 0;
3391
4078
  }
3392
4079
  throw new QueueConnectionError(
3393
- "Session queue owner is running but not accepting queue requests"
4080
+ "Session queue owner is running but not accepting queue requests",
4081
+ {
4082
+ detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
4083
+ origin: "queue",
4084
+ retryable: true
4085
+ }
3394
4086
  );
3395
4087
  }
3396
4088
  async function tryCancelOnRunningOwner(options) {
@@ -3417,7 +4109,12 @@ async function tryCancelOnRunningOwner(options) {
3417
4109
  return void 0;
3418
4110
  }
3419
4111
  throw new QueueConnectionError(
3420
- "Session queue owner is running but not accepting cancel requests"
4112
+ "Session queue owner is running but not accepting cancel requests",
4113
+ {
4114
+ detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
4115
+ origin: "queue",
4116
+ retryable: true
4117
+ }
3421
4118
  );
3422
4119
  }
3423
4120
  async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose) {
@@ -3444,7 +4141,12 @@ async function trySetModeOnRunningOwner(sessionId, modeId, timeoutMs, verbose) {
3444
4141
  return void 0;
3445
4142
  }
3446
4143
  throw new QueueConnectionError(
3447
- "Session queue owner is running but not accepting set_mode requests"
4144
+ "Session queue owner is running but not accepting set_mode requests",
4145
+ {
4146
+ detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
4147
+ origin: "queue",
4148
+ retryable: true
4149
+ }
3448
4150
  );
3449
4151
  }
3450
4152
  async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, timeoutMs, verbose) {
@@ -3476,7 +4178,12 @@ async function trySetConfigOptionOnRunningOwner(sessionId, configId, value, time
3476
4178
  return void 0;
3477
4179
  }
3478
4180
  throw new QueueConnectionError(
3479
- "Session queue owner is running but not accepting set_config_option requests"
4181
+ "Session queue owner is running but not accepting set_config_option requests",
4182
+ {
4183
+ detailCode: "QUEUE_NOT_ACCEPTING_REQUESTS",
4184
+ origin: "queue",
4185
+ retryable: true
4186
+ }
3480
4187
  );
3481
4188
  }
3482
4189
  async function terminateQueueOwnerForSession(sessionId) {
@@ -3537,6 +4244,7 @@ function parseSessionRecord(raw) {
3537
4244
  return null;
3538
4245
  }
3539
4246
  const record = raw;
4247
+ const runtimeSessionId = normalizeRuntimeSessionId(record.runtimeSessionId);
3540
4248
  const name = record.name == null ? void 0 : typeof record.name === "string" && record.name.trim().length > 0 ? record.name.trim() : null;
3541
4249
  const pid = record.pid == null ? void 0 : Number.isInteger(record.pid) && record.pid > 0 ? record.pid : null;
3542
4250
  const closed = record.closed == null ? false : typeof record.closed === "boolean" ? record.closed : null;
@@ -3559,6 +4267,7 @@ function parseSessionRecord(raw) {
3559
4267
  ...record,
3560
4268
  id: record.id,
3561
4269
  sessionId: record.sessionId,
4270
+ runtimeSessionId,
3562
4271
  agentCommand: record.agentCommand,
3563
4272
  cwd: record.cwd,
3564
4273
  name,
@@ -3815,6 +4524,8 @@ var QueueTaskOutputFormatter = class {
3815
4524
  this.requestId = task.requestId;
3816
4525
  this.send = task.send;
3817
4526
  }
4527
+ setContext() {
4528
+ }
3818
4529
  onSessionUpdate(notification) {
3819
4530
  this.send({
3820
4531
  type: "session_update",
@@ -3836,16 +4547,32 @@ var QueueTaskOutputFormatter = class {
3836
4547
  stopReason
3837
4548
  });
3838
4549
  }
4550
+ onError(params) {
4551
+ this.send({
4552
+ type: "error",
4553
+ requestId: this.requestId,
4554
+ code: params.code,
4555
+ detailCode: params.detailCode,
4556
+ origin: params.origin,
4557
+ message: params.message,
4558
+ retryable: params.retryable,
4559
+ acp: params.acp
4560
+ });
4561
+ }
3839
4562
  flush() {
3840
4563
  }
3841
4564
  };
3842
4565
  var DISCARD_OUTPUT_FORMATTER = {
4566
+ setContext() {
4567
+ },
3843
4568
  onSessionUpdate() {
3844
4569
  },
3845
4570
  onClientOperation() {
3846
4571
  },
3847
4572
  onDone() {
3848
4573
  },
4574
+ onError() {
4575
+ },
3849
4576
  flush() {
3850
4577
  }
3851
4578
  };
@@ -3858,22 +4585,6 @@ function normalizeQueueOwnerTtlMs(ttlMs) {
3858
4585
  }
3859
4586
  return Math.round(ttlMs);
3860
4587
  }
3861
- function formatError2(error) {
3862
- if (error instanceof Error) {
3863
- return error.message;
3864
- }
3865
- if (error && typeof error === "object") {
3866
- const maybeMessage = error.message;
3867
- if (typeof maybeMessage === "string" && maybeMessage.length > 0) {
3868
- return maybeMessage;
3869
- }
3870
- try {
3871
- return JSON.stringify(error);
3872
- } catch {
3873
- }
3874
- }
3875
- return String(error);
3876
- }
3877
4588
  function collapseWhitespace2(value) {
3878
4589
  return value.replace(/\s+/g, " ").trim();
3879
4590
  }
@@ -3949,16 +4660,18 @@ function applyLifecycleSnapshotToRecord(record, snapshot) {
3949
4660
  record.lastAgentExitAt = void 0;
3950
4661
  record.lastAgentDisconnectReason = void 0;
3951
4662
  }
4663
+ function reconcileRuntimeSessionId(record, runtimeSessionId) {
4664
+ const normalized = normalizeRuntimeSessionId(runtimeSessionId);
4665
+ if (!normalized) {
4666
+ return;
4667
+ }
4668
+ record.runtimeSessionId = normalized;
4669
+ }
3952
4670
  function shouldFallbackToNewSession(error) {
3953
4671
  if (error instanceof TimeoutError || error instanceof InterruptedError) {
3954
4672
  return false;
3955
4673
  }
3956
- const message = formatError2(error).toLowerCase();
3957
- if (message.includes("resource_not_found") || message.includes("resource not found") || message.includes("session not found") || message.includes("unknown session") || message.includes("invalid session")) {
3958
- return true;
3959
- }
3960
- const code = error && typeof error === "object" && "code" in error ? error.code : void 0;
3961
- return code === -32001 || code === -32002;
4674
+ return isAcpResourceNotFoundError(error);
3962
4675
  }
3963
4676
  async function connectAndLoadSession(options) {
3964
4677
  const record = options.record;
@@ -3990,31 +4703,40 @@ async function connectAndLoadSession(options) {
3990
4703
  let sessionId = record.sessionId;
3991
4704
  if (client.supportsLoadSession()) {
3992
4705
  try {
3993
- await withTimeout(
4706
+ const loadResult = await withTimeout(
3994
4707
  client.loadSessionWithOptions(record.sessionId, record.cwd, {
3995
4708
  suppressReplayUpdates: true
3996
4709
  }),
3997
4710
  options.timeoutMs
3998
4711
  );
4712
+ reconcileRuntimeSessionId(record, loadResult.runtimeSessionId);
3999
4713
  resumed = true;
4000
4714
  } catch (error) {
4001
- loadError = formatError2(error);
4715
+ loadError = formatErrorMessage(error);
4002
4716
  if (!shouldFallbackToNewSession(error)) {
4003
4717
  throw error;
4004
4718
  }
4005
- sessionId = await withTimeout(
4719
+ const createdSession = await withTimeout(
4006
4720
  client.createSession(record.cwd),
4007
4721
  options.timeoutMs
4008
4722
  );
4723
+ sessionId = createdSession.sessionId;
4009
4724
  record.sessionId = sessionId;
4725
+ reconcileRuntimeSessionId(record, createdSession.runtimeSessionId);
4010
4726
  }
4011
4727
  } else {
4012
- sessionId = await withTimeout(client.createSession(record.cwd), options.timeoutMs);
4728
+ const createdSession = await withTimeout(
4729
+ client.createSession(record.cwd),
4730
+ options.timeoutMs
4731
+ );
4732
+ sessionId = createdSession.sessionId;
4013
4733
  record.sessionId = sessionId;
4734
+ reconcileRuntimeSessionId(record, createdSession.runtimeSessionId);
4014
4735
  }
4015
4736
  options.onSessionIdResolved?.(sessionId);
4016
4737
  return {
4017
4738
  sessionId,
4739
+ runtimeSessionId: record.runtimeSessionId,
4018
4740
  resumed,
4019
4741
  loadError
4020
4742
  };
@@ -4026,10 +4748,12 @@ async function runQueuedTask(sessionRecordId, task, options) {
4026
4748
  sessionRecordId,
4027
4749
  message: task.message,
4028
4750
  permissionMode: task.permissionMode,
4751
+ nonInteractivePermissions: task.nonInteractivePermissions ?? options.nonInteractivePermissions,
4029
4752
  authCredentials: options.authCredentials,
4030
4753
  authPolicy: options.authPolicy,
4031
4754
  outputFormatter,
4032
4755
  timeoutMs: task.timeoutMs,
4756
+ suppressSdkConsoleErrors: task.suppressSdkConsoleErrors ?? options.suppressSdkConsoleErrors,
4033
4757
  verbose: options.verbose,
4034
4758
  onClientAvailable: options.onClientAvailable,
4035
4759
  onClientClosed: options.onClientClosed,
@@ -4043,12 +4767,20 @@ async function runQueuedTask(sessionRecordId, task, options) {
4043
4767
  });
4044
4768
  }
4045
4769
  } catch (error) {
4046
- const message = formatError2(error);
4770
+ const normalizedError = normalizeOutputError(error, {
4771
+ origin: "runtime",
4772
+ detailCode: "QUEUE_RUNTIME_PROMPT_FAILED"
4773
+ });
4047
4774
  if (task.waitForCompletion) {
4048
4775
  task.send({
4049
4776
  type: "error",
4050
4777
  requestId: task.requestId,
4051
- message
4778
+ code: normalizedError.code,
4779
+ detailCode: normalizedError.detailCode,
4780
+ origin: normalizedError.origin,
4781
+ message: normalizedError.message,
4782
+ retryable: normalizedError.retryable,
4783
+ acp: normalizedError.acp
4052
4784
  });
4053
4785
  }
4054
4786
  if (error instanceof InterruptedError) {
@@ -4061,13 +4793,19 @@ async function runQueuedTask(sessionRecordId, task, options) {
4061
4793
  async function runSessionPrompt(options) {
4062
4794
  const output = options.outputFormatter;
4063
4795
  const record = await resolveSessionRecord(options.sessionRecordId);
4796
+ output.setContext({
4797
+ sessionId: record.id,
4798
+ stream: "prompt"
4799
+ });
4064
4800
  const assistantSnippets = [];
4065
4801
  const client = new AcpClient({
4066
4802
  agentCommand: record.agentCommand,
4067
4803
  cwd: absolutePath(record.cwd),
4068
4804
  permissionMode: options.permissionMode,
4805
+ nonInteractivePermissions: options.nonInteractivePermissions,
4069
4806
  authCredentials: options.authCredentials,
4070
4807
  authPolicy: options.authPolicy,
4808
+ suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
4071
4809
  verbose: options.verbose,
4072
4810
  onSessionUpdate: (notification) => {
4073
4811
  output.onSessionUpdate(notification);
@@ -4129,7 +4867,7 @@ async function runSessionPrompt(options) {
4129
4867
  } catch (error) {
4130
4868
  if (options.verbose) {
4131
4869
  process.stderr.write(
4132
- `[acpx] onPromptActive hook failed: ${formatError2(error)}
4870
+ `[acpx] onPromptActive hook failed: ${formatErrorMessage(error)}
4133
4871
  `
4134
4872
  );
4135
4873
  }
@@ -4209,6 +4947,7 @@ async function withConnectedSession(options) {
4209
4947
  agentCommand: record.agentCommand,
4210
4948
  cwd: absolutePath(record.cwd),
4211
4949
  permissionMode: options.permissionMode ?? "approve-reads",
4950
+ nonInteractivePermissions: options.nonInteractivePermissions,
4212
4951
  authCredentials: options.authCredentials,
4213
4952
  authPolicy: options.authPolicy,
4214
4953
  verbose: options.verbose
@@ -4288,6 +5027,7 @@ async function withConnectedSession(options) {
4288
5027
  async function runSessionSetModeDirect(options) {
4289
5028
  const result = await withConnectedSession({
4290
5029
  sessionRecordId: options.sessionRecordId,
5030
+ nonInteractivePermissions: options.nonInteractivePermissions,
4291
5031
  authCredentials: options.authCredentials,
4292
5032
  authPolicy: options.authPolicy,
4293
5033
  timeoutMs: options.timeoutMs,
@@ -4310,6 +5050,7 @@ async function runSessionSetModeDirect(options) {
4310
5050
  async function runSessionSetConfigOptionDirect(options) {
4311
5051
  const result = await withConnectedSession({
4312
5052
  sessionRecordId: options.sessionRecordId,
5053
+ nonInteractivePermissions: options.nonInteractivePermissions,
4313
5054
  authCredentials: options.authCredentials,
4314
5055
  authPolicy: options.authPolicy,
4315
5056
  timeoutMs: options.timeoutMs,
@@ -4336,8 +5077,10 @@ async function runOnce(options) {
4336
5077
  agentCommand: options.agentCommand,
4337
5078
  cwd: absolutePath(options.cwd),
4338
5079
  permissionMode: options.permissionMode,
5080
+ nonInteractivePermissions: options.nonInteractivePermissions,
4339
5081
  authCredentials: options.authCredentials,
4340
5082
  authPolicy: options.authPolicy,
5083
+ suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
4341
5084
  verbose: options.verbose,
4342
5085
  onSessionUpdate: (notification) => output.onSessionUpdate(notification),
4343
5086
  onClientOperation: (operation) => output.onClientOperation(operation)
@@ -4346,10 +5089,15 @@ async function runOnce(options) {
4346
5089
  return await withInterrupt(
4347
5090
  async () => {
4348
5091
  await withTimeout(client.start(), options.timeoutMs);
4349
- const sessionId = await withTimeout(
5092
+ const createdSession = await withTimeout(
4350
5093
  client.createSession(absolutePath(options.cwd)),
4351
5094
  options.timeoutMs
4352
5095
  );
5096
+ const sessionId = createdSession.sessionId;
5097
+ output.setContext({
5098
+ sessionId,
5099
+ stream: "prompt"
5100
+ });
4353
5101
  const response = await withTimeout(
4354
5102
  client.prompt(sessionId, options.message),
4355
5103
  options.timeoutMs
@@ -4372,6 +5120,7 @@ async function createSession(options) {
4372
5120
  agentCommand: options.agentCommand,
4373
5121
  cwd: absolutePath(options.cwd),
4374
5122
  permissionMode: options.permissionMode,
5123
+ nonInteractivePermissions: options.nonInteractivePermissions,
4375
5124
  authCredentials: options.authCredentials,
4376
5125
  authPolicy: options.authPolicy,
4377
5126
  verbose: options.verbose
@@ -4380,15 +5129,17 @@ async function createSession(options) {
4380
5129
  return await withInterrupt(
4381
5130
  async () => {
4382
5131
  await withTimeout(client.start(), options.timeoutMs);
4383
- const sessionId = await withTimeout(
5132
+ const createdSession = await withTimeout(
4384
5133
  client.createSession(absolutePath(options.cwd)),
4385
5134
  options.timeoutMs
4386
5135
  );
5136
+ const sessionId = createdSession.sessionId;
4387
5137
  const lifecycle = client.getAgentLifecycleSnapshot();
4388
5138
  const now = isoNow2();
4389
5139
  const record = {
4390
5140
  id: sessionId,
4391
5141
  sessionId,
5142
+ runtimeSessionId: createdSession.runtimeSessionId,
4392
5143
  agentCommand: options.agentCommand,
4393
5144
  cwd: absolutePath(options.cwd),
4394
5145
  name: normalizeName(options.name),
@@ -4413,6 +5164,38 @@ async function createSession(options) {
4413
5164
  await client.close();
4414
5165
  }
4415
5166
  }
5167
+ async function ensureSession(options) {
5168
+ const cwd = absolutePath(options.cwd);
5169
+ const gitRoot = findGitRepositoryRoot(cwd);
5170
+ const walkBoundary = options.walkBoundary ?? gitRoot ?? cwd;
5171
+ const existing = await findSessionByDirectoryWalk({
5172
+ agentCommand: options.agentCommand,
5173
+ cwd,
5174
+ name: options.name,
5175
+ boundary: walkBoundary
5176
+ });
5177
+ if (existing) {
5178
+ return {
5179
+ record: existing,
5180
+ created: false
5181
+ };
5182
+ }
5183
+ const record = await createSession({
5184
+ agentCommand: options.agentCommand,
5185
+ cwd,
5186
+ name: options.name,
5187
+ permissionMode: options.permissionMode,
5188
+ nonInteractivePermissions: options.nonInteractivePermissions,
5189
+ authCredentials: options.authCredentials,
5190
+ authPolicy: options.authPolicy,
5191
+ timeoutMs: options.timeoutMs,
5192
+ verbose: options.verbose
5193
+ });
5194
+ return {
5195
+ record,
5196
+ created: true
5197
+ };
5198
+ }
4416
5199
  async function sendSession(options) {
4417
5200
  const waitForCompletion = options.waitForCompletion !== false;
4418
5201
  const queueOwnerTtlMs = normalizeQueueOwnerTtlMs(options.ttlMs);
@@ -4420,8 +5203,11 @@ async function sendSession(options) {
4420
5203
  sessionId: options.sessionId,
4421
5204
  message: options.message,
4422
5205
  permissionMode: options.permissionMode,
5206
+ nonInteractivePermissions: options.nonInteractivePermissions,
4423
5207
  outputFormatter: options.outputFormatter,
5208
+ errorEmissionPolicy: options.errorEmissionPolicy,
4424
5209
  timeoutMs: options.timeoutMs,
5210
+ suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
4425
5211
  waitForCompletion,
4426
5212
  verbose: options.verbose
4427
5213
  });
@@ -4435,8 +5221,11 @@ async function sendSession(options) {
4435
5221
  sessionId: options.sessionId,
4436
5222
  message: options.message,
4437
5223
  permissionMode: options.permissionMode,
5224
+ nonInteractivePermissions: options.nonInteractivePermissions,
4438
5225
  outputFormatter: options.outputFormatter,
5226
+ errorEmissionPolicy: options.errorEmissionPolicy,
4439
5227
  timeoutMs: options.timeoutMs,
5228
+ suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
4440
5229
  waitForCompletion,
4441
5230
  verbose: options.verbose
4442
5231
  });
@@ -4453,6 +5242,7 @@ async function sendSession(options) {
4453
5242
  await runSessionSetModeDirect({
4454
5243
  sessionRecordId: options.sessionId,
4455
5244
  modeId,
5245
+ nonInteractivePermissions: options.nonInteractivePermissions,
4456
5246
  authCredentials: options.authCredentials,
4457
5247
  authPolicy: options.authPolicy,
4458
5248
  timeoutMs,
@@ -4464,6 +5254,7 @@ async function sendSession(options) {
4464
5254
  sessionRecordId: options.sessionId,
4465
5255
  configId,
4466
5256
  value,
5257
+ nonInteractivePermissions: options.nonInteractivePermissions,
4467
5258
  authCredentials: options.authCredentials,
4468
5259
  authPolicy: options.authPolicy,
4469
5260
  timeoutMs,
@@ -4479,7 +5270,7 @@ async function sendSession(options) {
4479
5270
  void applyPendingCancel().catch((error) => {
4480
5271
  if (options.verbose) {
4481
5272
  process.stderr.write(
4482
- `[acpx] failed to apply deferred cancel: ${formatError2(error)}
5273
+ `[acpx] failed to apply deferred cancel: ${formatErrorMessage(error)}
4483
5274
  `
4484
5275
  );
4485
5276
  }
@@ -4526,10 +5317,12 @@ async function sendSession(options) {
4526
5317
  sessionRecordId: options.sessionId,
4527
5318
  message: options.message,
4528
5319
  permissionMode: options.permissionMode,
5320
+ nonInteractivePermissions: options.nonInteractivePermissions,
4529
5321
  authCredentials: options.authCredentials,
4530
5322
  authPolicy: options.authPolicy,
4531
5323
  outputFormatter: options.outputFormatter,
4532
5324
  timeoutMs: options.timeoutMs,
5325
+ suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
4533
5326
  verbose: options.verbose,
4534
5327
  onClientAvailable: setActiveController,
4535
5328
  onClientClosed: clearActiveController,
@@ -4554,8 +5347,10 @@ async function sendSession(options) {
4554
5347
  await runPromptTurn(async () => {
4555
5348
  await runQueuedTask(options.sessionId, task, {
4556
5349
  verbose: options.verbose,
5350
+ nonInteractivePermissions: options.nonInteractivePermissions,
4557
5351
  authCredentials: options.authCredentials,
4558
5352
  authPolicy: options.authPolicy,
5353
+ suppressSdkConsoleErrors: options.suppressSdkConsoleErrors,
4559
5354
  onClientAvailable: setActiveController,
4560
5355
  onClientClosed: clearActiveController,
4561
5356
  onPromptActive: async () => {
@@ -4598,6 +5393,7 @@ async function setSessionMode(options) {
4598
5393
  return await runSessionSetModeDirect({
4599
5394
  sessionRecordId: options.sessionId,
4600
5395
  modeId: options.modeId,
5396
+ nonInteractivePermissions: options.nonInteractivePermissions,
4601
5397
  authCredentials: options.authCredentials,
4602
5398
  authPolicy: options.authPolicy,
4603
5399
  timeoutMs: options.timeoutMs,
@@ -4623,6 +5419,7 @@ async function setSessionConfigOption(options) {
4623
5419
  sessionRecordId: options.sessionId,
4624
5420
  configId: options.configId,
4625
5421
  value: options.value,
5422
+ nonInteractivePermissions: options.nonInteractivePermissions,
4626
5423
  authCredentials: options.authCredentials,
4627
5424
  authPolicy: options.authPolicy,
4628
5425
  timeoutMs: options.timeoutMs,
@@ -4669,19 +5466,6 @@ async function closeSession(sessionId) {
4669
5466
  return record;
4670
5467
  }
4671
5468
 
4672
- // src/types.ts
4673
- var EXIT_CODES = {
4674
- SUCCESS: 0,
4675
- ERROR: 1,
4676
- USAGE: 2,
4677
- TIMEOUT: 3,
4678
- NO_SESSION: 4,
4679
- PERMISSION_DENIED: 5,
4680
- INTERRUPTED: 130
4681
- };
4682
- var OUTPUT_FORMATS = ["text", "json", "quiet"];
4683
- var AUTH_POLICIES = ["skip", "fail"];
4684
-
4685
5469
  // src/cli.ts
4686
5470
  var NoSessionError = class extends Error {
4687
5471
  constructor(message) {
@@ -4716,6 +5500,16 @@ function parseAuthPolicy2(value) {
4716
5500
  }
4717
5501
  return value;
4718
5502
  }
5503
+ function parseNonInteractivePermissionPolicy2(value) {
5504
+ if (!NON_INTERACTIVE_PERMISSION_POLICIES.includes(
5505
+ value
5506
+ )) {
5507
+ throw new InvalidArgumentError(
5508
+ `Invalid non-interactive permission policy "${value}". Expected one of: ${NON_INTERACTIVE_PERMISSION_POLICIES.join(", ")}`
5509
+ );
5510
+ }
5511
+ return value;
5512
+ }
4719
5513
  function parseTimeoutSeconds(value) {
4720
5514
  const parsed = Number(value);
4721
5515
  if (!Number.isFinite(parsed) || parsed <= 0) {
@@ -4817,7 +5611,14 @@ function addGlobalFlags(command) {
4817
5611
  ).option("--approve-all", "Auto-approve all permission requests").option(
4818
5612
  "--approve-reads",
4819
5613
  "Auto-approve read/search requests and prompt for writes"
4820
- ).option("--deny-all", "Deny all permission requests").option("--format <fmt>", "Output format: text, json, quiet", parseOutputFormat2).option(
5614
+ ).option("--deny-all", "Deny all permission requests").option(
5615
+ "--non-interactive-permissions <policy>",
5616
+ "When prompting is unavailable: deny or fail",
5617
+ parseNonInteractivePermissionPolicy2
5618
+ ).option("--format <fmt>", "Output format: text, json, quiet", parseOutputFormat2).option(
5619
+ "--json-strict",
5620
+ "Strict JSON mode: requires --format json and suppresses non-JSON stderr output"
5621
+ ).option(
4821
5622
  "--timeout <seconds>",
4822
5623
  "Maximum time to wait for agent response",
4823
5624
  parseTimeoutSeconds
@@ -4866,19 +5667,39 @@ function addPromptInputOption(command) {
4866
5667
  }
4867
5668
  function resolveGlobalFlags(command, config) {
4868
5669
  const opts = command.optsWithGlobals();
5670
+ const format = opts.format ?? config.format ?? "text";
5671
+ const jsonStrict = opts.jsonStrict === true;
5672
+ const verbose = opts.verbose === true;
5673
+ if (jsonStrict && format !== "json") {
5674
+ throw new InvalidArgumentError("--json-strict requires --format json");
5675
+ }
5676
+ if (jsonStrict && verbose) {
5677
+ throw new InvalidArgumentError("--json-strict cannot be combined with --verbose");
5678
+ }
4869
5679
  return {
4870
5680
  agent: opts.agent,
4871
5681
  cwd: opts.cwd ?? process.cwd(),
4872
5682
  authPolicy: opts.authPolicy ?? config.authPolicy,
5683
+ nonInteractivePermissions: opts.nonInteractivePermissions ?? config.nonInteractivePermissions,
5684
+ jsonStrict,
4873
5685
  timeout: opts.timeout ?? config.timeoutMs,
4874
5686
  ttl: opts.ttl ?? config.ttlMs ?? DEFAULT_QUEUE_OWNER_TTL_MS,
4875
- verbose: opts.verbose === true,
4876
- format: opts.format ?? config.format ?? "text",
5687
+ verbose,
5688
+ format,
4877
5689
  approveAll: opts.approveAll ? true : void 0,
4878
5690
  approveReads: opts.approveReads ? true : void 0,
4879
5691
  denyAll: opts.denyAll ? true : void 0
4880
5692
  };
4881
5693
  }
5694
+ function resolveOutputPolicy(format, jsonStrict) {
5695
+ return {
5696
+ format,
5697
+ jsonStrict,
5698
+ suppressNonJsonStderr: jsonStrict,
5699
+ queueErrorAlreadyEmitted: format !== "quiet",
5700
+ suppressSdkConsoleErrors: jsonStrict
5701
+ };
5702
+ }
4882
5703
  function resolveAgentInvocation(explicitAgentName, globalFlags, config) {
4883
5704
  const override = globalFlags.agent?.trim();
4884
5705
  if (override && explicitAgentName) {
@@ -4939,6 +5760,13 @@ function printClosedSessionByFormat(record, format) {
4939
5760
  process.stdout.write(`${record.id}
4940
5761
  `);
4941
5762
  }
5763
+ function runtimeSessionIdPayload(runtimeSessionId) {
5764
+ const normalized = normalizeRuntimeSessionId(runtimeSessionId);
5765
+ if (!normalized) {
5766
+ return {};
5767
+ }
5768
+ return { runtimeSessionId: normalized };
5769
+ }
4942
5770
  function printNewSessionByFormat(record, replaced, format) {
4943
5771
  if (format === "json") {
4944
5772
  process.stdout.write(
@@ -4947,7 +5775,8 @@ function printNewSessionByFormat(record, replaced, format) {
4947
5775
  id: record.id,
4948
5776
  sessionId: record.sessionId,
4949
5777
  name: record.name,
4950
- replacedSessionId: replaced?.id
5778
+ replacedSessionId: replaced?.id,
5779
+ ...runtimeSessionIdPayload(record.runtimeSessionId)
4951
5780
  })}
4952
5781
  `
4953
5782
  );
@@ -4966,6 +5795,30 @@ function printNewSessionByFormat(record, replaced, format) {
4966
5795
  process.stdout.write(`${record.id}
4967
5796
  `);
4968
5797
  }
5798
+ function printEnsuredSessionByFormat(record, created, format) {
5799
+ if (format === "json") {
5800
+ process.stdout.write(
5801
+ `${JSON.stringify({
5802
+ type: "session_ensured",
5803
+ id: record.id,
5804
+ sessionId: record.sessionId,
5805
+ name: record.name,
5806
+ created,
5807
+ ...runtimeSessionIdPayload(record.runtimeSessionId)
5808
+ })}
5809
+ `
5810
+ );
5811
+ return;
5812
+ }
5813
+ if (format === "quiet") {
5814
+ process.stdout.write(`${record.id}
5815
+ `);
5816
+ return;
5817
+ }
5818
+ const action = created ? "created" : "existing";
5819
+ process.stdout.write(`${record.id} (${action})
5820
+ `);
5821
+ }
4969
5822
  function printQueuedPromptByFormat(result, format) {
4970
5823
  if (format === "json") {
4971
5824
  process.stdout.write(
@@ -5008,15 +5861,15 @@ function formatPromptSessionBannerLine(record, currentCwd) {
5008
5861
  }
5009
5862
  return `[acpx] session ${label} (${record.id}) \xB7 ${normalizedSessionCwd} \xB7 agent ${status}`;
5010
5863
  }
5011
- function printPromptSessionBanner(record, currentCwd, format) {
5012
- if (format === "quiet") {
5864
+ function printPromptSessionBanner(record, currentCwd, format, jsonStrict = false) {
5865
+ if (format === "quiet" || jsonStrict && format === "json") {
5013
5866
  return;
5014
5867
  }
5015
5868
  process.stderr.write(`${formatPromptSessionBannerLine(record, currentCwd)}
5016
5869
  `);
5017
5870
  }
5018
- function printCreatedSessionBanner(record, agentName, format) {
5019
- if (format === "quiet") {
5871
+ function printCreatedSessionBanner(record, agentName, format, jsonStrict = false) {
5872
+ if (format === "quiet" || jsonStrict && format === "json") {
5020
5873
  return;
5021
5874
  }
5022
5875
  const label = formatSessionLabel(record);
@@ -5047,9 +5900,12 @@ Create one: ${createCmd}`
5047
5900
  }
5048
5901
  async function handlePrompt(explicitAgentName, promptParts, flags, command, config) {
5049
5902
  const globalFlags = resolveGlobalFlags(command, config);
5903
+ const outputPolicy = resolveOutputPolicy(
5904
+ globalFlags.format,
5905
+ globalFlags.jsonStrict === true
5906
+ );
5050
5907
  const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
5051
5908
  const prompt = await readPrompt(promptParts, flags.file, globalFlags.cwd);
5052
- const outputFormatter = createOutputFormatter(globalFlags.format);
5053
5909
  const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
5054
5910
  const record = await findRoutedSessionOrThrow(
5055
5911
  agent.agentCommand,
@@ -5057,21 +5913,37 @@ async function handlePrompt(explicitAgentName, promptParts, flags, command, conf
5057
5913
  agent.cwd,
5058
5914
  flags.session
5059
5915
  );
5060
- printPromptSessionBanner(record, agent.cwd, globalFlags.format);
5916
+ const outputFormatter = createOutputFormatter(outputPolicy.format, {
5917
+ jsonContext: {
5918
+ sessionId: record.id,
5919
+ stream: "prompt"
5920
+ }
5921
+ });
5922
+ printPromptSessionBanner(
5923
+ record,
5924
+ agent.cwd,
5925
+ outputPolicy.format,
5926
+ outputPolicy.jsonStrict
5927
+ );
5061
5928
  const result = await sendSession({
5062
5929
  sessionId: record.id,
5063
5930
  message: prompt,
5064
5931
  permissionMode,
5932
+ nonInteractivePermissions: globalFlags.nonInteractivePermissions,
5065
5933
  authCredentials: config.auth,
5066
5934
  authPolicy: globalFlags.authPolicy,
5067
5935
  outputFormatter,
5936
+ errorEmissionPolicy: {
5937
+ queueErrorAlreadyEmitted: outputPolicy.queueErrorAlreadyEmitted
5938
+ },
5939
+ suppressSdkConsoleErrors: outputPolicy.suppressSdkConsoleErrors,
5068
5940
  timeoutMs: globalFlags.timeout,
5069
5941
  ttlMs: globalFlags.ttl,
5070
5942
  verbose: globalFlags.verbose,
5071
5943
  waitForCompletion: flags.wait !== false
5072
5944
  });
5073
5945
  if ("queued" in result) {
5074
- printQueuedPromptByFormat(result, globalFlags.format);
5946
+ printQueuedPromptByFormat(result, outputPolicy.format);
5075
5947
  return;
5076
5948
  }
5077
5949
  applyPermissionExitCode(result);
@@ -5084,18 +5956,24 @@ async function handlePrompt(explicitAgentName, promptParts, flags, command, conf
5084
5956
  }
5085
5957
  async function handleExec(explicitAgentName, promptParts, flags, command, config) {
5086
5958
  const globalFlags = resolveGlobalFlags(command, config);
5959
+ const outputPolicy = resolveOutputPolicy(
5960
+ globalFlags.format,
5961
+ globalFlags.jsonStrict === true
5962
+ );
5087
5963
  const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
5088
5964
  const prompt = await readPrompt(promptParts, flags.file, globalFlags.cwd);
5089
- const outputFormatter = createOutputFormatter(globalFlags.format);
5965
+ const outputFormatter = createOutputFormatter(outputPolicy.format);
5090
5966
  const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
5091
5967
  const result = await runOnce({
5092
5968
  agentCommand: agent.agentCommand,
5093
5969
  cwd: agent.cwd,
5094
5970
  message: prompt,
5095
5971
  permissionMode,
5972
+ nonInteractivePermissions: globalFlags.nonInteractivePermissions,
5096
5973
  authCredentials: config.auth,
5097
5974
  authPolicy: globalFlags.authPolicy,
5098
5975
  outputFormatter,
5976
+ suppressSdkConsoleErrors: outputPolicy.suppressSdkConsoleErrors,
5099
5977
  timeoutMs: globalFlags.timeout,
5100
5978
  verbose: globalFlags.verbose
5101
5979
  });
@@ -5196,6 +6074,7 @@ async function handleSetMode(explicitAgentName, modeId, flags, command, config)
5196
6074
  const result = await setSessionMode({
5197
6075
  sessionId: record.id,
5198
6076
  modeId,
6077
+ nonInteractivePermissions: globalFlags.nonInteractivePermissions,
5199
6078
  authCredentials: config.auth,
5200
6079
  authPolicy: globalFlags.authPolicy,
5201
6080
  timeoutMs: globalFlags.timeout,
@@ -5222,6 +6101,7 @@ async function handleSetConfigOption(explicitAgentName, configId, value, flags,
5222
6101
  sessionId: record.id,
5223
6102
  configId,
5224
6103
  value,
6104
+ nonInteractivePermissions: globalFlags.nonInteractivePermissions,
5225
6105
  authCredentials: config.auth,
5226
6106
  authPolicy: globalFlags.authPolicy,
5227
6107
  timeoutMs: globalFlags.timeout,
@@ -5281,12 +6161,18 @@ async function handleSessionsNew(explicitAgentName, flags, command, config) {
5281
6161
  cwd: agent.cwd,
5282
6162
  name: flags.name,
5283
6163
  permissionMode,
6164
+ nonInteractivePermissions: globalFlags.nonInteractivePermissions,
5284
6165
  authCredentials: config.auth,
5285
6166
  authPolicy: globalFlags.authPolicy,
5286
6167
  timeoutMs: globalFlags.timeout,
5287
6168
  verbose: globalFlags.verbose
5288
6169
  });
5289
- printCreatedSessionBanner(created, agent.agentName, globalFlags.format);
6170
+ printCreatedSessionBanner(
6171
+ created,
6172
+ agent.agentName,
6173
+ globalFlags.format,
6174
+ globalFlags.jsonStrict
6175
+ );
5290
6176
  if (globalFlags.verbose) {
5291
6177
  const scope = flags.name ? `named session "${flags.name}"` : "cwd session";
5292
6178
  process.stderr.write(`[acpx] created ${scope}: ${created.id}
@@ -5294,6 +6180,31 @@ async function handleSessionsNew(explicitAgentName, flags, command, config) {
5294
6180
  }
5295
6181
  printNewSessionByFormat(created, replaced, globalFlags.format);
5296
6182
  }
6183
+ async function handleSessionsEnsure(explicitAgentName, flags, command, config) {
6184
+ const globalFlags = resolveGlobalFlags(command, config);
6185
+ const permissionMode = resolvePermissionMode(globalFlags, config.defaultPermissions);
6186
+ const agent = resolveAgentInvocation(explicitAgentName, globalFlags, config);
6187
+ const result = await ensureSession({
6188
+ agentCommand: agent.agentCommand,
6189
+ cwd: agent.cwd,
6190
+ name: flags.name,
6191
+ permissionMode,
6192
+ nonInteractivePermissions: globalFlags.nonInteractivePermissions,
6193
+ authCredentials: config.auth,
6194
+ authPolicy: globalFlags.authPolicy,
6195
+ timeoutMs: globalFlags.timeout,
6196
+ verbose: globalFlags.verbose
6197
+ });
6198
+ if (result.created) {
6199
+ printCreatedSessionBanner(
6200
+ result.record,
6201
+ agent.agentName,
6202
+ globalFlags.format,
6203
+ globalFlags.jsonStrict
6204
+ );
6205
+ }
6206
+ printEnsuredSessionByFormat(result.record, result.created, globalFlags.format);
6207
+ }
5297
6208
  function printSessionDetailsByFormat(record, format) {
5298
6209
  if (format === "json") {
5299
6210
  process.stdout.write(`${JSON.stringify(record)}
@@ -5308,6 +6219,8 @@ function printSessionDetailsByFormat(record, format) {
5308
6219
  process.stdout.write(`id: ${record.id}
5309
6220
  `);
5310
6221
  process.stdout.write(`sessionId: ${record.sessionId}
6222
+ `);
6223
+ process.stdout.write(`runtimeSessionId: ${record.runtimeSessionId ?? "-"}
5311
6224
  `);
5312
6225
  process.stdout.write(`agent: ${record.agentCommand}
5313
6226
  `);
@@ -5476,7 +6389,8 @@ async function handleStatus(explicitAgentName, flags, command, config) {
5476
6389
  uptime: running ? formatUptime(record.agentStartedAt) ?? null : null,
5477
6390
  lastPromptTime: record.lastPromptAt ?? null,
5478
6391
  exitCode: running ? null : record.lastAgentExitCode ?? null,
5479
- signal: running ? null : record.lastAgentExitSignal ?? null
6392
+ signal: running ? null : record.lastAgentExitSignal ?? null,
6393
+ ...runtimeSessionIdPayload(record.runtimeSessionId)
5480
6394
  };
5481
6395
  if (globalFlags.format === "json") {
5482
6396
  process.stdout.write(`${JSON.stringify(payload)}
@@ -5490,6 +6404,10 @@ async function handleStatus(explicitAgentName, flags, command, config) {
5490
6404
  }
5491
6405
  process.stdout.write(`session: ${payload.sessionId}
5492
6406
  `);
6407
+ if ("runtimeSessionId" in payload) {
6408
+ process.stdout.write(`runtimeSessionId: ${payload.runtimeSessionId}
6409
+ `);
6410
+ }
5493
6411
  process.stdout.write(`agent: ${payload.agentCommand}
5494
6412
  `);
5495
6413
  process.stdout.write(`pid: ${payload.pid ?? "-"}
@@ -5555,7 +6473,7 @@ async function handleConfigInit(command, config) {
5555
6473
  `);
5556
6474
  }
5557
6475
  function registerSessionsCommand(parent, explicitAgentName, config) {
5558
- const sessionsCommand = parent.command("sessions").description("List, create, or close sessions for this agent");
6476
+ const sessionsCommand = parent.command("sessions").description("List, ensure, create, or close sessions for this agent");
5559
6477
  sessionsCommand.action(async function() {
5560
6478
  await handleSessionsList(explicitAgentName, this, config);
5561
6479
  });
@@ -5565,6 +6483,9 @@ function registerSessionsCommand(parent, explicitAgentName, config) {
5565
6483
  sessionsCommand.command("new").description("Create a fresh session for current cwd").option("--name <name>", "Session name", parseSessionName).action(async function(flags) {
5566
6484
  await handleSessionsNew(explicitAgentName, flags, this, config);
5567
6485
  });
6486
+ sessionsCommand.command("ensure").description("Ensure a session exists for current cwd or ancestor").option("--name <name>", "Session name", parseSessionName).action(async function(flags) {
6487
+ await handleSessionsEnsure(explicitAgentName, flags, this, config);
6488
+ });
5568
6489
  sessionsCommand.command("close").description("Close session for current cwd").argument("[name]", "Session name", parseSessionName).action(async function(name) {
5569
6490
  await handleSessionsClose(explicitAgentName, name, this, config);
5570
6491
  });
@@ -5685,14 +6606,14 @@ function detectAgentToken(argv) {
5685
6606
  hasAgentOverride = true;
5686
6607
  continue;
5687
6608
  }
5688
- if (token === "--cwd" || token === "--auth-policy" || token === "--format" || token === "--timeout" || token === "--ttl" || token === "--file") {
6609
+ if (token === "--cwd" || token === "--auth-policy" || token === "--non-interactive-permissions" || token === "--format" || token === "--timeout" || token === "--ttl" || token === "--file") {
5689
6610
  index += 1;
5690
6611
  continue;
5691
6612
  }
5692
- if (token.startsWith("--cwd=") || token.startsWith("--auth-policy=") || token.startsWith("--format=") || token.startsWith("--timeout=") || token.startsWith("--ttl=") || token.startsWith("--file=")) {
6613
+ if (token.startsWith("--cwd=") || token.startsWith("--auth-policy=") || token.startsWith("--non-interactive-permissions=") || token.startsWith("--format=") || token.startsWith("--json-strict=") || token.startsWith("--timeout=") || token.startsWith("--ttl=") || token.startsWith("--file=")) {
5693
6614
  continue;
5694
6615
  }
5695
- if (token === "--approve-all" || token === "--approve-reads" || token === "--deny-all" || token === "--verbose") {
6616
+ if (token === "--approve-all" || token === "--approve-reads" || token === "--deny-all" || token === "--json-strict" || token === "--verbose") {
5696
6617
  continue;
5697
6618
  }
5698
6619
  return { hasAgentOverride };
@@ -5722,15 +6643,103 @@ function detectInitialCwd(argv) {
5722
6643
  }
5723
6644
  return process.cwd();
5724
6645
  }
6646
+ function detectRequestedOutputFormat(argv, fallback) {
6647
+ let detectedFormat = fallback;
6648
+ for (let index = 0; index < argv.length; index += 1) {
6649
+ const token = argv[index];
6650
+ if (token === "--") {
6651
+ break;
6652
+ }
6653
+ if (token === "--json-strict" || token.startsWith("--json-strict=")) {
6654
+ return "json";
6655
+ }
6656
+ if (token === "--format") {
6657
+ const raw = argv[index + 1];
6658
+ if (raw && OUTPUT_FORMATS.includes(raw)) {
6659
+ detectedFormat = raw;
6660
+ }
6661
+ continue;
6662
+ }
6663
+ if (token.startsWith("--format=")) {
6664
+ const raw = token.slice("--format=".length).trim();
6665
+ if (OUTPUT_FORMATS.includes(raw)) {
6666
+ detectedFormat = raw;
6667
+ }
6668
+ }
6669
+ }
6670
+ return detectedFormat;
6671
+ }
6672
+ function detectJsonStrict(argv) {
6673
+ for (let index = 0; index < argv.length; index += 1) {
6674
+ const token = argv[index];
6675
+ if (token === "--") {
6676
+ break;
6677
+ }
6678
+ if (token === "--json-strict") {
6679
+ return true;
6680
+ }
6681
+ if (token.startsWith("--json-strict=")) {
6682
+ return true;
6683
+ }
6684
+ }
6685
+ return false;
6686
+ }
6687
+ function emitJsonErrorEvent(error) {
6688
+ const formatter = createOutputFormatter("json", {
6689
+ jsonContext: {
6690
+ sessionId: "unknown",
6691
+ stream: "control"
6692
+ }
6693
+ });
6694
+ formatter.onError(error);
6695
+ formatter.flush();
6696
+ }
6697
+ function isOutputAlreadyEmitted(error) {
6698
+ if (!error || typeof error !== "object") {
6699
+ return false;
6700
+ }
6701
+ return error.outputAlreadyEmitted === true;
6702
+ }
6703
+ function emitRequestedError(error, normalized, outputPolicy) {
6704
+ if (isOutputAlreadyEmitted(error)) {
6705
+ return;
6706
+ }
6707
+ if (outputPolicy.format === "json") {
6708
+ emitJsonErrorEvent(normalized);
6709
+ } else if (!outputPolicy.suppressNonJsonStderr) {
6710
+ process.stderr.write(`${normalized.message}
6711
+ `);
6712
+ }
6713
+ }
6714
+ async function runWithOutputPolicy(_outputPolicy, run) {
6715
+ return await run();
6716
+ }
5725
6717
  async function main(argv = process.argv) {
5726
6718
  await maybeHandleSkillflag(argv, {
5727
6719
  skillsRoot: findSkillsRoot(import.meta.url),
5728
6720
  includeBundledSkill: false
5729
6721
  });
5730
6722
  const config = await loadResolvedConfig(detectInitialCwd(argv.slice(2)));
6723
+ const requestedJsonStrict = detectJsonStrict(argv.slice(2));
6724
+ const requestedOutputFormat = detectRequestedOutputFormat(
6725
+ argv.slice(2),
6726
+ config.format
6727
+ );
6728
+ const requestedOutputPolicy = resolveOutputPolicy(
6729
+ requestedOutputFormat,
6730
+ requestedJsonStrict
6731
+ );
5731
6732
  const builtInAgents = listBuiltInAgents(config.agents);
5732
6733
  const program = new Command();
5733
6734
  program.name("acpx").description("Headless CLI client for the Agent Client Protocol").enablePositionalOptions().showHelpAfterError();
6735
+ if (requestedJsonStrict) {
6736
+ program.configureOutput({
6737
+ writeOut: () => {
6738
+ },
6739
+ writeErr: () => {
6740
+ }
6741
+ });
6742
+ }
5734
6743
  addGlobalFlags(program);
5735
6744
  for (const agentName of builtInAgents) {
5736
6745
  registerAgentCommand(program, agentName, config);
@@ -5742,6 +6751,11 @@ async function main(argv = process.argv) {
5742
6751
  }
5743
6752
  program.argument("[prompt...]", "Prompt text").action(async function(promptParts) {
5744
6753
  if (promptParts.length === 0 && process.stdin.isTTY) {
6754
+ if (requestedJsonStrict) {
6755
+ throw new InvalidArgumentError(
6756
+ "Prompt is required (pass as argument, --file, or pipe via stdin)"
6757
+ );
6758
+ }
5745
6759
  this.outputHelp();
5746
6760
  return;
5747
6761
  }
@@ -5762,6 +6776,7 @@ Examples:
5762
6776
  acpx codex -s backend "fix the API"
5763
6777
  acpx codex sessions
5764
6778
  acpx codex sessions new --name backend
6779
+ acpx codex sessions ensure --name backend
5765
6780
  acpx codex sessions close backend
5766
6781
  acpx codex status
5767
6782
  acpx config show
@@ -5774,33 +6789,33 @@ Examples:
5774
6789
  program.exitOverride((error) => {
5775
6790
  throw error;
5776
6791
  });
5777
- try {
5778
- await program.parseAsync(argv);
5779
- } catch (error) {
5780
- if (error instanceof CommanderError) {
5781
- if (error.code === "commander.helpDisplayed" || error.code === "commander.version") {
5782
- process.exit(EXIT_CODES.SUCCESS);
6792
+ await runWithOutputPolicy(requestedOutputPolicy, async () => {
6793
+ try {
6794
+ await program.parseAsync(argv);
6795
+ } catch (error) {
6796
+ if (error instanceof CommanderError) {
6797
+ if (error.code === "commander.helpDisplayed" || error.code === "commander.version") {
6798
+ process.exit(EXIT_CODES.SUCCESS);
6799
+ }
6800
+ const normalized2 = normalizeOutputError(error, {
6801
+ defaultCode: "USAGE",
6802
+ origin: "cli"
6803
+ });
6804
+ if (requestedOutputPolicy.format === "json") {
6805
+ emitRequestedError(error, normalized2, requestedOutputPolicy);
6806
+ }
6807
+ process.exit(exitCodeForOutputErrorCode(normalized2.code));
5783
6808
  }
5784
- process.exit(EXIT_CODES.USAGE);
5785
- }
5786
- if (error instanceof InterruptedError) {
5787
- process.exit(EXIT_CODES.INTERRUPTED);
5788
- }
5789
- if (error instanceof TimeoutError) {
5790
- process.stderr.write(`${error.message}
5791
- `);
5792
- process.exit(EXIT_CODES.TIMEOUT);
5793
- }
5794
- if (error instanceof NoSessionError) {
5795
- process.stderr.write(`${error.message}
5796
- `);
5797
- process.exit(EXIT_CODES.NO_SESSION);
6809
+ if (error instanceof InterruptedError) {
6810
+ process.exit(EXIT_CODES.INTERRUPTED);
6811
+ }
6812
+ const normalized = normalizeOutputError(error, {
6813
+ origin: "cli"
6814
+ });
6815
+ emitRequestedError(error, normalized, requestedOutputPolicy);
6816
+ process.exit(exitCodeForOutputErrorCode(normalized.code));
5798
6817
  }
5799
- const message = error instanceof Error ? error.message : String(error);
5800
- process.stderr.write(`${message}
5801
- `);
5802
- process.exit(EXIT_CODES.ERROR);
5803
- }
6818
+ });
5804
6819
  }
5805
6820
  function isCliEntrypoint(argv) {
5806
6821
  const entry = argv[1];