@moxt-ai/mobius 0.0.17 → 0.0.19

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 (196) hide show
  1. package/dist/acp/activity.d.ts.map +1 -1
  2. package/dist/acp/activity.js +4 -12
  3. package/dist/acp/activity.js.map +1 -1
  4. package/dist/acp/create-client.d.ts.map +1 -1
  5. package/dist/acp/create-client.js.map +1 -1
  6. package/dist/acp/native-session.d.ts.map +1 -1
  7. package/dist/acp/native-session.js +1 -3
  8. package/dist/acp/native-session.js.map +1 -1
  9. package/dist/acp/run-prompt.d.ts.map +1 -1
  10. package/dist/acp/run-prompt.js +1 -1
  11. package/dist/acp/run-prompt.js.map +1 -1
  12. package/dist/acp/session-capabilities.d.ts +5 -0
  13. package/dist/acp/session-capabilities.d.ts.map +1 -0
  14. package/dist/acp/session-capabilities.js +31 -0
  15. package/dist/acp/session-capabilities.js.map +1 -0
  16. package/dist/acp/session-config/grok-build-session-config.d.ts.map +1 -1
  17. package/dist/acp/session-config/grok-build-session-config.js +6 -11
  18. package/dist/acp/session-config/grok-build-session-config.js.map +1 -1
  19. package/dist/acp/session-config/session-config-protocol.d.ts.map +1 -1
  20. package/dist/acp/session-config/session-config-protocol.js +4 -12
  21. package/dist/acp/session-config/session-config-protocol.js.map +1 -1
  22. package/dist/acp/session-config/session-config-registry.d.ts.map +1 -1
  23. package/dist/acp/session-config/session-config-registry.js +2 -4
  24. package/dist/acp/session-config/session-config-registry.js.map +1 -1
  25. package/dist/acp/session-history.d.ts.map +1 -1
  26. package/dist/acp/session-history.js +7 -8
  27. package/dist/acp/session-history.js.map +1 -1
  28. package/dist/acp/session-manager.d.ts.map +1 -1
  29. package/dist/acp/session-manager.js +59 -96
  30. package/dist/acp/session-manager.js.map +1 -1
  31. package/dist/acp/session-store.d.ts.map +1 -1
  32. package/dist/acp/session-store.js +12 -24
  33. package/dist/acp/session-store.js.map +1 -1
  34. package/dist/agent-catalog/acp-readiness-probe.d.ts +7 -1
  35. package/dist/agent-catalog/acp-readiness-probe.d.ts.map +1 -1
  36. package/dist/agent-catalog/acp-readiness-probe.js +13 -3
  37. package/dist/agent-catalog/acp-readiness-probe.js.map +1 -1
  38. package/dist/agent-catalog/agent-diagnostics.d.ts.map +1 -1
  39. package/dist/agent-catalog/agent-diagnostics.js.map +1 -1
  40. package/dist/agent-catalog/agent-discovery-context.d.ts.map +1 -1
  41. package/dist/agent-catalog/agent-discovery.d.ts +19 -9
  42. package/dist/agent-catalog/agent-discovery.d.ts.map +1 -1
  43. package/dist/agent-catalog/agent-discovery.js +32 -16
  44. package/dist/agent-catalog/agent-discovery.js.map +1 -1
  45. package/dist/agent-catalog/agent-probe-error.d.ts.map +1 -1
  46. package/dist/agent-catalog/agent-probe-error.js.map +1 -1
  47. package/dist/agent-catalog/agent-specification.d.ts +0 -1
  48. package/dist/agent-catalog/agent-specification.d.ts.map +1 -1
  49. package/dist/agent-catalog/agent-specification.js +2 -6
  50. package/dist/agent-catalog/agent-specification.js.map +1 -1
  51. package/dist/agent-catalog/agents/claude-code-agent.d.ts.map +1 -1
  52. package/dist/agent-catalog/agents/claude-code-agent.js +1 -8
  53. package/dist/agent-catalog/agents/claude-code-agent.js.map +1 -1
  54. package/dist/agent-catalog/agents/codex-agent.d.ts.map +1 -1
  55. package/dist/agent-catalog/agents/codex-agent.js.map +1 -1
  56. package/dist/agent-catalog/agents/cursor-cli-agent.d.ts +0 -1
  57. package/dist/agent-catalog/agents/cursor-cli-agent.d.ts.map +1 -1
  58. package/dist/agent-catalog/agents/cursor-cli-agent.js +0 -1
  59. package/dist/agent-catalog/agents/cursor-cli-agent.js.map +1 -1
  60. package/dist/agent-catalog/agents/deepseek-harness-agent.d.ts.map +1 -1
  61. package/dist/agent-catalog/agents/deepseek-harness-agent.js +4 -4
  62. package/dist/agent-catalog/agents/deepseek-harness-agent.js.map +1 -1
  63. package/dist/agent-catalog/agents/grok-build-agent.d.ts.map +1 -1
  64. package/dist/agent-catalog/agents/grok-build-agent.js +4 -14
  65. package/dist/agent-catalog/agents/grok-build-agent.js.map +1 -1
  66. package/dist/agent-catalog/agents/opencode-agent.d.ts.map +1 -1
  67. package/dist/agent-catalog/agents/opencode-agent.js +1 -1
  68. package/dist/agent-catalog/agents/opencode-agent.js.map +1 -1
  69. package/dist/agent-catalog/agents/pi-agent.d.ts.map +1 -1
  70. package/dist/agent-catalog/agents/pi-agent.js +1 -1
  71. package/dist/agent-catalog/agents/pi-agent.js.map +1 -1
  72. package/dist/agent-catalog/agents/supported-agent-specifications.js.map +1 -1
  73. package/dist/agent-catalog/agents/verify-session-creation.d.ts.map +1 -1
  74. package/dist/agent-catalog/agents/verify-session-creation.js +1 -1
  75. package/dist/agent-catalog/agents/verify-session-creation.js.map +1 -1
  76. package/dist/agent-catalog/agents/workbuddy-agent.d.ts.map +1 -1
  77. package/dist/agent-catalog/agents/workbuddy-agent.js +3 -6
  78. package/dist/agent-catalog/agents/workbuddy-agent.js.map +1 -1
  79. package/dist/agent-catalog/discover-agent.d.ts.map +1 -1
  80. package/dist/agent-catalog/discover-agent.js +33 -29
  81. package/dist/agent-catalog/discover-agent.js.map +1 -1
  82. package/dist/agent-catalog/discover-local-agents.d.ts +0 -1
  83. package/dist/agent-catalog/discover-local-agents.d.ts.map +1 -1
  84. package/dist/agent-catalog/discover-local-agents.js +1 -1
  85. package/dist/agent-catalog/discover-local-agents.js.map +1 -1
  86. package/dist/agent-catalog/executable-discovery.d.ts.map +1 -1
  87. package/dist/agent-catalog/executable-discovery.js.map +1 -1
  88. package/dist/agent-catalog/local-agent.d.ts +15 -2
  89. package/dist/agent-catalog/local-agent.d.ts.map +1 -1
  90. package/dist/agent-catalog/local-agent.js +21 -3
  91. package/dist/agent-catalog/local-agent.js.map +1 -1
  92. package/dist/agent-catalog/publish-agent-catalog.d.ts.map +1 -1
  93. package/dist/agent-catalog/publish-agent-catalog.js +5 -7
  94. package/dist/agent-catalog/publish-agent-catalog.js.map +1 -1
  95. package/dist/agent-installation/agent-installation-manager.d.ts.map +1 -1
  96. package/dist/agent-installation/agent-installation-manager.js +8 -7
  97. package/dist/agent-installation/agent-installation-manager.js.map +1 -1
  98. package/dist/agent-installation/deepseek-managed-package.d.ts.map +1 -1
  99. package/dist/agent-installation/deepseek-managed-package.js +1 -3
  100. package/dist/agent-installation/deepseek-managed-package.js.map +1 -1
  101. package/dist/agent-installation/fixed-installation-executor.d.ts +2 -3
  102. package/dist/agent-installation/fixed-installation-executor.d.ts.map +1 -1
  103. package/dist/agent-installation/fixed-installation-executor.js +47 -70
  104. package/dist/agent-installation/fixed-installation-executor.js.map +1 -1
  105. package/dist/agent-installation/grok-managed-package.d.ts.map +1 -1
  106. package/dist/agent-installation/grok-managed-package.js +2 -4
  107. package/dist/agent-installation/grok-managed-package.js.map +1 -1
  108. package/dist/agent-installation/managed-adapter-installer.d.ts.map +1 -1
  109. package/dist/agent-installation/managed-adapter-installer.js +3 -1
  110. package/dist/agent-installation/managed-adapter-installer.js.map +1 -1
  111. package/dist/agent-installation/managed-agent-archive.d.ts +2 -5
  112. package/dist/agent-installation/managed-agent-archive.d.ts.map +1 -1
  113. package/dist/agent-installation/managed-agent-archive.js +18 -20
  114. package/dist/agent-installation/managed-agent-archive.js.map +1 -1
  115. package/dist/agent-installation/managed-npm-package.d.ts.map +1 -1
  116. package/dist/agent-installation/managed-npm-package.js +8 -13
  117. package/dist/agent-installation/managed-npm-package.js.map +1 -1
  118. package/dist/agent-installation/opencode-managed-release.d.ts.map +1 -1
  119. package/dist/agent-installation/opencode-managed-release.js.map +1 -1
  120. package/dist/agent-installation/pi-managed-packages.js.map +1 -1
  121. package/dist/agent-installation/verified-download.d.ts +0 -4
  122. package/dist/agent-installation/verified-download.d.ts.map +1 -1
  123. package/dist/agent-installation/verified-download.js +5 -5
  124. package/dist/agent-installation/verified-download.js.map +1 -1
  125. package/dist/agent-installation/workbuddy-managed-package.d.ts.map +1 -1
  126. package/dist/agent-installation/workbuddy-managed-package.js +1 -3
  127. package/dist/agent-installation/workbuddy-managed-package.js.map +1 -1
  128. package/dist/audit/audit-store.d.ts.map +1 -1
  129. package/dist/audit/audit-store.js +1 -3
  130. package/dist/audit/audit-store.js.map +1 -1
  131. package/dist/authorization/local-authorization-error.d.ts.map +1 -1
  132. package/dist/authorization/local-authorization-error.js.map +1 -1
  133. package/dist/authorization/workspace-directory.d.ts.map +1 -1
  134. package/dist/authorization/workspace-directory.js +30 -21
  135. package/dist/authorization/workspace-directory.js.map +1 -1
  136. package/dist/cli.js +1295 -3431
  137. package/dist/cli.js.map +4 -4
  138. package/dist/config.d.ts.map +1 -1
  139. package/dist/config.js +1 -2
  140. package/dist/config.js.map +1 -1
  141. package/dist/index.d.ts.map +1 -1
  142. package/dist/index.js +1238 -3355
  143. package/dist/index.js.map +4 -4
  144. package/dist/native-sessions/codex-history.d.ts.map +1 -1
  145. package/dist/native-sessions/codex-history.js +26 -17
  146. package/dist/native-sessions/codex-history.js.map +1 -1
  147. package/dist/native-sessions/service.d.ts.map +1 -1
  148. package/dist/native-sessions/service.js +46 -36
  149. package/dist/native-sessions/service.js.map +1 -1
  150. package/dist/pairing/pair-local-machine.d.ts.map +1 -1
  151. package/dist/pairing/pair-local-machine.js +16 -29
  152. package/dist/pairing/pair-local-machine.js.map +1 -1
  153. package/dist/process/executable.d.ts.map +1 -1
  154. package/dist/process/executable.js +16 -15
  155. package/dist/process/executable.js.map +1 -1
  156. package/dist/prompt-artifact-resolver.d.ts +13 -0
  157. package/dist/prompt-artifact-resolver.d.ts.map +1 -0
  158. package/dist/prompt-artifact-resolver.js +205 -0
  159. package/dist/prompt-artifact-resolver.js.map +1 -0
  160. package/dist/relay-connection/connect.d.ts.map +1 -1
  161. package/dist/relay-connection/connect.js +3 -1
  162. package/dist/relay-connection/connect.js.map +1 -1
  163. package/dist/relay-connection/liveness.d.ts.map +1 -1
  164. package/dist/relay-connection/liveness.js +1 -2
  165. package/dist/relay-connection/liveness.js.map +1 -1
  166. package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
  167. package/dist/relay-connection/session-event-outbox.js +7 -19
  168. package/dist/relay-connection/session-event-outbox.js.map +1 -1
  169. package/dist/relay-connection/timer.d.ts.map +1 -1
  170. package/dist/relay-connection/timer.js.map +1 -1
  171. package/dist/run-daemon.d.ts.map +1 -1
  172. package/dist/run-daemon.js +202 -87
  173. package/dist/run-daemon.js.map +1 -1
  174. package/dist/storage/state-directory.d.ts.map +1 -1
  175. package/dist/storage/state-directory.js.map +1 -1
  176. package/package.json +55 -55
  177. package/dist/agent-installation/automatic-adapter-provisioner.d.ts +0 -19
  178. package/dist/agent-installation/automatic-adapter-provisioner.d.ts.map +0 -1
  179. package/dist/agent-installation/automatic-adapter-provisioner.js +0 -125
  180. package/dist/agent-installation/automatic-adapter-provisioner.js.map +0 -1
  181. package/dist/agent-installation/fixed-agent-install-recipe.d.ts +0 -20
  182. package/dist/agent-installation/fixed-agent-install-recipe.d.ts.map +0 -1
  183. package/dist/agent-installation/fixed-agent-install-recipe.js +0 -18
  184. package/dist/agent-installation/fixed-agent-install-recipe.js.map +0 -1
  185. package/dist/agent-installation/fixed-agent-install-recipes.d.ts +0 -26
  186. package/dist/agent-installation/fixed-agent-install-recipes.d.ts.map +0 -1
  187. package/dist/agent-installation/fixed-agent-install-recipes.js +0 -301
  188. package/dist/agent-installation/fixed-agent-install-recipes.js.map +0 -1
  189. package/dist/agent-installation/grok-build-install-recipe.d.ts +0 -12
  190. package/dist/agent-installation/grok-build-install-recipe.d.ts.map +0 -1
  191. package/dist/agent-installation/grok-build-install-recipe.js +0 -102
  192. package/dist/agent-installation/grok-build-install-recipe.js.map +0 -1
  193. package/dist/agent-installation/workbuddy-install-recipe.d.ts +0 -12
  194. package/dist/agent-installation/workbuddy-install-recipe.d.ts.map +0 -1
  195. package/dist/agent-installation/workbuddy-install-recipe.js +0 -51
  196. package/dist/agent-installation/workbuddy-install-recipe.js.map +0 -1
