@moxt-ai/mobius 0.0.4 → 0.0.6

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 (115) hide show
  1. package/dist/acp/native-session.d.ts +30 -0
  2. package/dist/acp/native-session.d.ts.map +1 -0
  3. package/dist/acp/native-session.js +25 -0
  4. package/dist/acp/native-session.js.map +1 -0
  5. package/dist/acp/session-history.d.ts +9 -0
  6. package/dist/acp/session-history.d.ts.map +1 -0
  7. package/dist/acp/session-history.js +69 -0
  8. package/dist/acp/session-history.js.map +1 -0
  9. package/dist/acp/session-manager.d.ts +16 -5
  10. package/dist/acp/session-manager.d.ts.map +1 -1
  11. package/dist/acp/session-manager.js +226 -41
  12. package/dist/acp/session-manager.js.map +1 -1
  13. package/dist/acp/session-store.d.ts +9 -0
  14. package/dist/acp/session-store.d.ts.map +1 -1
  15. package/dist/acp/session-store.js +43 -0
  16. package/dist/acp/session-store.js.map +1 -1
  17. package/dist/agent-catalog/agent-diagnostics.d.ts +3 -0
  18. package/dist/agent-catalog/agent-diagnostics.d.ts.map +1 -0
  19. package/dist/agent-catalog/agent-diagnostics.js +62 -0
  20. package/dist/agent-catalog/agent-diagnostics.js.map +1 -0
  21. package/dist/agent-catalog/discover-local-agents.d.ts +77 -0
  22. package/dist/agent-catalog/discover-local-agents.d.ts.map +1 -0
  23. package/dist/agent-catalog/discover-local-agents.js +467 -0
  24. package/dist/agent-catalog/discover-local-agents.js.map +1 -0
  25. package/dist/agent-catalog/local-agent.d.ts +38 -0
  26. package/dist/agent-catalog/local-agent.d.ts.map +1 -0
  27. package/dist/agent-catalog/local-agent.js +93 -0
  28. package/dist/agent-catalog/local-agent.js.map +1 -0
  29. package/dist/agent-catalog/publish-agent-catalog.d.ts +3 -0
  30. package/dist/agent-catalog/publish-agent-catalog.d.ts.map +1 -0
  31. package/dist/agent-catalog/publish-agent-catalog.js +58 -0
  32. package/dist/agent-catalog/publish-agent-catalog.js.map +1 -0
  33. package/dist/agent-installation/agent-installation-manager.d.ts +60 -0
  34. package/dist/agent-installation/agent-installation-manager.d.ts.map +1 -0
  35. package/dist/agent-installation/agent-installation-manager.js +242 -0
  36. package/dist/agent-installation/agent-installation-manager.js.map +1 -0
  37. package/dist/agent-installation/automatic-adapter-provisioner.d.ts +18 -0
  38. package/dist/agent-installation/automatic-adapter-provisioner.d.ts.map +1 -0
  39. package/dist/agent-installation/automatic-adapter-provisioner.js +116 -0
  40. package/dist/agent-installation/automatic-adapter-provisioner.js.map +1 -0
  41. package/dist/agent-installation/fixed-agent-install-recipes.d.ts +20 -0
  42. package/dist/agent-installation/fixed-agent-install-recipes.d.ts.map +1 -0
  43. package/dist/agent-installation/fixed-agent-install-recipes.js +163 -0
  44. package/dist/agent-installation/fixed-agent-install-recipes.js.map +1 -0
  45. package/dist/agent-installation/fixed-installation-executor.d.ts +20 -0
  46. package/dist/agent-installation/fixed-installation-executor.d.ts.map +1 -0
  47. package/dist/agent-installation/fixed-installation-executor.js +204 -0
  48. package/dist/agent-installation/fixed-installation-executor.js.map +1 -0
  49. package/dist/agent-installation/installation-grants.d.ts +2 -0
  50. package/dist/agent-installation/installation-grants.d.ts.map +1 -0
  51. package/dist/agent-installation/installation-grants.js +52 -0
  52. package/dist/agent-installation/installation-grants.js.map +1 -0
  53. package/dist/agent-installation/local-installation-authorizer.d.ts +7 -0
  54. package/dist/agent-installation/local-installation-authorizer.d.ts.map +1 -0
  55. package/dist/agent-installation/local-installation-authorizer.js +32 -0
  56. package/dist/agent-installation/local-installation-authorizer.js.map +1 -0
  57. package/dist/agent-installation/managed-adapter-installer.d.ts +20 -0
  58. package/dist/agent-installation/managed-adapter-installer.d.ts.map +1 -0
  59. package/dist/agent-installation/managed-adapter-installer.js +159 -0
  60. package/dist/agent-installation/managed-adapter-installer.js.map +1 -0
  61. package/dist/agent-installation/verified-download.d.ts +24 -0
  62. package/dist/agent-installation/verified-download.d.ts.map +1 -0
  63. package/dist/agent-installation/verified-download.js +158 -0
  64. package/dist/agent-installation/verified-download.js.map +1 -0
  65. package/dist/audit/audit-store.d.ts +1 -0
  66. package/dist/audit/audit-store.d.ts.map +1 -1
  67. package/dist/audit/audit-store.js +12 -4
  68. package/dist/audit/audit-store.js.map +1 -1
  69. package/dist/authorization/local-authorization-error.d.ts +5 -0
  70. package/dist/authorization/local-authorization-error.d.ts.map +1 -0
  71. package/dist/authorization/local-authorization-error.js +8 -0
  72. package/dist/authorization/local-authorization-error.js.map +1 -0
  73. package/dist/authorization/workspace-directory.d.ts +16 -5
  74. package/dist/authorization/workspace-directory.d.ts.map +1 -1
  75. package/dist/authorization/workspace-directory.js +109 -119
  76. package/dist/authorization/workspace-directory.js.map +1 -1
  77. package/dist/cli.js +6289 -2124
  78. package/dist/cli.js.map +4 -4
  79. package/dist/config.d.ts +8 -12
  80. package/dist/config.d.ts.map +1 -1
  81. package/dist/config.js +14 -55
  82. package/dist/config.js.map +1 -1
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +4894 -852
  86. package/dist/index.js.map +4 -4
  87. package/dist/native-sessions/codex-history.d.ts +11 -0
  88. package/dist/native-sessions/codex-history.d.ts.map +1 -0
  89. package/dist/native-sessions/codex-history.js +223 -0
  90. package/dist/native-sessions/codex-history.js.map +1 -0
  91. package/dist/native-sessions/service.d.ts +14 -0
  92. package/dist/native-sessions/service.d.ts.map +1 -0
  93. package/dist/native-sessions/service.js +249 -0
  94. package/dist/native-sessions/service.js.map +1 -0
  95. package/dist/pairing/pair-local-machine.d.ts +2 -2
  96. package/dist/pairing/pair-local-machine.d.ts.map +1 -1
  97. package/dist/pairing/pair-local-machine.js +65 -41
  98. package/dist/pairing/pair-local-machine.js.map +1 -1
  99. package/dist/process/executable.d.ts +40 -0
  100. package/dist/process/executable.d.ts.map +1 -0
  101. package/dist/process/executable.js +305 -0
  102. package/dist/process/executable.js.map +1 -0
  103. package/dist/run-daemon.d.ts +1 -1
  104. package/dist/run-daemon.d.ts.map +1 -1
  105. package/dist/run-daemon.js +431 -180
  106. package/dist/run-daemon.js.map +1 -1
  107. package/dist/storage/state-directory.d.ts +2 -0
  108. package/dist/storage/state-directory.d.ts.map +1 -0
  109. package/dist/storage/state-directory.js +10 -0
  110. package/dist/storage/state-directory.js.map +1 -0
  111. package/package.json +5 -6
  112. package/dist/authorization/authorize-binding.d.ts +0 -6
  113. package/dist/authorization/authorize-binding.d.ts.map +0 -1
  114. package/dist/authorization/authorize-binding.js +0 -30
  115. package/dist/authorization/authorize-binding.js.map +0 -1
