acpx 0.16.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +2 -0
  2. package/dist/{agent-registry-DU6mhZBL.js → agent-registry-evUoJINz.js} +5 -1
  3. package/dist/agent-registry-evUoJINz.js.map +1 -0
  4. package/dist/agent-registry.d.ts.map +1 -1
  5. package/dist/agent-registry.js +1 -1
  6. package/dist/{cli-BkYIxj_N.js → cli-B2ycKzaO.js} +5 -4
  7. package/dist/{cli-BkYIxj_N.js.map → cli-B2ycKzaO.js.map} +1 -1
  8. package/dist/cli.d.ts +1 -1
  9. package/dist/cli.js +34 -27
  10. package/dist/cli.js.map +1 -1
  11. package/dist/{client-5levcBIs.d.ts → client-CkbBoSGd.d.ts} +17 -7
  12. package/dist/client-CkbBoSGd.d.ts.map +1 -0
  13. package/dist/{flows-Bxs4udrs.js → flows-xGyhmoHO.js} +7 -6
  14. package/dist/{flows-Bxs4udrs.js.map → flows-xGyhmoHO.js.map} +1 -1
  15. package/dist/flows.d.ts +2 -2
  16. package/dist/flows.js +1 -1
  17. package/dist/{invocation-options-BY4lxkn3.js → invocation-options-eZEBh4vd.js} +5 -4
  18. package/dist/{invocation-options-BY4lxkn3.js.map → invocation-options-eZEBh4vd.js.map} +1 -1
  19. package/dist/ipc-D2rstexT.js +3350 -0
  20. package/dist/ipc-D2rstexT.js.map +1 -0
  21. package/dist/output-D2xL6wdp.js +980 -0
  22. package/dist/output-D2xL6wdp.js.map +1 -0
  23. package/dist/{live-checkpoint-BEfxBKCh.js → queue-owner-runtime-Dgv3e2Tp.js} +3104 -1878
  24. package/dist/queue-owner-runtime-Dgv3e2Tp.js.map +1 -0
  25. package/dist/runtime.d.ts +187 -46
  26. package/dist/runtime.d.ts.map +1 -1
  27. package/dist/runtime.js +416 -93
  28. package/dist/runtime.js.map +1 -1
  29. package/dist/{session-options-t3d-F4dn.d.ts → session-options-Dp6mzNSy.d.ts} +2 -2
  30. package/dist/{session-options-t3d-F4dn.d.ts.map → session-options-Dp6mzNSy.d.ts.map} +1 -1
  31. package/dist/session-watch-CTe9Bnbc.js +76 -0
  32. package/dist/session-watch-CTe9Bnbc.js.map +1 -0
  33. package/dist/watch-DxuXEc8n.js +32 -0
  34. package/dist/watch-DxuXEc8n.js.map +1 -0
  35. package/package.json +3 -3
  36. package/skills/acpx/SKILL.md +6 -2
  37. package/dist/agent-registry-DU6mhZBL.js.map +0 -1
  38. package/dist/client-5levcBIs.d.ts.map +0 -1
  39. package/dist/live-checkpoint-BEfxBKCh.js.map +0 -1
  40. package/dist/output-uo5a27KB.js +0 -4711
  41. package/dist/output-uo5a27KB.js.map +0 -1
package/dist/runtime.js CHANGED
@@ -1,5 +1,8 @@
1
- import { A as isRequestedModelUnsupportedError, C as recordSessionUpdate, D as REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, Dt as withTimeout, E as AcpClient, L as textPrompt, Mt as extractAcpError, Nt as isAcpResourceNotFoundError, O as REQUESTED_MODEL_UNSUPPORTED_REASONS, S as recordPromptSubmission, T as advertisedModelState, Z as assertPersistedKeyPolicy, _ as reconcileAgentSessionId, a as applyConfigOptionSelection, c as applyModelSelection, f as persistSessionOptions, g as createInitialSessionRecord, gt as writePrivateJsonFile, h as applyLifecycleSnapshotToRecord, i as connectAndLoadSession, jt as normalizeOutputError, k as RequestedModelUnsupportedError, l as applyRequestedModelIfAdvertised, m as applyConversation, n as runPromptTurn, o as applyConfigOptionsToRecord, ot as parseSessionRecord, p as sessionOptionsFromRecord, r as withConnectedSession, s as applyInitialModelSelection, st as serializeSessionRecordForDisk, t as LiveSessionCheckpoint, u as setDesiredModeId, v as cloneSessionAcpxState, w as trimConversationForRuntime, x as recordClientOperation, y as cloneSessionConversation } from "./live-checkpoint-BEfxBKCh.js";
2
- import { D as normalizeAgentCommandInput, a as createAgentRegistry, i as DEFAULT_AGENT_NAME } from "./agent-registry-DU6mhZBL.js";
1
+ import { C as textPrompt, D as findSession, F as resolveSessionRecord, L as assertPersistedKeyPolicy, P as readSessionRecord, R as parseSessionRecord, gt as extractAcpError, mt as writePrivateJsonFile, t as tryCancelOnRunningOwner, vt as isAcpResourceNotFoundError, z as serializeSessionRecordForDisk } from "./ipc-D2rstexT.js";
2
+ import { A as setDesiredModeId, B as recordClientOperation, C as withConnectedSession, D as applyInitialModelSelection, E as applyConfigOptionsToRecord, F as createInitialSessionRecord, G as AcpClient, H as recordSessionUpdate, I as reconcileAgentSessionId, J as RequestedModelUnsupportedError, K as REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, L as cloneSessionAcpxState, M as sessionOptionsFromRecord, N as applyConversation, O as applyModelSelection, P as applyLifecycleSnapshotToRecord, R as cloneSessionConversation, T as applyConfigOptionSelection, U as trimConversationForRuntime, V as recordPromptSubmission, W as advertisedModelState, Y as isRequestedModelUnsupportedError, _ as cancelSessionPrompt, c as LiveSessionCheckpoint, ct as normalizeOutputError, h as ensureSession, it as assertControlAuthority, j as persistSessionOptions, k as applyRequestedModelIfAdvertised, l as runPromptTurn, ot as withTimeout, q as REQUESTED_MODEL_UNSUPPORTED_REASONS, r as sendSession, rt as TimeoutError, s as DISCARD_OUTPUT_FORMATTER, v as closeSession, w as connectAndLoadSession } from "./queue-owner-runtime-Dgv3e2Tp.js";
3
+ import { D as normalizeAgentCommandInput, a as createAgentRegistry, i as DEFAULT_AGENT_NAME } from "./agent-registry-evUoJINz.js";
4
+ import { t as watchSession } from "./watch-DxuXEc8n.js";
5
+ import { fileURLToPath } from "node:url";
3
6
  import path from "node:path";