@@ -1,26 +1,28 @@
1
1
  import { basename } from "node:path";
2
2
  import { styleText } from "node:util";
3
3
  import { AGENT_ACTIVITY_KIND, AGENT_INSTALL_CANCEL_KIND, AGENT_INSTALL_CANCELLED_KIND, AGENT_INSTALL_COMPLETED_KIND, AGENT_INSTALL_FAILED_KIND, AGENT_INSTALL_PROGRESS_KIND, AGENT_INSTALL_REQUEST_KIND, AGENT_INSTALL_STARTED_KIND, AGENT_MESSAGE_DELTA_KIND, decodeAgentInstallCancelCommand, decodeAgentInstallRequestCommand, decodeMessageKind, decodeNativeSessionCancel, decodeNativeSessionCommand, decodeRuntimeHeartbeatAckEvent, decodeRuntimeLivenessReadyEvent, decodeSessionCancelCommand, decodeSessionConfigureCommand, decodeSessionElicitationRespondCommand, decodeSessionEventAcknowledgement, decodeSessionPermissionRespondCommand, decodeSessionPrepareCommand, decodeSessionPromptCommand, decodeWorkspaceListCommand, encodeAgentActivityEvent, encodeAgentContentEvent, encodeAgentInstallCancelledEvent, encodeAgentInstallCompletedEvent, encodeAgentInstallFailedEvent, encodeAgentInstallProgressEvent, encodeAgentInstallStartedEvent, encodeAgentMessageDeltaEvent, encodeNativeSessionResult, encodeSessionCommandsUpdatedEvent, encodeSessionCompletedEvent, encodeSessionConfigUpdatedEvent, encodeSessionElicitationRequestedEvent, encodeSessionElicitationResolvedEvent, encodeSessionFailedEvent, encodeSessionInfoUpdatedEvent, encodeSessionOperationFailedEvent, encodeSessionPermissionRequestedEvent, encodeSessionPermissionResolvedEvent, encodeSessionReadyEvent, encodeSessionStartedEvent, encodeSessionUsageUpdatedEvent, encodeWorkspaceEntriesEvent, NATIVE_SESSION_CANCEL_KIND, NATIVE_SESSION_REQUEST_KIND, RUNTIME_HEARTBEAT_ACK_KIND, RUNTIME_LIVENESS_READY_KIND, SESSION_CANCEL_KIND, SESSION_COMPLETED_KIND, SESSION_CONFIGURE_KIND, SESSION_ELICITATION_RESPOND_KIND, SESSION_EVENT_ACK_KIND, SESSION_FAILED_KIND, SESSION_PERMISSION_RESPOND_KIND, SESSION_PREPARE_KIND, SESSION_PROMPT_KIND, SESSION_STARTED_KIND, WORKSPACE_LIST_KIND, } from "@moxt-ai/mobius-protocol";