@@ -1,10 +1,12 @@
1
1
  import { spawn } from "node:child_process";
2
- import { once } from "node:events";
3
2
  import { methods, ndJsonStream, PROTOCOL_VERSION, } from "@agentclientprotocol/sdk";
4
- import { AGENT_CONTENT_KIND, MOBIUS_PROTOCOL_VERSION, SESSION_COMMANDS_UPDATED_KIND, SESSION_CONFIG_UPDATED_KIND, SESSION_ELICITATION_REQUESTED_KIND, SESSION_ELICITATION_RESOLVED_KIND, SESSION_INFO_UPDATED_KIND, SESSION_PERMISSION_REQUESTED_KIND, SESSION_PERMISSION_RESOLVED_KIND, SESSION_USAGE_UPDATED_KIND, } from "@moxt-ai/mobius-protocol";
3
+ import { AGENT_CONTENT_KIND, decodeAgentPromptResponse, MOBIUS_PROTOCOL_VERSION, SESSION_COMMANDS_UPDATED_KIND, SESSION_CONFIG_UPDATED_KIND, SESSION_ELICITATION_REQUESTED_KIND, SESSION_ELICITATION_RESOLVED_KIND, SESSION_INFO_UPDATED_KIND, SESSION_PERMISSION_REQUESTED_KIND, SESSION_PERMISSION_RESOLVED_KIND, SESSION_USAGE_UPDATED_KIND, } from "@moxt-ai/mobius-protocol";
4
+ import { revalidateExecutable, stopProcessTree, } from "../process/executable.js";
5
5
  import { AcpActivityTracker, isProgressMessage, normalizeContentBlock, } from "./activity.js";