4
7
  import fs from "node:fs/promises";
5
8
  import { randomUUID } from "node:crypto";
@@ -544,6 +547,61 @@ function updateStatusEvent(payload, tag) {
544
547
  }
545
548
  //#endregion
546
549
  //#region src/runtime/public/probe.ts
550
+ /**
551
+ * Inspect session model metadata without a runtime store or prompt. The agent
552
+ * may create native session history; only its owned transport is closed.
553
+ */
554
+ async function inspectAgentModels(options) {
555
+ options.signal?.throwIfAborted();
556
+ const timeoutMs = options.timeoutMs ?? 12e4;
557
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0 || timeoutMs > 2147483647) throw new RangeError("Model inspection timeoutMs must be positive and at most 2147483647");
558
+ const deadline = new AbortController();
559
+ const signal = options.signal ? AbortSignal.any([options.signal, deadline.signal]) : deadline.signal;
560
+ const assertActive = () => signal.throwIfAborted();
561
+ const client = new AcpClient({
562
+ ...normalizeAgentCommandInput(options.agentCommand),
563
+ cwd: options.cwd,
564
+ agentProcessEnv: options.agentProcessEnv,
565
+ permissionMode: "deny-all",
566
+ nonInteractivePermissions: "deny",
567
+ fs: false,
568
+ terminal: false,
569
+ processLifecycle: {
570
+ onBeforeSpawn: assertActive,
571
+ onSpawned: assertActive
572
+ }
573
+ });
574
+ const timer = setTimeout(() => deadline.abort(new TimeoutError(timeoutMs)), timeoutMs);
575
+ let onAbort;
576
+ const aborted = new Promise((_resolve, reject) => {
577
+ onAbort = () => reject(signal.reason);
578
+ signal.addEventListener("abort", onAbort, { once: true });
579
+ });
580
+ const inspection = (async () => {
581
+ assertActive();
582
+ await client.start();
583
+ assertActive();
584
+ const { models } = await client.createSession();
585
+ assertActive();
586
+ return models ? {
587
+ currentModelId: models.currentModelId,
588
+ availableModelIds: models.availableModels.map((model) => model.modelId),
589
+ availableModels: models.availableModels
590
+ } : void 0;
591
+ })();
592
+ try {
593
+ return await Promise.race([inspection, aborted]);
594
+ } finally {
595
+ clearTimeout(timer);
596
+ signal.removeEventListener("abort", onAbort);
597
+ try {
598
+ if (signal.aborted) await client.close();
599
+ } finally {
600
+ await inspection.catch(() => {});
601
+ await client.close();
602
+ }
603
+ }
604
+ }
547
605
  function isPrimitiveDetail(value) {
548
606
  return value == null || typeof value === "number" || typeof value === "boolean" || typeof value === "bigint" || typeof value === "symbol";
549
607
  }
@@ -733,7 +791,7 @@ function commandRecord(value) {
733
791
  return value;
734
792
  }
735
793
  //#endregion
736
- //#region src/runtime/engine/manager.ts
794
+ //#region src/runtime/engine/turn.ts
737
795
  function createDeferred() {
738
796
  let resolve;
739
797
  let reject;
@@ -746,22 +804,6 @@ function createDeferred() {
746
804
  reject
747
805
  };
748
806
  }
749
- async function settleAttempt(run) {
750
- try {
751
- return {
752
- ok: true,
753
- value: await run()
754
- };
755
- } catch (error) {
756
- return {
757
- ok: false,
758
- error
759
- };
760
- }
761
- }
762
- function firstFailedAttempt(attempts) {
763
- return attempts.find((attempt) => !attempt.ok);
764
- }
765
807
  var AsyncEventQueue = class {
766
808
  items = [];
767
809
  waits = [];
@@ -798,17 +840,6 @@ var AsyncEventQueue = class {
798
840
  }
799
841
  }
800
842
  };
801
- function isoNow() {
802
- return (/* @__PURE__ */ new Date()).toISOString();
803
- }
804
- function isUnsupportedSessionCloseError(error) {
805
- const acp = extractAcpError(error);
806
- if (!acp) return false;
807
- if (acp.code === -32601 || acp.code === -32602) return true;
808
- if (acp.code !== -32603 || !acp.data || typeof acp.data !== "object") return false;
809
- const details = acp.data.details;
810
- return typeof details === "string" && details.toLowerCase().includes("invalid params");
811
- }
812
843
  function toPromptInput(text, attachments) {
813
844
  if (!attachments || attachments.length === 0) return text;
814
845
  const blocks = [];
@@ -837,13 +868,6 @@ function toPromptInput(text, attachments) {
837
868
  }
838
869
  return blocks.length > 0 ? blocks : textPrompt(text);
839
870
  }