4
- import { WebSocket } from "undici";
5
- import { AcpExecutionError, AcpSessionManager, NullAcpSessionObserver, } from "./acp/session-manager.js";
4
+ import { fetch, WebSocket } from "undici";
5
+ import { AcpExecutionError, AcpSessionManager } from "./acp/session-manager.js";
6
6
  import { SqliteAcpSessionStore } from "./acp/session-store.js";
7
7
  import { supportedLocalAgentSpecifications } from "./agent-catalog/discover-local-agents.js";
8
8
  import { LocalAgentCatalogError } from "./agent-catalog/local-agent.js";
9
9
  import { publishAgentCatalog } from "./agent-catalog/publish-agent-catalog.js";
10
10
  import { AgentInstallationManager, CancelledInstallation, FailedInstallation, RequestedInstallation, RunningInstallation, SucceededInstallation, } from "./agent-installation/agent-installation-manager.js";
11
- import { AutomaticAdapterProvisioner } from "./agent-installation/automatic-adapter-provisioner.js";
12
11
  import { FixedInstallationExecutor } from "./agent-installation/fixed-installation-executor.js";
13
- import { ManagedAdapterInstaller } from "./agent-installation/managed-adapter-installer.js";
14
12
  import { LocalAuthorizationError } from "./authorization/local-authorization-error.js";