6
6
  import { createAcpClient } from "./create-client.js";
7
- import { AcpStoredSession, FoundAcpStoredSession, MemoryAcpSessionStore, } from "./session-store.js";
7
+ import { nativeSessionIdSchema, nativeSessionPageSchema, } from "./native-session.js";
8
+ import { AcpHistoryReplay } from "./session-history.js";
9
+ import { AcpStoredSession, DeletedAcpStoredSession, FoundAcpStoredSession, MemoryAcpSessionStore, } from "./session-store.js";
8
10
  const PROCESS_STOP_TIMEOUT_MILLISECONDS = 1_000;
9
11
  const SESSION_CONTROL_COMMAND_ID = "session-control";
10
12
  export class NullAcpSessionObserver {
@@ -117,31 +119,6 @@ export function createWritableAcpStream(output) {
117
119
  },
118
120
  });
119
121
  }
120
- async function stopAgentProcess(child) {
121
- child.stdin?.end();
122
- if (child.exitCode !== null) {
123
- return;
124
- }
125
- child.kill("SIGTERM");
126
- try {
127
- await once(child, "exit", {
128
- signal: AbortSignal.timeout(PROCESS_STOP_TIMEOUT_MILLISECONDS),
129
- });
130
- }
131
- catch {
132
- if (child.exitCode === null) {
133
- child.kill("SIGKILL");
134
- try {
135
- await once(child, "exit", {
136
- signal: AbortSignal.timeout(PROCESS_STOP_TIMEOUT_MILLISECONDS),
137
- });
138
- }
139
- catch {
140
- // Teardown must not wait forever for a process the OS did not reap.
141
- }
142
- }
143
- }
144
- }
145
122
  function truncate(value, maximumLength) {
146
123
  return value.length <= maximumLength
147
124
  ? value
@@ -299,6 +276,7 @@ function normalizeCapabilities(capabilities) {
299
276
  listSessions: session?.list != null,
300
277
  loadSession: capabilities.loadSession === true,
301
278
  resumeSession: session?.resume != null,
279
+ unstableForkSession: session?.fork != null,
302
280
  };
303
281
  }