840
- function createRecordId(sessionKey, mode) {
841
- if (mode === "persistent") return sessionKey;
842
- return `${sessionKey}:oneshot:${randomUUID()}`;
843
- }
844
- function resumePolicyForSessionMode(mode) {
845
- return mode === "persistent" ? "same-session-only" : "allow-new";
846
- }
847
871
  function legacyTerminalEventFromTurnResult(result) {
848
872
  if (result.status === "failed") return {
849
873
  type: "error",
@@ -858,6 +882,42 @@ function legacyTerminalEventFromTurnResult(result) {
858
882
  ...result._meta === void 0 ? {} : { _meta: result._meta }
859
883
  };
860
884
  }
885
+ //#endregion
886
+ //#region src/runtime/engine/manager.ts
887
+ async function settleAttempt(run) {
888
+ try {
889
+ return {
890
+ ok: true,
891
+ value: await run()
892
+ };
893
+ } catch (error) {
894
+ return {
895
+ ok: false,
896
+ error
897
+ };
898
+ }
899
+ }
900
+ function firstFailedAttempt(attempts) {
901
+ return attempts.find((attempt) => !attempt.ok);
902
+ }
903
+ function isoNow() {
904
+ return (/* @__PURE__ */ new Date()).toISOString();
905
+ }
906
+ function isUnsupportedSessionCloseError(error) {
907
+ const acp = extractAcpError(error);
908
+ if (!acp) return false;
909
+ if (acp.code === -32601 || acp.code === -32602) return true;
910
+ if (acp.code !== -32603 || !acp.data || typeof acp.data !== "object") return false;
911
+ const details = acp.data.details;
912
+ return typeof details === "string" && details.toLowerCase().includes("invalid params");
913
+ }
914
+ function createRecordId(sessionKey, mode) {
915
+ if (mode === "persistent") return sessionKey;
916
+ return `${sessionKey}:oneshot:${randomUUID()}`;
917
+ }
918
+ function resumePolicyForSessionMode(mode) {
919
+ return mode === "persistent" ? "same-session-only" : "allow-new";
920
+ }
861
921
  function advertisedConfigOptionIds(record) {
862
922
  const configOptions = record.acpx?.config_options;
863
923
  if (!configOptions) return;
@@ -872,6 +932,14 @@ function resolveSupportedConfigOptionId(record, configId) {
872
932
  const supportedText = supported.length > 0 ? supported.join(", ") : "none";
873
933
  throw new AcpRuntimeError("ACP_BACKEND_UNSUPPORTED_CONTROL", `ACP session ${record.acpxRecordId} does not advertise config option '${configId}'. Supported config options: ${supportedText}.`);
874
934
  }
935
+ async function startRuntimeClient(client, agent, timeoutMs) {
936
+ try {
937
+ await withTimeout(client.start(), timeoutMs);
938
+ } catch (error) {
939
+ if (!(error instanceof TimeoutError)) throw error;
940
+ throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", `ACP agent ${path.basename(agent.agentArgv?.[0] ?? agent.agentCommand.split(/\s+/)[0])} did not complete ACP initialization within ${timeoutMs}ms. Check that the configured command starts an ACP server.`, { cause: error });
941
+ }
942
+ }
875
943
  async function createOrLoadRuntimeSession(client, resumeSessionId, cwd) {
876
944
  if (resumeSessionId) {
877
945
  if (client.supportsResumeSession()) {
@@ -1179,9 +1247,26 @@ var AcpRuntimeManager = class {
1179
1247
  agentArgv: agentArgv ? [...agentArgv] : void 0
1180
1248
  }) : servers ?? []];
1181
1249
  }
1182
- async withRuntimeControlSession(record, sessionMode, run, replacingConfigOption) {
1250
+ resolveSessionPermissions(session) {
1251
+ const { sessionKey, cwd, agentCommand, agentArgv } = session;
1252
+ const { permissionMode = this.options.permissionMode, nonInteractivePermissions = this.options.nonInteractivePermissions, permissionPolicy = this.options.permissionPolicy, onPermissionRequest = this.options.onPermissionRequest } = this.options.sessionPermissions?.({
1253
+ sessionKey,
1254
+ cwd,
1255
+ agentCommand,
1256
+ agentArgv: agentArgv ? [...agentArgv] : void 0
1257
+ }) ?? {};
1258
+ return {
1259
+ permissionMode,
1260
+ nonInteractivePermissions,
1261
+ permissionPolicy,
1262
+ onPermissionRequest
1263
+ };
1264
+ }
1265
+ async withRuntimeControlSession(record, sessionMode, run, replacingConfigOption, authority) {
1183
1266
  await this.retrySessionCleanup(record.acpxRecordId);
1267
+ assertControlAuthority(authority);
1184
1268
  const owner = await this.readRetainedSessionOwner(record, { consume: false });
1269
+ assertControlAuthority(authority);
1185
1270
  if (owner) {
1186
1271
  const ownedRecord = owner.projection?.record ?? record;
1187
1272
  try {
@@ -1217,15 +1302,16 @@ var AcpRuntimeManager = class {
1217
1302
  ...record,
1218
1303
  sessionKey: record.name ?? record.acpxRecordId
1219
1304
  }),
1220
- permissionMode: this.options.permissionMode,
1221
- nonInteractivePermissions: this.options.nonInteractivePermissions,
1222
- permissionPolicy: this.options.permissionPolicy,
1223
- onPermissionRequest: this.options.onPermissionRequest,
1305
+ ...this.resolveSessionPermissions({
1306
+ ...record,
1307
+ sessionKey: record.name ?? record.acpxRecordId
1308
+ }),
1224
1309
  elicitationModes: this.options.elicitationModes,
1225
1310
  verbose: this.options.verbose,
1226
1311
  timeoutMs: this.options.timeoutMs,
1227
1312
  resumePolicy: resumePolicyForSessionMode(sessionMode),
1228
1313
  replacingConfigOption,
1314
+ authority,
1229
1315
  run
1230
1316
  });
1231
1317
  return {
@@ -1251,8 +1337,9 @@ var AcpRuntimeManager = class {
1251
1337
  const key = `${input.mode}\0${input.sessionKey}`;
1252
1338
  return await this.withManagerLock(this.ensureSessionLocks, key, run);
1253
1339
  }
1254
- async withManagerLock(locks, key, run) {
1340
+ async withManagerLock(locks, key, run, authority) {
1255
1341
  this.assertOpen();
1342
+ assertControlAuthority(authority);
1256
1343
  const previous = locks.get(key) ?? Promise.resolve();
1257
1344
  let release;
1258
1345
  const gate = new Promise((resolve) => {
@@ -1263,6 +1350,7 @@ var AcpRuntimeManager = class {
1263
1350
  await previous;
1264
1351
  try {
1265
1352
  this.assertOpen();
1353
+ assertControlAuthority(authority);
1266
1354
  return await run();
1267
1355
  } finally {
1268
1356
  release();
@@ -1331,10 +1419,10 @@ var AcpRuntimeManager = class {
1331
1419
  ...agent,
1332
1420
  sessionKey: input.sessionKey
1333
1421
  }),
1334
- permissionMode: this.options.permissionMode,
1335
- nonInteractivePermissions: this.options.nonInteractivePermissions,
1336
- permissionPolicy: this.options.permissionPolicy,
1337
- onPermissionRequest: this.options.onPermissionRequest,
1422
+ ...this.resolveSessionPermissions({
1423
+ ...agent,
1424
+ sessionKey: input.sessionKey
1425
+ }),
1338
1426
  elicitationModes: this.options.elicitationModes,
1339
1427
  processLifecycle: this.options.processLifecycle,
1340
1428
  processLaunchScope: {
@@ -1351,7 +1439,7 @@ var AcpRuntimeManager = class {
1351
1439
  });
1352
1440
  let retained = false;
1353
1441
  try {
1354
- await client.start();
1442
+ await startRuntimeClient(client, agent, this.options.timeoutMs);
1355
1443
  this.assertOpen();
1356
1444
  const session = await createOrLoadRuntimeSession(client, input.resumeSessionId, cwd);
1357
1445
  const record = await this.prepareInitialRuntimeRecord({
@@ -1455,7 +1543,7 @@ var AcpRuntimeManager = class {
1455
1543
  return true;
1456
1544
  };
1457
1545
  const abortHandler = () => {
1458
- requestCancel();
1546
+ requestCancel().catch(() => {});
1459
1547
  };
1460
1548
  if (input.signal && !input.signal.aborted) input.signal.addEventListener("abort", abortHandler, { once: true });
1461
1549
  this.queueSessionTask(input.handle.acpxRecordId ?? input.handle.sessionKey, async () => this.runRuntimeTurnTask({
@@ -1505,7 +1593,6 @@ var AcpRuntimeManager = class {
1505
1593
  stopReason: "cancelled"
1506
1594
  };
1507
1595
  else {
1508
- await this.applyPendingRuntimeTurnCancel(task, turn);
1509
1596
  const response = await this.runRuntimePrompt(task, turn, sessionId);
1510
1597
  await this.saveCompletedRuntimeTurn(turn);
1511
1598
  terminalResult = {
@@ -1516,7 +1603,7 @@ var AcpRuntimeManager = class {
1516
1603
  }
1517
1604
  }
1518
1605
  } catch (error) {
1519
- terminalResult = this.failRuntimeTurn(task, error);
1606
+ terminalResult = this.handleRuntimeTurnFailure(task, error);
1520
1607
  }
1521
1608
  const finalization = await settleAttempt(async () => this.finalizeRuntimeTurn(task, turn));
1522
1609
  if (!finalization.ok) {
@@ -1536,7 +1623,8 @@ var AcpRuntimeManager = class {
1536
1623
  promptMessageId: turn.promptMessageId,
1537
1624
  onPromptRequestWritten: () => task.promptStarted.resolve(),
1538
1625
  onElicitation: task.input.onElicitation,
1539
- onPermissionRequest: task.input.onPermissionRequest
1626
+ onPermissionRequest: task.input.onPermissionRequest,
1627
+ authority: task.input
1540
1628
  });
1541
1629
  } finally {
1542
1630
  turn.client.endPromptElicitation?.(sessionId);
@@ -1658,10 +1746,10 @@ var AcpRuntimeManager = class {
1658
1746
  ...record,
1659
1747
  sessionKey: record.name ?? record.acpxRecordId
1660
1748
  }),
1661
- permissionMode: this.options.permissionMode,
1662
- nonInteractivePermissions: this.options.nonInteractivePermissions,
1663
- permissionPolicy: this.options.permissionPolicy,
1664
- onPermissionRequest: this.options.onPermissionRequest,
1749
+ ...this.resolveSessionPermissions({
1750
+ ...record,
1751
+ sessionKey: record.name ?? record.acpxRecordId
1752
+ }),
1665
1753
  elicitationModes: this.options.elicitationModes,
1666
1754
  processLifecycle: this.options.processLifecycle,
1667
1755
  processLaunchScope: {
@@ -1685,24 +1773,24 @@ var AcpRuntimeManager = class {
1685
1773
  return {
1686
1774
  hasActivePrompt: () => turn.client.hasActivePrompt(),
1687
1775
  requestCancelActivePrompt: async () => await this.requestRuntimeTurnCancel(task, turn),
1688
- setSessionMode: async (modeId) => {
1776
+ setSessionMode: async (modeId, authority) => {
1689
1777
  await this.waitForRuntimeControlSession(task, turn);
1690
- await turn.client.setSessionMode(turn.activeSessionId, modeId);
1778
+ await turn.client.setSessionMode(turn.activeSessionId, modeId, authority);
1691
1779
  const nextState = cloneSessionAcpxState(turn.acpxState) ?? {};
1692
1780
  nextState.desired_mode_id = modeId;
1693
1781
  turn.acpxState = nextState;
1694
1782
  },
1695
- setSessionModel: async (modelId) => {
1783
+ setSessionModel: async (modelId, authority) => {
1696
1784
  await this.waitForRuntimeControlSession(task, turn);
1697
1785
  const models = advertisedModelState(turn.acpxState);
1698
- const response = await turn.client.setSessionModel(turn.activeSessionId, modelId, models);
1786
+ const response = await turn.client.setSessionModel(turn.activeSessionId, modelId, models, authority);
1699
1787
  turn.acpxState = applyModelSelection(turn.acpxState, modelId, response);
1700
1788
  return response;
1701
1789
  },
1702
- setSessionConfigOption: async (configId, value) => {
1703
- return (await task.state.activeController.setResolvedSessionConfigOption(configId, value)).response;
1790
+ setSessionConfigOption: async (configId, value, authority) => {
1791
+ return (await task.state.activeController.setResolvedSessionConfigOption(configId, value, authority)).response;
1704
1792
  },
1705
- setResolvedSessionConfigOption: async (configId, value) => await this.setRuntimeResolvedSessionConfigOption(task, turn, configId, value)
1793
+ setResolvedSessionConfigOption: async (configId, value, authority) => await this.setRuntimeResolvedSessionConfigOption(task, turn, configId, value, authority)
1706
1794
  };
1707
1795
  }
1708
1796
  async waitForRuntimeControlSession(task, turn) {
@@ -1715,14 +1803,14 @@ var AcpRuntimeManager = class {
1715
1803
  task.state.pendingCancel = true;
1716
1804
  return true;
1717
1805
  }
1718
- async setRuntimeResolvedSessionConfigOption(task, turn, configId, value) {
1806
+ async setRuntimeResolvedSessionConfigOption(task, turn, configId, value, authority) {
1719
1807
  await this.waitForRuntimeControlSession(task, turn);
1720
1808
  const resolvedConfigId = resolveSupportedConfigOptionId({
1721
1809
  ...turn.record,
1722
1810
  acpx: turn.acpxState ?? void 0
1723
1811
  }, configId);
1724
1812
  const modelConfigId = advertisedModelState(turn.acpxState)?.configId;
1725
- const response = await turn.client.setSessionConfigOption(turn.activeSessionId, resolvedConfigId, value);
1813
+ const response = await turn.client.setSessionConfigOption(turn.activeSessionId, resolvedConfigId, value, authority);
1726
1814
  turn.acpxState = applyConfigOptionSelection(turn.acpxState, resolvedConfigId, value, response, modelConfigId);
1727
1815
  return {
1728
1816
  configId: resolvedConfigId,
@@ -1780,12 +1868,6 @@ var AcpRuntimeManager = class {
1780
1868
  task.promptStarted.reject(/* @__PURE__ */ new Error("ACP turn cancelled before prompt submission."));
1781
1869
  return true;
1782
1870
  }
1783
- async applyPendingRuntimeTurnCancel(task, turn) {
1784
- if (!task.state.pendingCancel || !turn.client.hasActivePrompt()) return false;
1785
- const cancelled = await turn.client.requestCancelActivePrompt();
1786
- if (cancelled) task.state.pendingCancel = false;
1787
- return cancelled;
1788
- }
1789
1871
  async saveCompletedRuntimeTurn(turn) {
1790
1872
  turn.record.acpSessionId = turn.activeSessionId;
1791
1873
  reconcileAgentSessionId(turn.record, turn.record.agentSessionId);
@@ -1796,6 +1878,15 @@ var AcpRuntimeManager = class {
1796
1878
  applyLifecycleSnapshotToRecord(turn.record, turn.client.getAgentLifecycleSnapshot());
1797
1879
  await this.options.sessionStore.save(turn.record);
1798
1880
  }
1881
+ handleRuntimeTurnFailure(task, error) {
1882
+ if (!task.input.signal?.aborted || error !== task.input.signal.reason) return this.failRuntimeTurn(task, error);
1883
+ task.promptStarted.reject(error);
1884
+ task.sessionReady.reject(error);
1885
+ return {
1886
+ status: "cancelled",
1887
+ stopReason: "cancelled"
1888
+ };
1889
+ }
1799
1890
  failRuntimeTurn(task, error) {
1800
1891
  task.promptStarted.reject(error);
1801
1892
  task.sessionReady.reject(error);
@@ -1879,49 +1970,52 @@ var AcpRuntimeManager = class {
1879
1970
  if (owner) await this.flushSessionOwner(owner);
1880
1971
  return runtimeStatusFromRecord(await this.requireRecord(recordId));
1881
1972
  }
1882
- async setMode(handle, mode, sessionMode = "persistent") {
1973
+ async setMode(handle, mode, sessionMode = "persistent", authority) {
1883
1974
  const recordId = handle.acpxRecordId ?? handle.sessionKey;
1884
- await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setModeWithOwnership(handle, mode, sessionMode));
1975
+ await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setModeWithOwnership(handle, mode, sessionMode, authority), authority);
1885
1976
  }
1886
- async setModeWithOwnership(handle, mode, sessionMode) {
1977
+ async setModeWithOwnership(handle, mode, sessionMode, authority) {
1887
1978
  const record = await this.requireRecord(handle.acpxRecordId ?? handle.sessionKey);
1979
+ assertControlAuthority(authority);
1888
1980
  const controller = this.activeControllers.get(record.acpxRecordId);
1889
1981
  let targetRecord = record;
1890
- if (controller) await controller.setSessionMode(mode);
1982
+ if (controller) await controller.setSessionMode(mode, authority);
1891
1983
  else targetRecord = (await this.withRuntimeControlSession(record, sessionMode, async ({ client, sessionId }) => {
1892
- await client.setSessionMode(sessionId, mode);
1893
- })).record;
1984
+ await client.setSessionMode(sessionId, mode, authority);
1985
+ }, void 0, authority)).record;
1894
1986
  setDesiredModeId(targetRecord, mode);
1895
1987
  await this.options.sessionStore.save(targetRecord);
1896
1988
  }
1897
- async setModel(handle, model, sessionMode = "persistent") {
1989
+ async setModel(handle, model, sessionMode = "persistent", authority) {
1898
1990
  const recordId = handle.acpxRecordId ?? handle.sessionKey;
1899
- await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setModelWithOwnership(handle, model, sessionMode));
1991
+ await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setModelWithOwnership(handle, model, sessionMode, authority), authority);
1900
1992
  }
1901
- async setModelWithOwnership(handle, model, sessionMode) {
1993
+ async setModelWithOwnership(handle, model, sessionMode, authority) {
1902
1994
  const record = await this.requireRecord(handle.acpxRecordId ?? handle.sessionKey);
1995
+ assertControlAuthority(authority);
1903
1996
  const controller = this.activeControllers.get(record.acpxRecordId);
1904
1997
  if (controller) {
1905
- const response = await controller.setSessionModel(model);
1998
+ const response = await controller.setSessionModel(model, authority);
1906
1999
  record.acpx = applyModelSelection(record.acpx, model, response);
1907
2000
  await this.options.sessionStore.save(record);
1908
2001
  return;
1909
2002
  }
1910
2003
  const result = await this.withRuntimeControlSession(record, sessionMode, async ({ client, sessionId, record: connectedRecord }) => {
1911
- const response = await client.setSessionModel(sessionId, model, advertisedModelState(connectedRecord.acpx));
2004
+ const response = await client.setSessionModel(sessionId, model, advertisedModelState(connectedRecord.acpx), authority);
1912
2005
  connectedRecord.acpx = applyModelSelection(connectedRecord.acpx, model, response);
1913
- }, { key: "model" });
2006
+ }, { key: "model" }, authority);
1914
2007
  await this.options.sessionStore.save(result.record);
1915
2008
  }
1916
- async setConfigOption(handle, key, value, sessionMode = "persistent") {
2009
+ async setConfigOption(handle, key, value, sessionMode = "persistent", authority) {
1917
2010
  const recordId = handle.acpxRecordId ?? handle.sessionKey;
1918
- return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setConfigOptionWithOwnership(handle, key, value, sessionMode));
2011
+ return await this.withManagerLock(this.runtimeOperationLocks, recordId, async () => this.setConfigOptionWithOwnership(handle, key, value, sessionMode, authority), authority);
1919
2012
  }
1920
- async setConfigOptionWithOwnership(handle, key, value, sessionMode) {
2013
+ async setConfigOptionWithOwnership(handle, key, value, sessionMode, authority) {
1921
2014
  const record = await this.requireRecord(handle.acpxRecordId ?? handle.sessionKey);
2015
+ assertControlAuthority(authority);
1922
2016
  const controller = this.activeControllers.get(record.acpxRecordId);
1923
2017
  if (controller) {
1924
- const { configId, response } = await controller.setResolvedSessionConfigOption(key, value);
2018
+ const { configId, response } = await controller.setResolvedSessionConfigOption(key, value, authority);
1925
2019
  record.acpx = applyConfigOptionSelection(record.acpx, configId, value, response);
1926
2020
  await this.options.sessionStore.save(record);
1927
2021
  return response;
@@ -1929,13 +2023,13 @@ var AcpRuntimeManager = class {
1929
2023
  const result = await this.withRuntimeControlSession(record, sessionMode, async ({ client, sessionId, record: connectedRecord }) => {
1930
2024
  const configId = resolveSupportedConfigOptionId(connectedRecord, key);
1931
2025
  const modelConfigId = advertisedModelState(connectedRecord.acpx)?.configId;
1932
- const response = await client.setSessionConfigOption(sessionId, configId, value);
2026
+ const response = await client.setSessionConfigOption(sessionId, configId, value, authority);
1933
2027
  connectedRecord.acpx = applyConfigOptionSelection(connectedRecord.acpx, configId, value, response, modelConfigId);
1934
2028
  return response;
1935
2029
  }, {
1936
2030
  key,
1937
2031
  resolve: (connectedRecord) => resolveSupportedConfigOptionId(connectedRecord, key)
1938
- });
2032
+ }, authority);
1939
2033
  await this.options.sessionStore.save(result.record);
1940
2034
  return result.value;
1941
2035
  }
@@ -2125,6 +2219,233 @@ function writeHandleState(handle, state) {
2125
2219
  handle.agentSessionId = state.agentSessionId;
2126
2220
  }
2127
2221
  //#endregion
2222
+ //#region src/runtime/shared.ts
2223
+ const SHARED_BACKEND = "acpx-shared";
2224
+ function invalidOption(message) {
2225
+ throw new AcpRuntimeError("ACP_INVALID_RUNTIME_OPTION", message);
2226
+ }
2227
+ function sharedHandle(record) {
2228
+ return {
2229
+ backend: SHARED_BACKEND,
2230
+ sessionKey: record.name ?? "",
2231
+ runtimeSessionName: record.acpxRecordId,
2232
+ acpxRecordId: record.acpxRecordId,
2233
+ backendSessionId: record.acpSessionId,
2234
+ agentSessionId: record.agentSessionId,
2235
+ cwd: record.cwd
2236
+ };
2237
+ }
2238
+ function sharedRecordId(handle) {
2239
+ if (handle.backend !== SHARED_BACKEND || !handle.acpxRecordId) return invalidOption("A shared runtime requires a handle from a shared session.");
2240
+ return handle.acpxRecordId;
2241
+ }
2242
+ function turnFailure(error) {
2243
+ const normalized = normalizeOutputError(error, {
2244
+ defaultCode: "RUNTIME",
2245
+ origin: "queue"
2246
+ });
2247
+ if (normalized.detailCode === "QUEUE_REQUEST_CANCELLED") return {
2248
+ status: "cancelled",
2249
+ stopReason: "cancelled"
2250
+ };
2251
+ return {
2252
+ status: "failed",
2253
+ error: {
2254
+ message: normalized.message,
2255
+ code: normalized.code,
2256
+ detailCode: normalized.detailCode,
2257
+ retryable: normalized.retryable
2258
+ }
2259
+ };
2260
+ }
2261
+ /** A client of the CLI's local session owner. Shutdown detaches this client. */
2262
+ var SharedAcpRuntime = class {
2263
+ registry;
2264
+ options;
2265
+ disconnect = new AbortController();
2266
+ pending = /* @__PURE__ */ new Set();
2267
+ constructor(options) {
2268
+ for (const name of [
2269
+ "sessionStore",
2270
+ "agentProcessEnv",
2271
+ "mcpServers",
2272
+ "sessionPermissions",
2273
+ "processLifecycle",
2274
+ "onPermissionRequest",
2275
+ "elicitationModes"
2276
+ ]) if (Object.hasOwn(options, name)) invalidOption(`${name} belongs to the in-process runtime and is not supported by shared sessions.`);
2277
+ this.options = {
2278
+ ...options,
2279
+ cwd: path.resolve(options.cwd),
2280
+ authCredentials: options.authCredentials ? { ...options.authCredentials } : void 0
2281
+ };
2282
+ this.registry = options.agentRegistry ?? createAgentRegistry();
2283
+ }
2284
+ assertOpen() {
2285
+ if (this.disconnect.signal.aborted) throw new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "Shared ACP runtime is shut down.");
2286
+ }
2287
+ track(task) {
2288
+ this.pending.add(task);
2289
+ task.then(() => this.pending.delete(task), () => this.pending.delete(task));
2290
+ return task;
2291
+ }
2292
+ sessionOptions(input) {
2293
+ if (!input.agent.trim()) invalidOption("ACP agent id is required.");
2294
+ return {
2295
+ ...normalizeAgentCommandInput(this.registry.resolve(input.agent)),
2296
+ cwd: path.resolve(input.cwd ?? this.options.cwd),
2297
+ name: input.sessionKey.trim() || void 0
2298
+ };
2299
+ }
2300
+ async findSession(input) {
2301
+ this.assertOpen();
2302
+ const record = await findSession(this.sessionOptions(input));
2303
+ return record ? sharedHandle(record) : void 0;
2304
+ }
2305
+ ensureSession(input) {
2306
+ this.assertOpen();
2307
+ if (input.mode !== "persistent") invalidOption("Shared sessions require persistent mode; use the in-process runtime for oneshot sessions.");
2308
+ const session = this.sessionOptions(input);
2309
+ return this.track(ensureSession({
2310
+ ...this.options,
2311
+ ...session,
2312
+ walkBoundary: session.cwd,
2313
+ sessionOptions: input.sessionOptions,
2314
+ resumeSessionId: input.resumeSessionId,
2315
+ handleProcessInterrupts: false,
2316
+ signal: this.disconnect.signal
2317
+ }).then(({ record }) => {
2318
+ this.assertOpen();
2319
+ return sharedHandle(record);
2320
+ }));
2321
+ }
2322
+ startTurn(input) {
2323
+ this.assertOpen();
2324
+ if (input.mode !== "prompt" || input.assertActive || input.onPermissionRequest || input.onElicitation) invalidOption("Shared turns support prompt mode and the client's static permission policy; in-process callbacks and steering are unsupported.");
2325
+ if (!input.requestId.trim()) invalidOption("A shared turn requires a requestId.");
2326
+ const sessionId = sharedRecordId(input.handle);
2327
+ const prompt = toPromptInput(input.text, input.attachments);
2328
+ const events = new AsyncEventQueue();
2329
+ const promptStarted = createDeferred();
2330
+ promptStarted.promise.catch(() => {});
2331
+ let accepted = false;
2332
+ let settled = false;
2333
+ let cancelRequested = input.signal?.aborted === true;
2334
+ let cancellation;
2335
+ const cancel = async () => {
2336
+ cancelRequested = true;
2337
+ if (!accepted || settled) return;
2338
+ cancellation ??= tryCancelOnRunningOwner({
2339
+ sessionId,
2340
+ targetRequestId: input.requestId
2341
+ }).then(() => {});
2342
+ await cancellation;
2343
+ };
2344
+ const onAbort = () => {
2345
+ cancel().catch(() => {});
2346
+ };
2347
+ input.signal?.addEventListener("abort", onAbort, { once: true });
2348
+ const run = async () => {
2349
+ if (cancelRequested) return {
2350
+ status: "cancelled",
2351
+ stopReason: "cancelled"
2352
+ };
2353
+ if ((await resolveSessionRecord(sessionId)).closed) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "Shared session is closed; ensure it again before submitting a turn.");
2354
+ if (cancelRequested) return {
2355
+ status: "cancelled",
2356
+ stopReason: "cancelled"
2357
+ };
2358
+ const result = await sendSession({
2359
+ ...this.options,
2360
+ sessionId,
2361
+ requestId: input.requestId,
2362
+ prompt: typeof prompt === "string" ? textPrompt(prompt) : prompt,
2363
+ resumePolicy: "same-session-only",
2364
+ requireSharedRuntime: true,
2365
+ signal: this.disconnect.signal,
2366
+ timeoutMs: input.timeoutMs ?? this.options.timeoutMs,
2367
+ queueOwnerArgs: [fileURLToPath(import.meta.resolve("acpx/dist/cli.js")), "__queue-owner"],
2368
+ onQueueAccepted: () => {
2369
+ accepted = true;
2370
+ if (cancelRequested) cancel().catch(() => {});
2371
+ },
2372
+ onPromptStarted: () => promptStarted.resolve(),
2373
+ outputFormatter: {
2374
+ ...DISCARD_OUTPUT_FORMATTER,
2375
+ onAcpMessage: (message) => {
2376
+ const event = parsePromptEventLine(JSON.stringify(message));
2377
+ if (event) events.push(event);
2378
+ }
2379
+ },
2380
+ errorEmissionPolicy: { queueErrorAlreadyEmitted: false },
2381
+ waitForCompletion: true
2382
+ });
2383
+ if ("queued" in result) throw new AcpRuntimeError("ACP_TURN_FAILED", "Shared turn ended without a completion result.");
2384
+ return {
2385
+ status: result.stopReason === "cancelled" ? "cancelled" : "completed",
2386
+ stopReason: result.stopReason,
2387
+ _meta: result._meta
2388
+ };
2389
+ };
2390
+ const result = this.track(run().catch(turnFailure).finally(() => {
2391
+ settled = true;
2392
+ promptStarted.reject(new AcpRuntimeError("ACP_TURN_FAILED", "Turn settled before its prompt was written."));
2393
+ input.signal?.removeEventListener("abort", onAbort);
2394
+ events.close();
2395
+ }));
2396
+ return {
2397
+ requestId: input.requestId,
2398
+ promptStarted: promptStarted.promise,
2399
+ events: events.iterate(),
2400
+ result,
2401
+ cancel,
2402
+ closeStream: async () => {
2403
+ events.clear();
2404
+ events.close();
2405
+ }
2406
+ };
2407
+ }
2408
+ async *runTurn(input) {
2409
+ const turn = this.startTurn(input);
2410
+ yield* turn.events;
2411
+ yield legacyTerminalEventFromTurnResult(await turn.result);
2412
+ }
2413
+ async getStatus(input) {
2414
+ this.assertOpen();
2415
+ return runtimeStatusFromRecord(await resolveSessionRecord(sharedRecordId(input.handle)));
2416
+ }
2417
+ watchSession(input) {
2418
+ this.assertOpen();
2419
+ const signal = input.signal ? AbortSignal.any([input.signal, this.disconnect.signal]) : this.disconnect.signal;
2420
+ const recordId = sharedRecordId(input.handle);
2421
+ const record = readSessionRecord(recordId).then((value) => {
2422
+ if (!value || value.acpxRecordId !== recordId) throw new AcpRuntimeError("ACP_SESSION_INIT_FAILED", "Shared session record is unavailable.");
2423
+ return value;
2424
+ });
2425
+ record.catch(() => {});
2426
+ return watchSession({
2427
+ record,
2428
+ cursor: input.cursor,
2429
+ signal
2430
+ });
2431
+ }
2432
+ async cancel(input) {
2433
+ this.assertOpen();
2434
+ await this.track(cancelSessionPrompt({ sessionId: sharedRecordId(input.handle) }));
2435
+ }
2436
+ async close(input) {
2437
+ this.assertOpen();
2438
+ await this.track(closeSession(sharedRecordId(input.handle)));
2439
+ }
2440
+ async shutdown() {
2441
+ this.disconnect.abort(new AcpRuntimeError("ACP_BACKEND_UNAVAILABLE", "Shared client detached; submitted work may still be running."));
2442
+ await Promise.allSettled(this.pending);
2443
+ }
2444
+ };
2445
+ function createSharedAcpRuntime(options) {
2446
+ return new SharedAcpRuntime(options);
2447
+ }
2448
+ //#endregion
2128
2449
  //#region src/runtime.ts
2129
2450
  const ACPX_BACKEND_ID = "acpx";
2130
2451
  const ACPX_CAPABILITIES = { controls: [
@@ -2232,6 +2553,7 @@ var AcpxRuntime = class {
2232
2553
  requestId: input.requestId,
2233
2554
  timeoutMs: input.timeoutMs,
2234
2555
  signal: input.signal,
2556
+ assertActive: input.assertActive,
2235
2557
  onElicitation: input.onElicitation,
2236
2558
  onPermissionRequest: input.onPermissionRequest
2237
2559
  }));
@@ -2265,6 +2587,7 @@ var AcpxRuntime = class {
2265
2587
  requestId: input.requestId,
2266
2588
  timeoutMs: input.timeoutMs,
2267
2589
  signal: input.signal,
2590
+ assertActive: input.assertActive,
2268
2591
  onElicitation: input.onElicitation,
2269
2592
  onPermissionRequest: input.onPermissionRequest
2270
2593
  });
@@ -2286,15 +2609,15 @@ var AcpxRuntime = class {
2286
2609
  }
2287
2610
  async setMode(input) {
2288
2611
  const { handle, state } = this.resolveManagerHandle(input.handle);
2289
- await (await this.getManager()).setMode(handle, input.mode, state.mode);
2612
+ await (await this.getManager()).setMode(handle, input.mode, state.mode, input);
2290
2613
  }
2291
2614
  async setModel(input) {
2292
2615
  const { handle, state } = this.resolveManagerHandle(input.handle);
2293
- await (await this.getManager()).setModel(handle, input.model, state.mode);
2616
+ await (await this.getManager()).setModel(handle, input.model, state.mode, input);
2294
2617
  }
2295
2618
  async setConfigOption(input) {
2296
2619
  const { handle, state } = this.resolveManagerHandle(input.handle);
2297
- return await (await this.getManager()).setConfigOption(handle, input.key, input.value, state.mode);
2620
+ return await (await this.getManager()).setConfigOption(handle, input.key, input.value, state.mode, input);
2298
2621
  }
2299
2622
  async cancel(input) {
2300
2623
  const { handle } = this.resolveManagerHandle(input.handle);
@@ -2377,6 +2700,6 @@ function createRuntimeStore(options) {
2377
2700
  return createFileSessionStore(options);
2378
2701
  }
2379
2702
  //#endregion
2380
- export { ACPX_BACKEND_ID, AcpRuntimeError, AcpxRuntime, DEFAULT_AGENT_NAME, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, REQUESTED_MODEL_UNSUPPORTED_REASONS, RequestedModelUnsupportedError, createAcpRuntime, createAgentRegistry, createFileSessionStore, createRuntimeStore, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, isAcpRuntimeError, isRequestedModelUnsupportedError };
2703
+ export { ACPX_BACKEND_ID, AcpRuntimeError, AcpxRuntime, DEFAULT_AGENT_NAME, REQUESTED_MODEL_UNSUPPORTED_ERROR_CODE, REQUESTED_MODEL_UNSUPPORTED_REASONS, RequestedModelUnsupportedError, SharedAcpRuntime, createAcpRuntime, createAgentRegistry, createFileSessionStore, createRuntimeStore, createSharedAcpRuntime, decodeAcpxRuntimeHandleState, encodeAcpxRuntimeHandleState, inspectAgentModels, isAcpRuntimeError, isRequestedModelUnsupportedError };
2381
2704
 
2382
2705
  //# sourceMappingURL=runtime.js.map