15
13
  import { WorkspaceDirectoryService } from "./authorization/workspace-directory.js";
16
14
  import { NativeSessionService } from "./native-sessions/service.js";
15
+ import { environmentProxyDispatcher } from "./network/environment-proxy.js";
16
+ import { PromptArtifactError, resolvePromptArtifacts } from "./prompt-artifact-resolver.js";
17
17
  import { connectRelay } from "./relay-connection/connect.js";
18
18
  import { RelayConnectionLiveness } from "./relay-connection/liveness.js";
19
19
  import { SessionEventOutbox } from "./relay-connection/session-event-outbox.js";
20
- import { systemDaemonTimer, } from "./relay-connection/timer.js";
20
+ import { systemDaemonTimer } from "./relay-connection/timer.js";
21
21
  import { readStateDirectory } from "./storage/state-directory.js";
22
22
  const RELAY_RECONNECT_DELAY_MILLISECONDS = 500;
23
23
  const RELAY_CONNECTION_REFRESH_MILLISECONDS = 20 * 60 * 1_000;
24
+ const ACTIVE_TURN_RECONCILIATION_TIMEOUT_MILLISECONDS = 20_000;
25
+ const ACTIVE_TURN_RECONCILIATION_CLOSE_CODE = 4009;
24
26
  class RelayConnectionRefreshState {
25
27
  }
26
28
  class ScheduledRelayConnectionRefreshState extends RelayConnectionRefreshState {
@@ -200,7 +202,7 @@ class DaemonInstallationObserver {
200
202
  }));
201
203
  return Promise.resolve();
202
204
  }
203
- throw new Error("Unsupported Agent installation state");
205
+ throw new Error("Unsupported Agent integration installation state");
204
206
  };
205
207
  }
206
208
  class ResolvedSessionDirectories {
@@ -214,18 +216,21 @@ class ResolvedSessionDirectories {
214
216
  }
215
217
  }