304
282
  function elicitationFieldType(value) {
@@ -607,14 +585,17 @@ class AcpProcessSession {
607
585
  listSessions: false,
608
586
  loadSession: false,
609
587
  resumeSession: false,
588
+ unstableForkSession: false,
610
589
  };
611
590
  #commands = [];
612
591
  #configOptions = [];
613
592
  #nativeSessionId = "";
614
593
  #prompted = false;
615
- #replaying = false;
594
+ #replay = {
595
+ kind: "idle",
596
+ };
616
597
  #restored = false;
617
- #sessionTitle = "";
598
+ #sessionTitle = null;
618
599
  #sessionUpdatedAt = "";
619
600
  #turn = new IdleAcpTurn();
620
601
  #updateTail = Promise.resolve();
@@ -625,8 +606,11 @@ class AcpProcessSession {
625
606
  this.#getObserver = getObserver;
626
607
  this.#mobiusSessionId = mobiusSessionId;
627
608
  this.#store = store;
628
- this.#child = spawn(agent.command, [...agent.arguments], {
609
+ this.#child = spawn(agent.adapterPath, [...agent.arguments], {
629
610
  cwd,
611
+ detached: process.platform !== "win32",
612
+ env: { ...process.env, ...agent.environment },
613
+ shell: false,
630
614
  stdio: ["pipe", "pipe", "pipe"],
631
615
  });
632
616
  const stdin = this.#child.stdin;
@@ -649,13 +633,22 @@ class AcpProcessSession {
649
633
  }