216
218
  class ActiveDaemonTurn {
219
+ attemptId;
217
220
  command;
218
- #connectionController;
221
+ #controller;
222
+ #finished = Promise.withResolvers();
219
223
  #cancellationRequested = false;
220
- constructor(command, connectionController) {
224
+ constructor(command, attemptId) {
225
+ this.attemptId = attemptId;
221
226
  this.command = command;
222
- this.#connectionController = connectionController;
227
+ this.#controller = new AbortController();
223
228
  }
224
- abortConnection = () => {
225
- this.#connectionController.abort();
229
+ abortDaemon = () => {
230
+ this.#controller.abort();
226
231
  };
227
- matches = (cancellation) => cancellation.commandId === this.command.commandId &&
228
- cancellation.sessionId === this.command.sessionId;
232
+ finish = () => this.#finished.resolve();
233
+ matches = (cancellation) => cancellation.commandId === this.command.commandId && cancellation.sessionId === this.command.sessionId;
229
234
  requestCancellation = () => {
230
235
  if (this.#cancellationRequested) {
231
236
  return false;
@@ -234,23 +239,128 @@ class ActiveDaemonTurn {
234
239
  return true;
235
240
  };
236
241
  get signal() {
237
- return this.#connectionController.signal;
242
+ return this.#controller.signal;
243
+ }
244
+ get completion() {
245
+ return this.#finished.promise;
246
+ }
247
+ }
248
+ class ActiveTurnReconciliationError extends Error {
249
+ code = "active_turn_reconciliation_failed";
250
+ constructor(message, options = {}) {
251
+ super(message, options);
252
+ this.name = "ActiveTurnReconciliationError";
253
+ }
254
+ }
255
+ function activeTurnReconciliationEndpoint(config, turn) {
256
+ const endpoint = new URL(config.relayUrl);
257
+ endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
258
+ if (!endpoint.pathname.endsWith("/daemon")) {
259
+ throw new ActiveTurnReconciliationError("The Relay URL is invalid");
260
+ }
261
+ endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
262
+ endpoint.searchParams.delete("liveness");
263
+ return endpoint;
264
+ }
265
+ async function reconcileActiveTurn(config, turn, signal) {
266
+ signal.throwIfAborted();
267
+ const timeoutSignal = AbortSignal.timeout(ACTIVE_TURN_RECONCILIATION_TIMEOUT_MILLISECONDS);
268
+ const requestSignal = AbortSignal.any([signal, timeoutSignal]);
269
+ let response;
270
+ try {
271
+ response = await fetch(activeTurnReconciliationEndpoint(config, turn), {
272
+ body: JSON.stringify({ outcome: { kind: "running" } }),
273
+ dispatcher: environmentProxyDispatcher,
274
+ headers: { "content-type": "application/json" },
275
+ method: "POST",
276
+ signal: requestSignal,
277
+ });
278
+ }
279
+ catch (error) {
280
+ if (signal.aborted) {
281
+ signal.throwIfAborted();
282
+ }
283
+ throw new ActiveTurnReconciliationError(timeoutSignal.aborted
284
+ ? "The active Agent turn reconciliation timed out"
285
+ : "The active Agent turn could not be reconciled", { cause: error });
286
+ }
287
+ await response.body?.cancel();
288
+ if (!response.ok) {
289
+ throw new ActiveTurnReconciliationError(`The Relay rejected the active Agent turn reconciliation with status ${response.status.toString()}`);
238
290
  }
239
291
  }
292
+ async function reconcileActiveTurns(config, turns, signal) {
293
+ await Promise.all(turns.map(async (turn) => await reconcileActiveTurn(config, turn, signal)));
294
+ }
240
295
  function send(socket, message) {
241
296
  if (socket.readyState !== WebSocket.OPEN) {
242
297
  throw new Error("Relay connection is not open");
243
298
  }
244
299
  socket.send(message);
245
300
  }
246
- function recordSessionEvent(outbox, socket, sessionId, payload, attemptId = null) {
247
- const envelope = outbox.enqueue(sessionId, payload, attemptId);
248
- if (socket.readyState === WebSocket.OPEN) {
249
- socket.send(envelope);
301
+ function closeRelaySocket(socket, code, reason) {
302
+ try {
303
+ socket.close(code, reason);
304
+ }
305
+ catch (error) {
306
+ console.error("Mobius daemon could not close the Relay WebSocket", error);
307
+ try {
308
+ socket.close();
309
+ }
310
+ catch (fallbackError) {
311
+ console.error("Mobius daemon could not release the Relay WebSocket", fallbackError);
312
+ }
313
+ }
314
+ }
315
+ class DaemonSessionEventConnectionState {
316
+ }
317
+ class DisconnectedDaemonSessionEventState extends DaemonSessionEventConnectionState {
318
+ disconnect = () => this;
319
+ send = () => undefined;
320
+ }
321
+ class ConnectedDaemonSessionEventState extends DaemonSessionEventConnectionState {
322
+ #socket;
323
+ constructor(socket) {
324
+ super();
325
+ this.#socket = socket;
326
+ }
327
+ disconnect = (socket) => socket === this.#socket ? new DisconnectedDaemonSessionEventState() : this;
328
+ send = (message) => {
329
+ if (this.#socket.readyState === WebSocket.OPEN) {
330
+ try {
331
+ this.#socket.send(message);
332
+ }
333
+ catch {
334
+ // The durable outbox retains the event for the replacement connection.
335
+ }
336
+ }
337
+ };
338
+ }
339
+ class DaemonSessionEventPublisher {
340
+ #outbox;
341
+ #connection = new DisconnectedDaemonSessionEventState();
342
+ constructor(outbox) {
343
+ this.#outbox = outbox;
250
344
  }
345
+ connect = (socket) => {
346
+ const connection = new ConnectedDaemonSessionEventState(socket);
347
+ this.#connection = connection;
348
+ for (const pending of this.#outbox.pending()) {
349
+ connection.send(pending);
350
+ }
351
+ };
352
+ disconnect = (socket) => {
353
+ this.#connection = this.#connection.disconnect(socket);
354
+ };
355
+ recordAttempt = (sessionId, payload, attemptId) => {
356
+ this.#connection.send(this.#outbox.enqueue(sessionId, payload, attemptId));
357
+ };
358
+ recordRuntime = (sessionId, payload) => {
359
+ this.#connection.send(this.#outbox.enqueue(sessionId, payload));
360
+ };
251
361
  }
252
- function reportFailure(outbox, socket, command, code, message, attemptId = null) {
253
- recordSessionEvent(outbox, socket, command.sessionId, encodeSessionFailedEvent({
362
+ function reportFailure(publisher, command, code, message, attemptId) {
363
+ publisher.recordAttempt(command.sessionId, encodeSessionFailedEvent({
254
364
  code,
255
365
  commandId: command.commandId,
256
366
  kind: SESSION_FAILED_KIND,
@@ -261,7 +371,8 @@ function reportFailure(outbox, socket, command, code, message, attemptId = null)
261
371
  function operationErrorCode(error) {
262
372
  if (error instanceof AcpExecutionError ||
263
373
  error instanceof LocalAuthorizationError ||
264
- error instanceof LocalAgentCatalogError) {
374
+ error instanceof LocalAgentCatalogError ||
375
+ error instanceof PromptArtifactError) {
265
376
  return error.code;
266
377
  }
267
378
  return "session_operation_failed";
@@ -269,7 +380,8 @@ function operationErrorCode(error) {
269
380
  function operationErrorMessage(error) {
270
381
  if (error instanceof AcpExecutionError ||
271
382
  error instanceof LocalAuthorizationError ||
272
- error instanceof LocalAgentCatalogError) {
383
+ error instanceof LocalAgentCatalogError ||
384
+ error instanceof PromptArtifactError) {
273
385
  return error.message;
274
386
  }
275
387
  return "The local agent operation could not be completed";
@@ -327,7 +439,7 @@ async function executeWorkspaceList(socket, command, directories, audit, signal)
327
439
  reportOperationFailure(socket, command.requestId, "", error);
328
440
  }
329
441
  }
330
- async function executeSessionPrepare(outbox, socket, command, config, directories, audit, sessions, signal) {
442
+ async function executeSessionPrepare(publisher, socket, command, config, directories, audit, sessions, signal) {
331
443
  try {
332
444
  const resolved = await resolveSessionDirectories(directories, config.bindingId, command.directoryId, command.additionalDirectoryIds, signal);
333
445
  const agent = config.catalog.find(command.agentId);
@@ -344,7 +456,7 @@ async function executeSessionPrepare(outbox, socket, command, config, directorie
344
456
  event: "session_prepared",
345
457
  sessionId: command.sessionId,
346
458
  });
347
- recordSessionEvent(outbox, socket, command.sessionId, encodeSessionReadyEvent({
459
+ publisher.recordRuntime(command.sessionId, encodeSessionReadyEvent({
348
460
  additionalDirectories: resolved.additionalDirectoryEntries,
349
461
  agentId: command.agentId,
350
462
  agentName: prepared.agentName,
@@ -373,7 +485,7 @@ async function executeSessionPrepare(outbox, socket, command, config, directorie
373
485
  reportOperationFailure(socket, command.requestId, command.sessionId, error);
374
486
  }
375
487
  }
376
- async function executeSessionConfigure(outbox, socket, command, config, directories, audit, sessions, signal) {
488
+ async function executeSessionConfigure(publisher, socket, command, config, directories, audit, sessions, signal) {
377
489
  try {
378
490
  const resolved = await resolveSessionDirectories(directories, config.bindingId, command.directoryId, command.additionalDirectoryIds, signal);
379
491
  const configOptions = await sessions.configureSession({
@@ -393,7 +505,7 @@ async function executeSessionConfigure(outbox, socket, command, config, director
393
505
  event: "session_configured",
394
506
  sessionId: command.sessionId,
395
507
  });
396
- recordSessionEvent(outbox, socket, command.sessionId, encodeSessionConfigUpdatedEvent({
508
+ publisher.recordRuntime(command.sessionId, encodeSessionConfigUpdatedEvent({
397
509
  configOptions,
398
510
  kind: "session.config.updated",
399
511
  requestId: command.requestId,
@@ -404,7 +516,7 @@ async function executeSessionConfigure(outbox, socket, command, config, director
404
516
  reportOperationFailure(socket, command.requestId, command.sessionId, error);
405
517
  }
406
518
  }
407
- async function executePrompt(outbox, socket, command, config, directories, audit, sessions, attemptId, signal) {
519
+ async function executePrompt(outbox, publisher, command, config, directories, audit, sessions, attemptId, signal) {
408
520
  try {
409
521
  const resolved = await resolveSessionDirectories(directories, config.bindingId, command.directoryId, command.additionalDirectoryIds, signal);
410
522
  const agent = config.catalog.find(command.agentId);
@@ -414,20 +526,21 @@ async function executePrompt(outbox, socket, command, config, directories, audit
414
526
  sessionId: command.sessionId,
415
527
  });
416
528
  outbox.transitionExecution(command.commandId, "running");
417
- recordSessionEvent(outbox, socket, command.sessionId, encodeSessionStartedEvent({
529
+ publisher.recordAttempt(command.sessionId, encodeSessionStartedEvent({
418
530
  commandId: command.commandId,
419
531
  kind: SESSION_STARTED_KIND,
420
532
  attemptId,
421
533
  sessionId: command.sessionId,
422
534
  }), attemptId);
535
+ const attachments = await resolvePromptArtifacts(config, command, signal);
423
536
  const result = await sessions.runPrompt({
424
537
  additionalDirectories: resolved.additionalDirectories,
425
538
  agent,
426
539
  commandId: command.commandId,
427
540
  cwd: resolved.cwd,
428
- images: command.images,
541
+ images: attachments.images,
429
542
  onActivity: (activity) => {
430
- recordSessionEvent(outbox, socket, command.sessionId, encodeAgentActivityEvent({
543
+ publisher.recordAttempt(command.sessionId, encodeAgentActivityEvent({
431
544
  activityId: activity.activityId,
432
545
  activityType: activity.activityType,
433
546
  commandId: command.commandId,
@@ -447,7 +560,7 @@ async function executePrompt(outbox, socket, command, config, directories, audit
447
560
  return Promise.resolve();
448
561
  },
449
562
  onText: (text) => {
450
- recordSessionEvent(outbox, socket, command.sessionId, encodeAgentMessageDeltaEvent({
563
+ publisher.recordAttempt(command.sessionId, encodeAgentMessageDeltaEvent({
451
564
  commandId: command.commandId,
452
565
  kind: AGENT_MESSAGE_DELTA_KIND,
453
566
  sessionId: command.sessionId,
@@ -456,11 +569,11 @@ async function executePrompt(outbox, socket, command, config, directories, audit
456
569
  return Promise.resolve();
457
570
  },
458
571
  prompt: command.prompt,
459
- resources: command.resources,
572
+ resources: attachments.resources,
460
573
  sessionId: command.sessionId,
461
574
  signal,
462
575
  });
463
- recordSessionEvent(outbox, socket, command.sessionId, encodeSessionCompletedEvent({
576
+ publisher.recordAttempt(command.sessionId, encodeSessionCompletedEvent({
464
577
  commandId: command.commandId,
465
578
  kind: SESSION_COMPLETED_KIND,
466
579
  sessionId: command.sessionId,
@@ -469,9 +582,7 @@ async function executePrompt(outbox, socket, command, config, directories, audit
469
582
  outbox.transitionExecution(command.commandId, result.stopReason === "cancelled" ? "cancelled" : "succeeded");
470
583
  audit.record({
471
584
  commandId: command.commandId,
472
- event: result.stopReason === "cancelled"
473
- ? "session_cancelled"
474
- : "session_completed",
585
+ event: result.stopReason === "cancelled" ? "session_cancelled" : "session_completed",
475
586
  sessionId: command.sessionId,
476
587
  });
477
588
  }
@@ -482,7 +593,7 @@ async function executePrompt(outbox, socket, command, config, directories, audit
482
593
  sessionId: command.sessionId,
483
594
  });
484
595
  const code = operationErrorCode(error);
485
- reportFailure(outbox, socket, command, code === "session_operation_failed" ? "agent_execution_failed" : code, code === "session_operation_failed"
596
+ reportFailure(publisher, command, code === "session_operation_failed" ? "agent_execution_failed" : code, code === "session_operation_failed"
486
597
  ? "The local agent could not complete the request"
487
598
  : operationErrorMessage(error), attemptId);
488
599
  outbox.transitionExecution(command.commandId, "failed");
@@ -527,23 +638,15 @@ async function publishAgentCatalogUntilAvailable(config, signal, timer) {
527
638
  }
528
639
  }
529
640
  }
530
- async function runRelayConnection(config, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, onConnected, signal, timer) {
641
+ async function runRelayConnection(config, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, activeTurns, activeAttemptIds, onConnected, signal, timer) {
531
642
  const relayUrl = new URL(config.relayUrl);
532
643
  relayUrl.searchParams.set("liveness", "1");
533
644
  const socket = await connectRelay(relayUrl, signal);
645
+ const reconnectingTurns = [...activeTurns.values()];
534
646
  onConnected();
535
- const activeTurns = new Map();
536
- const activeAttemptIds = new Map();
537
647
  const connectionController = new AbortController();
538
648
  const nativeRequests = new Map();
539
- const record = (sessionId, payload) => {
540
- recordSessionEvent(outbox, socket, sessionId, payload, activeAttemptIds.get(sessionId) ?? null);
541
- };
542
- sessions.setObserver(new DaemonAcpObserver(record));
543
649
  installationObserver.connect(socket);
544
- for (const pending of outbox.pending()) {
545
- send(socket, pending);
546
- }
547
650
  try {
548
651
  await new Promise((resolve, reject) => {
549
652
  let finished = false;
@@ -555,11 +658,8 @@ async function runRelayConnection(config, directories, audit, sessions, installa
555
658
  socket.removeEventListener("error", handleError);
556
659
  socket.removeEventListener("message", handleMessage);
557
660
  installationObserver.disconnect(socket);
661
+ publisher.disconnect(socket);
558
662
  connectionController.abort();
559
- for (const turn of activeTurns.values()) {
560
- turn.abortConnection();
561
- }
562
- activeTurns.clear();
563
663
  };
564
664
  const resolveConnection = () => {
565
665
  if (finished) {
@@ -578,17 +678,17 @@ async function runRelayConnection(config, directories, audit, sessions, installa
578
678
  reject(new Error("Relay WebSocket failed"));
579
679
  };
580
680
  const stop = () => {
581
- socket.close(1000, "Daemon stopped");
681
+ closeRelaySocket(socket, 1000, "Daemon stopped");
582
682
  resolveConnection();
583
683
  };
584
684
  const refreshConnection = () => {
585
- socket.close(1000, "Refreshing idle relay connection");
685
+ closeRelaySocket(socket, 1000, "Refreshing idle relay connection");
586
686
  resolveConnection();
587
687
  };
588
688
  const connectionLiveness = new RelayConnectionLiveness(timer, {
589
689
  send: (message) => send(socket, message),
590
690
  unresponsive: () => {
591
- socket.close(4001, "Relay liveness check timed out");
691
+ closeRelaySocket(socket, 4001, "Relay liveness check timed out");
592
692
  resolveConnection();
593
693
  },
594
694
  });
@@ -607,7 +707,7 @@ async function runRelayConnection(config, directories, audit, sessions, installa
607
707
  const handleMessage = (event) => {
608
708
  const encoded = event.data;
609
709
  if (typeof encoded !== "string") {
610
- socket.close(4003, "Text message required");
710
+ closeRelaySocket(socket, 4003, "Text message required");
611
711
  rejectConnection();
612
712
  return;
613
713
  }
@@ -620,8 +720,9 @@ async function runRelayConnection(config, directories, audit, sessions, installa
620
720
  if (kind === NATIVE_SESSION_CANCEL_KIND) {
621
721
  const command = decodeNativeSessionCancel(encoded);
622
722
  const pending = nativeRequests.get(command.requestId);
623
- if (pending?.sessionId === command.sessionId)
723
+ if (pending?.sessionId === command.sessionId) {
624
724
  pending.controller.abort(new AcpExecutionError("Native session operation cancelled", {}, "native_session_cancelled"));
725
+ }
625
726
  return;
626
727
  }
627
728
  if (kind === NATIVE_SESSION_REQUEST_KIND) {
@@ -634,10 +735,7 @@ async function runRelayConnection(config, directories, audit, sessions, installa
634
735
  return;
635
736
  }
636
737
  const controller = new AbortController();
637
- const operationSignal = AbortSignal.any([
638
- connectionController.signal,
639
- controller.signal,
640
- ]);
738
+ const operationSignal = AbortSignal.any([connectionController.signal, controller.signal]);
641
739
  const timeout = timer.schedule(() => controller.abort(new AcpExecutionError("Native session operation timed out", {}, "native_session_timeout")), 90_000);
642
740
  const completion = (async () => {
643
741
  try {
@@ -659,8 +757,9 @@ async function runRelayConnection(config, directories, audit, sessions, installa
659
757
  }
660
758
  operationSignal.throwIfAborted();
661
759
  const result = await nativeSessions.execute(command, operationSignal);
662
- if (socket.readyState === WebSocket.OPEN)
760
+ if (socket.readyState === WebSocket.OPEN) {
663
761
  send(socket, encodeNativeSessionResult(result));
762
+ }
664
763
  }
665
764
  catch (error) {
666
765
  const failure = operationSignal.aborted
@@ -712,9 +811,7 @@ async function runRelayConnection(config, directories, audit, sessions, installa
712
811
  event: "session_cancellation_requested",
713
812
  sessionId: cancellation.sessionId,
714
813
  });
715
- void sessions
716
- .cancelPrompt(cancellation.sessionId)
717
- .catch((error) => {
814
+ void sessions.cancelPrompt(cancellation.sessionId).catch((error) => {
718
815
  console.error("Mobius daemon could not cancel the agent turn", error);
719
816
  });
720
817
  return;
@@ -726,12 +823,12 @@ async function runRelayConnection(config, directories, audit, sessions, installa
726
823
  }
727
824
  if (kind === SESSION_PREPARE_KIND) {
728
825
  const command = decodeSessionPrepareCommand(encoded);
729
- void executeSessionPrepare(outbox, socket, command, config, directories, audit, sessions, connectionController.signal);
826
+ void executeSessionPrepare(publisher, socket, command, config, directories, audit, sessions, connectionController.signal);
730
827
  return;
731
828
  }
732
829
  if (kind === SESSION_CONFIGURE_KIND) {
733
830
  const command = decodeSessionConfigureCommand(encoded);
734
- void executeSessionConfigure(outbox, socket, command, config, directories, audit, sessions, connectionController.signal);
831
+ void executeSessionConfigure(publisher, socket, command, config, directories, audit, sessions, connectionController.signal);
735
832
  return;
736
833
  }
737
834
  if (kind === SESSION_PERMISSION_RESPOND_KIND) {
@@ -768,18 +865,14 @@ async function runRelayConnection(config, directories, audit, sessions, installa
768
865
  }
769
866
  if (kind === AGENT_INSTALL_REQUEST_KIND) {
770
867
  const command = decodeAgentInstallRequestCommand(encoded);
771
- void installations
772
- .request(command, signal)
773
- .catch((error) => {
774
- console.error("Mobius Agent installation failed", error);
868
+ void installations.request(command, signal).catch((error) => {
869
+ console.error("Mobius Agent integration installation failed", error);
775
870
  });
776
871
  return;
777
872
  }
778
873
  if (kind === AGENT_INSTALL_CANCEL_KIND) {
779
874
  const command = decodeAgentInstallCancelCommand(encoded);
780
- void installations
781
- .cancel(command.requestId)
782
- .catch((error) => {
875
+ void installations.cancel(command.requestId).catch((error) => {
783
876
  reportOperationFailure(socket, command.requestId, "", error);
784
877
  });
785
878
  return;
@@ -795,10 +888,10 @@ async function runRelayConnection(config, directories, audit, sessions, installa
795
888
  if (receipt.kind === "duplicate") {
796
889
  return;
797
890
  }
798
- const turn = new ActiveDaemonTurn(command, new AbortController());
891
+ const turn = new ActiveDaemonTurn(command, receipt.attemptId);
799
892
  activeTurns.set(command.commandId, turn);
800
893
  activeAttemptIds.set(command.sessionId, receipt.attemptId);
801
- void executePrompt(outbox, socket, command, config, directories, audit, sessions, receipt.attemptId, turn.signal)
894
+ void executePrompt(outbox, publisher, command, config, directories, audit, sessions, receipt.attemptId, turn.signal)
802
895
  .catch((error) => {
803
896
  console.error("Mobius daemon turn failed", error);
804
897
  })
@@ -807,18 +900,33 @@ async function runRelayConnection(config, directories, audit, sessions, installa
807
900
  if (activeAttemptIds.get(command.sessionId) === receipt.attemptId) {
808
901
  activeAttemptIds.delete(command.sessionId);
809
902
  }
903
+ turn.finish();
810
904
  connectionRefresh.afterTurnFinished();
811
905
  });
812
906
  }
813
907
  catch (error) {
814
908
  console.error("Mobius daemon rejected a message", error);
815
- socket.close(4008, "Invalid protocol message");
909
+ closeRelaySocket(socket, 4008, "Invalid protocol message");
816
910
  rejectConnection();
817
911
  }
818
912
  };
819
913
  socket.addEventListener("close", handleClose);
820
914
  socket.addEventListener("error", handleError);
821
915
  socket.addEventListener("message", handleMessage);
916
+ void reconcileActiveTurns(config, reconnectingTurns, connectionController.signal).then(() => {
917
+ if (finished) {
918
+ return;
919
+ }
920
+ publisher.connect(socket);
921
+ void Promise.all(reconnectingTurns.map((turn) => turn.completion)).then(connectionRefresh.afterTurnFinished);
922
+ }, (error) => {
923
+ if (finished) {
924
+ return;
925
+ }
926
+ console.error("Mobius daemon could not reconcile active Agent turns", error);
927
+ closeRelaySocket(socket, ACTIVE_TURN_RECONCILIATION_CLOSE_CODE, "Active Agent turn reconciliation failed");
928
+ rejectConnection();
929
+ });
822
930
  if (signal.aborted) {
823
931
  stop();
824
932
  }
@@ -841,18 +949,20 @@ export async function runDaemon(config, audit, signal, timer = systemDaemonTimer
841
949
  publishCatalog: async (installationSignal) => await publishAgentCatalog(config, installationSignal),
842
950
  stateDirectory,
843
951
  });
844
- await new AutomaticAdapterProvisioner({
845
- defaultWorkspace: config.defaultWorkspace,
846
- audit: installationAudit,
847
- catalog: config.catalog,
848
- environment: process.env,
849
- }, new ManagedAdapterInstaller({
850
- environment: process.env,
851
- stateDirectory,
852
- }), specifications).prepare(signal);
853
952
  const sessionStore = new SqliteAcpSessionStore(config.auditDatabasePath);
854
953
  const outbox = new SessionEventOutbox(config.auditDatabasePath);
855
- const sessions = new AcpSessionManager(new NullAcpSessionObserver(), sessionStore);
954
+ const publisher = new DaemonSessionEventPublisher(outbox);
955
+ const activeTurns = new Map();
956
+ const activeAttemptIds = new Map();
957
+ const recordSessionEvent = (sessionId, payload) => {
958
+ const attemptId = activeAttemptIds.get(sessionId);
959
+ if (attemptId === undefined) {
960
+ publisher.recordRuntime(sessionId, payload);
961
+ return;
962
+ }
963
+ publisher.recordAttempt(sessionId, payload, attemptId);
964
+ };
965
+ const sessions = new AcpSessionManager(new DaemonAcpObserver(recordSessionEvent), sessionStore);
856
966
  const directories = new WorkspaceDirectoryService(config.bindingId, config.defaultWorkspace);
857
967
  const installationObserver = new DaemonInstallationObserver();
858
968
  const nativeSessions = new NativeSessionService(config, directories, sessions, audit);
@@ -872,7 +982,7 @@ export async function runDaemon(config, audit, signal, timer = systemDaemonTimer
872
982
  try {
873
983
  while (!signal.aborted) {
874
984
  try {
875
- await runRelayConnection(config, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, announceConnection, signal, timer);
985
+ await runRelayConnection(config, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, activeTurns, activeAttemptIds, announceConnection, signal, timer);
876
986
  retryAnnounced = false;
877
987
  }
878
988
  catch {
@@ -888,6 +998,11 @@ export async function runDaemon(config, audit, signal, timer = systemDaemonTimer
888
998
  }
889
999
  }
890
1000
  finally {
1001
+ const turns = [...activeTurns.values()];
1002
+ for (const turn of turns) {
1003
+ turn.abortDaemon();
1004
+ }
1005
+ await Promise.all(turns.map((turn) => turn.completion));
891
1006
  try {
892
1007
  await catalogPublication;
893
1008
  }