650
634
  static start = async (options, getObserver, store) => {
651
635
  options.signal.throwIfAborted();
636
+ const [adapterUnchanged, agentUnchanged] = await Promise.all([
637
+ revalidateExecutable(options.agent.adapterIdentity),
638
+ revalidateExecutable(options.agent.agentIdentity),
639
+ ]);
640
+ options.signal.throwIfAborted();
641
+ if (!adapterUnchanged || !agentUnchanged) {
642
+ throw new AcpExecutionError("The local Agent runtime changed; restart Mobius before starting a new conversation", {}, "agent_runtime_changed");
643
+ }
652
644
  const runtime = new AcpProcessSession(options.agent, options.cwd, options.additionalDirectories, options.sessionId, getObserver, store);
653
645
  try {
654
- await runtime.initialize(options.signal);
646
+ await runtime.initialize(options.signal, options.origin);
655
647
  return runtime;
656
648
  }
657
649
  catch (error) {
658
650
  await runtime.closeProcess();
651
+ options.signal.throwIfAborted();
659
652
  if (error instanceof AcpExecutionError) {
660
653
  throw error;
661
654
  }
@@ -669,14 +662,34 @@ class AcpProcessSession {
669
662
  arraysEqual(this.#additionalDirectories, additionalDirectories);
670
663
  canRebind = () => !this.#prompted && !this.#turn.running;
671
664
  prepared = () => new AcpPreparedSession(this.#agentName, this.#agentVersion, this.#capabilities, this.#commands, this.#configOptions, this.#restored);
672
- initialize = async (signal) => {
665
+ loadHistory = async (signal) => {
666
+ if (!this.#capabilities.loadSession)
667
+ return { status: "unavailable" };
668
+ const history = new AcpHistoryReplay();
669
+ this.#replay = { kind: "capture", history };
670
+ try {
671
+ await this.race(this.#connection.agent.request(methods.agent.session.load, {
672
+ additionalDirectories: [...this.#additionalDirectories],
673
+ cwd: this.#cwd,
674
+ mcpServers: [],
675
+ sessionId: this.#nativeSessionId,
676
+ }, { cancellationSignal: signal }), "ACP agent exited while loading history", signal);
677
+ await this.#updateTail;
678
+ signal.throwIfAborted();
679
+ return history.snapshot();
680
+ }
681
+ finally {
682
+ this.#replay = { kind: "idle" };
683
+ }
684
+ };
685
+ initialize = async (signal, origin) => {
673
686
  const initialized = await this.race(this.#connection.agent.request(methods.agent.initialize, {
674
687
  clientCapabilities: {
675
688
  elicitation: { form: {}, url: {} },
676
689
  plan: {},
677
690
  session: { configOptions: { boolean: {} } },
678
691
  },
679
- clientInfo: { name: "mobius", version: "0.0.2" },
692
+ clientInfo: { name: "mobius", version: "0.0.3" },
680
693
  protocolVersion: PROTOCOL_VERSION,
681
694
  }, { cancellationSignal: signal }), "ACP agent exited during initialization", signal);
682
695
  if (initialized.protocolVersion !== PROTOCOL_VERSION) {
@@ -688,9 +701,31 @@ class AcpProcessSession {
688
701
  initialized.agentInfo?.name ??
689
702
  this.#agentId, 255);
690
703
  this.#agentVersion = truncate(initialized.agentInfo?.version ?? "", 128);
691
- const stored = this.#store.read(this.#mobiusSessionId);
704
+ const stored = origin?.kind === "import"
705
+ ? new FoundAcpStoredSession(new AcpStoredSession(this.#mobiusSessionId, this.#agentId, nativeSessionIdSchema.parse(origin.nativeSessionId), this.#cwd, this.#additionalDirectories, true))
706
+ : this.#store.read(this.#mobiusSessionId);
707
+ if (stored instanceof DeletedAcpStoredSession)
708
+ throw new AcpExecutionError("The native Agent session was deleted. Start a new chat.", {}, "native_session_deleted");
692
709
  let lifecycleResponse;
693
- if (stored instanceof FoundAcpStoredSession &&
710
+ if (origin?.kind === "fork") {
711
+ if (!this.#capabilities.unstableForkSession)
712
+ throw new AcpExecutionError("This Agent does not support session forking", {}, "native_session_unsupported");
713
+ const forked = await this.race(this.#connection.agent.request(methods.agent.session.fork, {
714
+ sessionId: nativeSessionIdSchema.parse(origin.nativeSessionId),
715
+ cwd: this.#cwd,
716
+ additionalDirectories: [...this.#additionalDirectories],
717
+ mcpServers: [],
718
+ }, { cancellationSignal: signal }), "ACP agent exited while forking a session", signal);
719
+ this.#nativeSessionId = nativeSessionIdSchema.parse(forked.sessionId);
720
+ if (this.#nativeSessionId === origin.nativeSessionId ||
721
+ this.#store.findNative(this.#agentId, this.#nativeSessionId).length > 0) {
722
+ throw new AcpExecutionError("The Agent did not create an independent fork", {}, "invalid_native_session_response");
723
+ }
724
+ this.#prompted = true;
725
+ this.#restored = true;
726
+ lifecycleResponse = forked;
727
+ }
728
+ else if (stored instanceof FoundAcpStoredSession &&
694
729
  stored.session.agentId === this.#agentId &&
695
730
  stored.session.cwd === this.#cwd &&
696
731
  arraysEqual(stored.session.additionalDirectories, this.#additionalDirectories)) {
@@ -706,7 +741,7 @@ class AcpProcessSession {
706
741
  }, { cancellationSignal: signal }), "ACP agent exited while resuming a session", signal);
707
742
  }
708
743
  else if (this.#capabilities.loadSession) {
709
- this.#replaying = true;
744
+ this.#replay = { kind: "discard" };
710
745
  try {
711
746
  lifecycleResponse = await this.race(this.#connection.agent.request(methods.agent.session.load, {
712
747
  additionalDirectories: [...this.#additionalDirectories],
@@ -716,7 +751,8 @@ class AcpProcessSession {
716
751
  }, { cancellationSignal: signal }), "ACP agent exited while loading a session", signal);
717
752
  }
718
753
  finally {
719
- this.#replaying = false;
754
+ await this.#updateTail;
755
+ this.#replay = { kind: "idle" };
720
756
  }
721
757
  }
722
758
  else {
@@ -745,7 +781,23 @@ class AcpProcessSession {
745
781
  this.#configOptions = normalizeConfigOptions(lifecycleRecord["configOptions"], lifecycleRecord["modes"]);
746
782
  await this.#updateTail;
747
783
  signal.throwIfAborted();
784
+ this.#store.write(new AcpStoredSession(this.#mobiusSessionId, this.#agentId, this.#nativeSessionId, this.#cwd, this.#additionalDirectories, this.#prompted));
785
+ };
786
+ listNativeSessions = async (cursor, signal) => {
787
+ if (!this.#capabilities.listSessions)
788
+ throw new AcpExecutionError("This Agent does not support native session listing", {}, "native_session_unsupported");
789
+ const response = await this.race(this.#connection.agent.request(methods.agent.session.list, cursor === null ? {} : { cursor }, { cancellationSignal: signal }), "ACP agent exited while listing sessions", signal);
790
+ const decoded = nativeSessionPageSchema.safeParse(response);
791
+ if (!decoded.success)
792
+ throw new AcpExecutionError("The Agent returned an invalid native session list", { cause: decoded.error }, "invalid_native_session_response");
793
+ return decoded.data;
794
+ };
795
+ deleteNativeSession = async (nativeSessionId, signal) => {
796
+ if (!this.#capabilities.deleteSession || !this.#capabilities.listSessions)
797
+ throw new AcpExecutionError("This Agent does not support native session deletion", {}, "native_session_unsupported");
798
+ await this.race(this.#connection.agent.request(methods.agent.session.delete, { sessionId: nativeSessionIdSchema.parse(nativeSessionId) }, { cancellationSignal: signal }), "ACP agent exited while deleting a session", signal);
748
799
  };
800
+ nativeSessionId = () => this.#nativeSessionId;
749
801
  configure = async (options) => {
750
802
  const existing = this.#configOptions.find((option) => option.id === options.configId);
751
803
  if (existing === undefined || existing.type !== options.valueType) {
@@ -835,7 +887,25 @@ class AcpProcessSession {
835
887
  const response = await this.race(this.#connection.agent.request(methods.agent.session.prompt, { prompt, sessionId: this.#nativeSessionId }, { cancellationSignal: options.signal }), "ACP agent exited during a prompt", options.signal);
836
888
  await this.#updateTail;
837
889
  await running.activities.finish();
838
- return { stopReason: response.stopReason };
890
+ let result;
891
+ try {
892
+ result = decodeAgentPromptResponse(response);
893
+ }
894
+ catch (cause) {
895
+ throw new AcpExecutionError("ACP agent returned an invalid prompt response", { cause }, "acp_invalid_prompt_response");
896
+ }
897
+ if (result.usage != null || result._meta?.quota !== undefined) {
898
+ await this.#getObserver().onUsage({
899
+ _meta: result._meta ?? null,
900
+ commandId: options.commandId,
901
+ kind: SESSION_USAGE_UPDATED_KIND,
902
+ protocolVersion: MOBIUS_PROTOCOL_VERSION,
903
+ sessionId: this.#mobiusSessionId,
904
+ source: "prompt",
905
+ usage: result.usage ?? null,
906
+ });
907
+ }
908
+ return result;
839
909
  }
840
910
  catch (error) {
841
911
  await running.activities.finish();
@@ -1024,7 +1094,9 @@ class AcpProcessSession {
1024
1094
  processUpdate = async (notification) => {
1025
1095
  this.assertNativeSession(notification.sessionId);
1026
1096
  const update = notification.update;
1027
- if (this.#replaying &&
1097
+ if (this.#replay.kind === "capture")
1098
+ this.#replay.history.record(update);
1099
+ if (this.#replay.kind !== "idle" &&
1028
1100
  (update.sessionUpdate === "user_message_chunk" ||
1029
1101
  update.sessionUpdate === "agent_message_chunk" ||
1030
1102
  update.sessionUpdate === "agent_thought_chunk" ||
@@ -1140,6 +1212,7 @@ class AcpProcessSession {
1140
1212
  protocolVersion: MOBIUS_PROTOCOL_VERSION,
1141
1213
  sessionId: this.#mobiusSessionId,
1142
1214
  size: Math.max(0, Math.trunc(update.size)),
1215
+ source: "context",
1143
1216
  used: Math.max(0, Math.trunc(update.used)),
1144
1217
  });
1145
1218
  }
@@ -1160,7 +1233,7 @@ class AcpProcessSession {
1160
1233
  };
1161
1234
  closeProcess = async () => {
1162
1235
  this.#connection.close();
1163
- await stopAgentProcess(this.#child);
1236
+ await stopProcessTree(this.#child);
1164
1237
  };
1165
1238
  race = async (operation, exitMessage, signal) => {
1166
1239
  signal.throwIfAborted();
@@ -1253,6 +1326,7 @@ class AcpProcessSession {
1253
1326
  };
1254
1327
  }
1255
1328
  export class AcpSessionManager {
1329
+ #nativeOperations = new Set();
1256
1330
  #activeSessionIds = new Set();
1257
1331
  #cancellationRequests = new Set();
1258
1332
  #preparing = new Map();
@@ -1298,6 +1372,12 @@ export class AcpSessionManager {
1298
1372
  return await operation;
1299
1373
  }
1300
1374
  prepareSession = async (options) => {
1375
+ const stored = this.#store.read(options.sessionId);
1376
+ if (stored instanceof DeletedAcpStoredSession)
1377
+ throw new AcpExecutionError("The native Agent session was deleted. Start a new chat.", {}, "native_session_deleted");
1378
+ if (stored instanceof FoundAcpStoredSession &&
1379
+ this.#nativeOperations.has(`${stored.session.agentId}:${stored.session.nativeSessionId}`))
1380
+ throw new AcpExecutionError("The native session is being managed", {}, "session_busy");
1301
1381
  const pending = this.#preparing.get(options.sessionId);
1302
1382
  if (pending !== undefined) {
1303
1383
  const runtime = await pending;
@@ -1332,6 +1412,11 @@ export class AcpSessionManager {
1332
1412
  }
1333
1413
  };
1334
1414
  runPrompt = async (options) => {
1415
+ const stored = this.#store.read(options.sessionId);
1416
+ if (stored instanceof FoundAcpStoredSession &&
1417
+ this.#nativeOperations.has(`${stored.session.agentId}:${stored.session.nativeSessionId}`)) {
1418
+ throw new AcpExecutionError("The native session is being managed", {}, "session_busy");
1419
+ }
1335
1420
  if (this.#activeSessionIds.has(options.sessionId)) {
1336
1421
  throw new AcpExecutionError("Another prompt is already running in this conversation", {}, "session_busy");
1337
1422
  }
@@ -1341,6 +1426,8 @@ export class AcpSessionManager {
1341
1426
  return { stopReason: "cancelled" };
1342
1427
  }
1343
1428
  const runtime = await this.ensureSession(options);
1429
+ if (this.#nativeOperations.has(`${options.agent.id}:${runtime.nativeSessionId()}`))
1430
+ throw new AcpExecutionError("The native session is being managed", {}, "session_busy");
1344
1431
  if (this.#cancellationRequests.has(options.sessionId)) {
1345
1432
  return { stopReason: "cancelled" };
1346
1433
  }
@@ -1398,5 +1485,103 @@ export class AcpSessionManager {
1398
1485
  }
1399
1486
  return started;
1400
1487
  };
1488
+ listNativeSessions = async (options, cursor) => await (await this.ensureSession(options)).listNativeSessions(cursor, options.signal);
1489
+ openNativeSession = async (options, origin) => {
1490
+ const key = `${options.agent.id}:${origin.nativeSessionId}`;
1491
+ if (this.#nativeOperations.has(key))
1492
+ throw new AcpExecutionError("The native session is being managed", {}, "session_busy");
1493
+ const linked = this.#store.findNative(options.agent.id, origin.nativeSessionId);
1494
+ if (linked.some((entry) => this.#activeSessionIds.has(entry.sessionId) ||
1495
+ this.#preparing.has(entry.sessionId)))
1496
+ throw new AcpExecutionError("Wait for the native session's running operation to finish", {}, "session_busy");
1497
+ const existing = linked[0];
1498
+ if (origin.kind === "import" && existing !== undefined) {
1499
+ const runtime = await this.ensureSession({
1500
+ ...options,
1501
+ sessionId: existing.sessionId,
1502
+ });
1503
+ if (this.#activeSessionIds.has(existing.sessionId) ||
1504
+ this.#nativeOperations.has(key))
1505
+ throw new AcpExecutionError("Wait for the native session's running operation to finish", {}, "session_busy");
1506
+ this.#nativeOperations.add(key);
1507
+ try {
1508
+ const history = await runtime.loadHistory(options.signal);
1509
+ return {
1510
+ prepared: runtime.prepared(),
1511
+ sessionId: existing.sessionId,
1512
+ history,
1513
+ };
1514
+ }
1515
+ catch (error) {
1516
+ this.#sessions.delete(existing.sessionId);
1517
+ await runtime.closeProcess();
1518
+ throw error;
1519
+ }
1520
+ finally {
1521
+ this.#nativeOperations.delete(key);
1522
+ }
1523
+ }
1524
+ if (this.#sessions.has(options.sessionId) ||
1525
+ this.#preparing.has(options.sessionId) ||
1526
+ this.#store.read(options.sessionId) instanceof FoundAcpStoredSession ||
1527
+ this.#store.read(options.sessionId) instanceof DeletedAcpStoredSession)
1528
+ throw new AcpExecutionError("The destination conversation already exists", {}, "session_identity_mismatch");
1529
+ this.#nativeOperations.add(key);
1530
+ try {
1531
+ const runtime = await this.#startPreparation({ ...options, origin });
1532
+ const openedKey = `${options.agent.id}:${runtime.nativeSessionId()}`;
1533
+ this.#nativeOperations.add(openedKey);
1534
+ try {
1535
+ const history = await runtime.loadHistory(options.signal);
1536
+ return {
1537
+ sessionId: options.sessionId,
1538
+ prepared: runtime.prepared(),
1539
+ history,
1540
+ };
1541
+ }
1542
+ catch (error) {
1543
+ this.#sessions.delete(options.sessionId);
1544
+ this.#store.remove(options.sessionId);
1545
+ await runtime.closeProcess();
1546
+ throw error;
1547
+ }
1548
+ finally {
1549
+ this.#nativeOperations.delete(openedKey);
1550
+ }
1551
+ }
1552
+ finally {
1553
+ this.#nativeOperations.delete(key);
1554
+ }
1555
+ };
1556
+ deleteNativeSession = async (options, nativeSessionId) => {
1557
+ const runtime = await this.ensureSession(options);
1558
+ const key = `${options.agent.id}:${nativeSessionId}`;
1559
+ const linked = this.#store.findNative(options.agent.id, nativeSessionId);
1560
+ if (this.#nativeOperations.has(key) ||
1561
+ linked.some((entry) => this.#activeSessionIds.has(entry.sessionId) ||
1562
+ this.#preparing.has(entry.sessionId)))
1563
+ throw new AcpExecutionError("Wait for the native session's running operation to finish", {}, "session_busy");
1564
+ this.#nativeOperations.add(key);
1565
+ try {
1566
+ for (const entry of linked) {
1567
+ const active = this.#sessions.get(entry.sessionId);
1568
+ if (active !== undefined && active !== runtime) {
1569
+ this.#sessions.delete(entry.sessionId);
1570
+ await active.close();
1571
+ }
1572
+ }
1573
+ options.signal.throwIfAborted();
1574
+ await runtime.deleteNativeSession(nativeSessionId, options.signal);
1575
+ const deleted = this.#store.markDeleted(options.agent.id, nativeSessionId);
1576
+ if (runtime.nativeSessionId() === nativeSessionId) {
1577
+ this.#sessions.delete(options.sessionId);
1578
+ await runtime.close();
1579
+ }
1580
+ return deleted;
1581
+ }
1582
+ finally {
1583
+ this.#nativeOperations.delete(key);
1584
+ }
1585
+ };
1401
1586
  }
1402
1587
  //# sourceMappingURL=session-manager.js.map