@moxt-ai/mobius 0.0.26 → 0.0.28

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 (52) hide show
  1. package/dist/acp/activity.d.ts +1 -0
  2. package/dist/acp/activity.d.ts.map +1 -1
  3. package/dist/acp/activity.js +14 -1
  4. package/dist/acp/activity.js.map +1 -1
  5. package/dist/acp/session-history.d.ts.map +1 -1
  6. package/dist/acp/session-history.js +10 -16
  7. package/dist/acp/session-history.js.map +1 -1
  8. package/dist/acp/session-manager.d.ts +7 -0
  9. package/dist/acp/session-manager.d.ts.map +1 -1
  10. package/dist/acp/session-manager.js +65 -11
  11. package/dist/acp/session-manager.js.map +1 -1
  12. package/dist/agent-catalog/publish-agent-catalog.js +3 -3
  13. package/dist/agent-catalog/publish-agent-catalog.js.map +1 -1
  14. package/dist/artifacts/runtime-artifact-uploader.d.ts +12 -0
  15. package/dist/artifacts/runtime-artifact-uploader.d.ts.map +1 -0
  16. package/dist/artifacts/runtime-artifact-uploader.js +157 -0
  17. package/dist/artifacts/runtime-artifact-uploader.js.map +1 -0
  18. package/dist/authorization/local-raster.d.ts +10 -0
  19. package/dist/authorization/local-raster.d.ts.map +1 -0
  20. package/dist/authorization/local-raster.js +134 -0
  21. package/dist/authorization/local-raster.js.map +1 -0
  22. package/dist/cli.js +1644 -235
  23. package/dist/cli.js.map +4 -4
  24. package/dist/index.js +1652 -251
  25. package/dist/index.js.map +4 -4
  26. package/dist/native-sessions/codex-history.d.ts.map +1 -1
  27. package/dist/native-sessions/codex-history.js +11 -11
  28. package/dist/native-sessions/codex-history.js.map +1 -1
  29. package/dist/native-sessions/history-images.d.ts +3 -0
  30. package/dist/native-sessions/history-images.d.ts.map +1 -0
  31. package/dist/native-sessions/history-images.js +74 -0
  32. package/dist/native-sessions/history-images.js.map +1 -0
  33. package/dist/prompt-artifact-resolver.d.ts.map +1 -1
  34. package/dist/prompt-artifact-resolver.js +7 -4
  35. package/dist/prompt-artifact-resolver.js.map +1 -1
  36. package/dist/relay-connection/connect.d.ts.map +1 -1
  37. package/dist/relay-connection/connect.js +3 -6
  38. package/dist/relay-connection/connect.js.map +1 -1
  39. package/dist/relay-connection/runtime-authentication.d.ts +3 -0
  40. package/dist/relay-connection/runtime-authentication.d.ts.map +1 -0
  41. package/dist/relay-connection/runtime-authentication.js +14 -0
  42. package/dist/relay-connection/runtime-authentication.js.map +1 -0
  43. package/dist/relay-connection/session-event-outbox.d.ts +32 -2
  44. package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
  45. package/dist/relay-connection/session-event-outbox.js +362 -11
  46. package/dist/relay-connection/session-event-outbox.js.map +1 -1
  47. package/dist/run-daemon.d.ts.map +1 -1
  48. package/dist/run-daemon.js +297 -24
  49. package/dist/run-daemon.js.map +1 -1
  50. package/dist/runtime.js +1654 -253
  51. package/dist/runtime.js.map +4 -4
  52. package/package.json +2 -1
@@ -1,7 +1,7 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { basename, join } from "node:path";
3
3
  import { styleText } from "node:util";
4
- 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, BOT_BINDING_APPROVAL_REQUEST_KIND, BOT_BINDING_APPROVED_KIND, BOT_BINDING_UNAVAILABLE_KIND, BOT_SESSION_PROMPT_KIND, BotExecutionBindingUnavailableEvent, decodeAgentInstallCancelCommand, decodeAgentInstallRequestCommand, decodeBotExecutionBindingApprovalRequest, decodeBotExecutionBindingApprovedEvent, decodeBotExecutionBindingUnavailableEvent, decodeBotSessionPromptCommand, decodeMessageKind, decodeNativeSessionCancel, decodeNativeSessionCommand, decodeNativeSessionTurnForkCommand, decodeRuntimeHeartbeatAckEvent, decodeRuntimeLivenessReadyEvent, decodeSessionCancelCommand, decodeSessionConfigureCommand, decodeSessionElicitationRespondCommand, decodeSessionEventAcknowledgement, decodeSessionEventEnvelope, decodeSessionEventRejection, decodeSessionPermissionRespondCommand, decodeSessionPrepareCommand, decodeSessionPromptCommand, decodeWorkspaceListCommand, encodeAgentActivityEvent, encodeAgentContentEvent, encodeAgentInstallCancelledEvent, encodeAgentInstallCompletedEvent, encodeAgentInstallFailedEvent, encodeAgentInstallProgressEvent, encodeAgentInstallStartedEvent, encodeAgentMessageDeltaEvent, encodeBotExecutionBindingApprovedEvent, encodeBotExecutionBindingUnavailableEvent, encodeNativeSessionResult, encodeSessionCommandsUpdatedEvent, encodeSessionCompletedEvent, encodeSessionConfigUpdatedEvent, encodeSessionElicitationRequestedEvent, encodeSessionElicitationResolvedEvent, encodeSessionFailedEvent, encodeSessionInfoUpdatedEvent, encodeSessionOperationFailedEvent, encodeSessionPermissionRequestedEvent, encodeSessionPermissionResolvedEvent, encodeSessionReadyEvent, encodeSessionStartedEvent, encodeSessionUsageUpdatedEvent, encodeWorkspaceEntriesEvent, NATIVE_SESSION_CANCEL_KIND, NATIVE_SESSION_REQUEST_KIND, NATIVE_SESSION_TURN_FORK_KIND, OwnerBotReplyRoute, 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_EVENT_REJECTED_KIND, SESSION_FAILED_KIND, SESSION_PERMISSION_RESPOND_KIND, SESSION_PREPARE_KIND, SESSION_PROMPT_KIND, SESSION_STARTED_KIND, SessionPromptDisposition, WORKSPACE_LIST_KIND, } from "@moxt-ai/mobius-protocol";
4
+ 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, BOT_BINDING_APPROVAL_REQUEST_KIND, BOT_BINDING_APPROVED_KIND, BOT_BINDING_UNAVAILABLE_KIND, BOT_SESSION_PROMPT_KIND, BotExecutionBindingUnavailableEvent, decodeAgentInstallCancelCommand, decodeAgentInstallRequestCommand, decodeBotExecutionBindingApprovalRequest, decodeBotExecutionBindingApprovedEvent, decodeBotExecutionBindingUnavailableEvent, decodeBotSessionPromptCommand, decodeMessageKind, decodeNativeSessionCancel, decodeNativeSessionCommand, decodeNativeSessionTurnForkCommand, decodeRuntimeDelivery, decodeRuntimeDeliveryCommitted, decodeRuntimeHeartbeatAckEvent, decodeRuntimeLivenessReadyEvent, decodeSessionCancelCommand, decodeSessionConfigureCommand, decodeSessionElicitationRespondCommand, decodeSessionEventAcknowledgement, decodeSessionEventEnvelope, decodeSessionEventRejection, decodeSessionPermissionRespondCommand, decodeSessionPrepareCommand, decodeSessionPromptCommand, decodeWorkspaceListCommand, encodeAgentActivityEvent, encodeAgentContentEvent, encodeAgentInstallCancelledEvent, encodeAgentInstallCompletedEvent, encodeAgentInstallFailedEvent, encodeAgentInstallProgressEvent, encodeAgentInstallStartedEvent, encodeAgentMessageDeltaEvent, encodeBotExecutionBindingApprovedEvent, encodeBotExecutionBindingUnavailableEvent, encodeNativeSessionResult, encodeRuntimeDeliveryReceived, encodeSessionCommandsUpdatedEvent, encodeSessionCompletedEvent, encodeSessionConfigUpdatedEvent, encodeSessionElicitationRequestedEvent, encodeSessionElicitationResolvedEvent, encodeSessionFailedEvent, encodeSessionInfoUpdatedEvent, encodeSessionOperationFailedEvent, encodeSessionPermissionRequestedEvent, encodeSessionPermissionResolvedEvent, encodeSessionReadyEvent, encodeSessionStartedEvent, encodeSessionUsageUpdatedEvent, encodeWorkspaceEntriesEvent, NATIVE_SESSION_CANCEL_KIND, NATIVE_SESSION_REQUEST_KIND, NATIVE_SESSION_TURN_FORK_KIND, OwnerBotReplyRoute, RUNTIME_DELIVERY_COMMITTED_KIND, RUNTIME_DELIVERY_KIND, RUNTIME_DELIVERY_RECEIVED_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_EVENT_REJECTED_KIND, SESSION_FAILED_KIND, SESSION_PERMISSION_RESPOND_KIND, SESSION_PREPARE_KIND, SESSION_PROMPT_KIND, SESSION_STARTED_KIND, SessionPromptDisposition, WORKSPACE_LIST_KIND, } from "@moxt-ai/mobius-protocol";
5
5
  import { fetch, WebSocket } from "undici";
6
6
  import { AcpExecutionError, AcpSessionManager } from "./acp/session-manager.js";
7
7
  import { SqliteAcpSessionStore } from "./acp/session-store.js";
@@ -10,6 +10,7 @@ import { LocalAgentCatalogError, LocalAgentDefinition } from "./agent-catalog/lo
10
10
  import { publishAgentCatalog } from "./agent-catalog/publish-agent-catalog.js";
11
11
  import { AgentInstallationManager, CancelledInstallation, FailedInstallation, RequestedInstallation, RunningInstallation, SucceededInstallation, } from "./agent-installation/agent-installation-manager.js";
12
12
  import { FixedInstallationExecutor } from "./agent-installation/fixed-installation-executor.js";
13
+ import { uploadRuntimeArtifact } from "./artifacts/runtime-artifact-uploader.js";
13
14
  import { LocalAuthorizationError } from "./authorization/local-authorization-error.js";
14
15
  import { WorkspaceDirectoryService } from "./authorization/workspace-directory.js";
15
16
  import { EmbeddedBotContextApplicator } from "./bots/bot-context-applicator.js";
@@ -22,7 +23,8 @@ import { PromptArtifactError, resolvePromptArtifacts } from "./prompt-artifact-r
22
23
  import { connectRelay } from "./relay-connection/connect.js";
23
24
  import { RelayConnectionDiagnostics } from "./relay-connection/diagnostics.js";
24
25
  import { RelayConnectionLiveness } from "./relay-connection/liveness.js";
25
- import { SessionEventOutbox } from "./relay-connection/session-event-outbox.js";
26
+ import { relayHttpEndpoint, runtimeAuthorization } from "./relay-connection/runtime-authentication.js";
27
+ import { RuntimeDeliveryInboxCapacityError, SessionEventOutbox } from "./relay-connection/session-event-outbox.js";
26
28
  import { systemDaemonTimer } from "./relay-connection/timer.js";
27
29
  import { readStateDirectory } from "./storage/state-directory.js";
28
30
  const RELAY_RECONNECT_DELAY_MILLISECONDS = 500;
@@ -36,10 +38,24 @@ function botIpcSocketPath() {
36
38
  return join("/tmp", `mobius-bot-${randomUUID()}.sock`);
37
39
  }
38
40
  class DaemonAcpObserver {
41
+ #config;
39
42
  #record;
40
- constructor(record) {
43
+ constructor(config, record) {
44
+ this.#config = config;
41
45
  this.#record = record;
42
46
  }
47
+ importLocalImage = async (sessionId, image, signal) => {
48
+ try {
49
+ return (await uploadRuntimeArtifact(this.#config, sessionId, image, signal)).artifactId;
50
+ }
51
+ catch (cause) {
52
+ signal.throwIfAborted();
53
+ if (cause instanceof Error && cause.name === "AbortError") {
54
+ throw cause;
55
+ }
56
+ return "";
57
+ }
58
+ };
43
59
  onCommands = (event) => {
44
60
  this.#record(event.sessionId, encodeSessionCommandsUpdatedEvent(event));
45
61
  return Promise.resolve();
@@ -215,8 +231,7 @@ class ActiveTurnReconciliationError extends Error {
215
231
  }
216
232
  }
217
233
  function activeTurnReconciliationEndpoint(config, turn) {
218
- const endpoint = new URL(config.relayUrl);
219
- endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
234
+ const endpoint = relayHttpEndpoint(config.relayUrl);
220
235
  let channelPath = "";
221
236
  if (endpoint.pathname.endsWith("/daemon")) {
222
237
  channelPath = endpoint.pathname.slice(0, -"/daemon".length);
@@ -229,7 +244,6 @@ function activeTurnReconciliationEndpoint(config, turn) {
229
244
  channelPath = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config.channelId)}`;
230
245
  }
231
246
  endpoint.pathname = `${channelPath}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
232
- endpoint.searchParams.delete("liveness");
233
247
  return endpoint;
234
248
  }
235
249
  async function reconcileActiveTurn(config, turn, signal) {
@@ -241,7 +255,7 @@ async function reconcileActiveTurn(config, turn, signal) {
241
255
  response = await fetch(activeTurnReconciliationEndpoint(config, turn), {
242
256
  body: JSON.stringify({ outcome: { kind: "running" } }),
243
257
  dispatcher: environmentProxyDispatcher,
244
- headers: { "content-type": "application/json" },
258
+ headers: { ...runtimeAuthorization(config.relayUrl), "content-type": "application/json" },
245
259
  method: "POST",
246
260
  signal: requestSignal,
247
261
  });
@@ -311,17 +325,33 @@ class ConnectedDaemonSessionEventState extends DaemonSessionEventConnectionState
311
325
  }
312
326
  };
313
327
  }
314
- const SESSION_EVENT_REPLAY_WINDOW = 4;
328
+ const SESSION_EVENT_REPLAY_WINDOW = 16;
329
+ const SESSION_EVENT_SOURCE_REPLAY_WINDOW = 4;
330
+ const SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
331
+ class InFlightSessionEvent {
332
+ byteLength;
333
+ sourceKey;
334
+ constructor(sourceKey, byteLength) {
335
+ this.byteLength = byteLength;
336
+ this.sourceKey = sourceKey;
337
+ }
338
+ }
339
+ function sessionEventSourceKey(envelope) {
340
+ return envelope.source.kind === "attempt"
341
+ ? `attempt:${envelope.sessionId}:${envelope.source.attemptId}`
342
+ : `runtime:${envelope.sessionId}`;
343
+ }
315
344
  class DaemonSessionEventPublisher {
316
345
  #outbox;
317
- #inFlightEventIds = new Set();
346
+ #inFlightEvents = new Map();
347
+ #inFlightBytes = 0;
318
348
  #connection = new DisconnectedDaemonSessionEventState();
319
349
  constructor(outbox) {
320
350
  this.#outbox = outbox;
321
351
  }
322
352
  connect = (socket) => {
323
353
  this.#connection = new ConnectedDaemonSessionEventState(socket);
324
- this.#inFlightEventIds.clear();
354
+ this.#clearInFlight();
325
355
  this.#publishNextBatch();
326
356
  };
327
357
  disconnect = (socket) => {
@@ -329,10 +359,16 @@ class DaemonSessionEventPublisher {
329
359
  return;
330
360
  }
331
361
  this.#connection = this.#connection.disconnect(socket);
332
- this.#inFlightEventIds.clear();
362
+ this.#clearInFlight();
333
363
  };
334
364
  recordAttempt = (sessionId, payload, attemptId) => {
335
- this.#outbox.enqueue(sessionId, payload, attemptId);
365
+ const kind = decodeMessageKind(payload);
366
+ if (kind === SESSION_COMPLETED_KIND || kind === SESSION_FAILED_KIND) {
367
+ this.#outbox.enqueueCritical(sessionId, payload, attemptId);
368
+ }
369
+ else {
370
+ this.#outbox.enqueue(sessionId, payload, attemptId);
371
+ }
336
372
  this.#publishNextBatch();
337
373
  };
338
374
  recordRuntime = (sessionId, payload) => {
@@ -343,7 +379,7 @@ class DaemonSessionEventPublisher {
343
379
  if (!this.#outbox.acknowledge(acknowledgement)) {
344
380
  return;
345
381
  }
346
- this.#inFlightEventIds.delete(acknowledgement.eventId);
382
+ this.#removeInFlight(acknowledgement.eventId);
347
383
  this.#publishNextBatch();
348
384
  };
349
385
  quarantine = (rejection) => {
@@ -351,24 +387,58 @@ class DaemonSessionEventPublisher {
351
387
  if (!quarantined) {
352
388
  return false;
353
389
  }
354
- this.#inFlightEventIds.delete(rejection.eventId);
390
+ this.#removeInFlight(rejection.eventId);
355
391
  this.#publishNextBatch();
356
392
  return true;
357
393
  };
358
394
  #publishNextBatch = () => {
359
- if (this.#inFlightEventIds.size > 0) {
360
- return;
361
- }
362
- const batch = this.#outbox.pendingBatch(SESSION_EVENT_REPLAY_WINDOW);
363
- for (const encoded of batch) {
364
- const envelope = decodeSessionEventEnvelope(encoded);
365
- if (!this.#connection.send(encoded)) {
366
- this.#inFlightEventIds.clear();
395
+ while (this.#inFlightEvents.size < SESSION_EVENT_REPLAY_WINDOW) {
396
+ const sourceCounts = new Map();
397
+ for (const inFlight of this.#inFlightEvents.values()) {
398
+ sourceCounts.set(inFlight.sourceKey, (sourceCounts.get(inFlight.sourceKey) ?? 0) + 1);
399
+ }
400
+ const saturatedSources = [...sourceCounts.entries()]
401
+ .filter(([, count]) => count >= SESSION_EVENT_SOURCE_REPLAY_WINDOW)
402
+ .map(([sourceKey]) => sourceKey);
403
+ const batch = this.#outbox.pendingSourceHeads(SESSION_EVENT_REPLAY_WINDOW - this.#inFlightEvents.size, [...this.#inFlightEvents.keys()], saturatedSources);
404
+ let sent = 0;
405
+ for (const encoded of batch) {
406
+ const envelope = decodeSessionEventEnvelope(encoded);
407
+ const sourceKey = sessionEventSourceKey(envelope);
408
+ const sourceCount = sourceCounts.get(sourceKey) ?? 0;
409
+ if (sourceCount >= SESSION_EVENT_SOURCE_REPLAY_WINDOW) {
410
+ continue;
411
+ }
412
+ const byteLength = new TextEncoder().encode(encoded).byteLength;
413
+ if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
414
+ continue;
415
+ }
416
+ if (!this.#connection.send(encoded)) {
417
+ this.#clearInFlight();
418
+ return;
419
+ }
420
+ this.#inFlightEvents.set(envelope.eventId, new InFlightSessionEvent(sourceKey, byteLength));
421
+ this.#inFlightBytes += byteLength;
422
+ sourceCounts.set(sourceKey, sourceCount + 1);
423
+ sent += 1;
424
+ }
425
+ if (sent === 0) {
367
426
  return;
368
427
  }
369
- this.#inFlightEventIds.add(envelope.eventId);
370
428
  }
371
429
  };
430
+ #removeInFlight = (eventId) => {
431
+ const inFlight = this.#inFlightEvents.get(eventId);
432
+ if (inFlight === undefined) {
433
+ return;
434
+ }
435
+ this.#inFlightEvents.delete(eventId);
436
+ this.#inFlightBytes -= inFlight.byteLength;
437
+ };
438
+ #clearInFlight = () => {
439
+ this.#inFlightEvents.clear();
440
+ this.#inFlightBytes = 0;
441
+ };
372
442
  }
373
443
  class CoalescingDaemonActivityPublisher {
374
444
  #pending = new Map();
@@ -597,6 +667,9 @@ async function executeSessionConfigure(publisher, socket, command, config, direc
597
667
  }));
598
668
  }
599
669
  catch (error) {
670
+ if (signal.aborted) {
671
+ signal.throwIfAborted();
672
+ }
600
673
  reportOperationFailure(socket, command.requestId, command.sessionId, error);
601
674
  }
602
675
  }
@@ -865,6 +938,7 @@ async function publishAgentCatalogUntilAvailable(config, diagnostics, signal, ti
865
938
  }
866
939
  async function runRelayConnection(config, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, grants, contexts, applicator, ipc, activeTurns, activeAttemptIds, connectionEpoch, diagnostics, onConnected, signal, timer) {
867
940
  const relayUrl = new URL(config.relayUrl);
941
+ relayUrl.searchParams.set("deliveryReceipts", "1");
868
942
  relayUrl.searchParams.set("liveness", "1");
869
943
  const socket = await connectRelay(relayUrl, signal);
870
944
  const reconnectingTurns = [...activeTurns.values()];
@@ -913,6 +987,189 @@ async function runRelayConnection(config, directories, audit, sessions, installa
913
987
  closeRelaySocket(socket, code, reason);
914
988
  rejectConnection(failure);
915
989
  };
990
+ const processingDeliveryIds = new Set();
991
+ const processDeliveredPrompt = async (delivery, command, trackActiveAttempt, execute) => {
992
+ if (command.sessionId !== delivery.sessionId) {
993
+ throw new Error("Runtime delivery Session identity does not match its payload");
994
+ }
995
+ const active = activeTurns.get(command.commandId);
996
+ if (active !== undefined) {
997
+ await active.completion;
998
+ return;
999
+ }
1000
+ const receipt = outbox.claimExecution(command.commandId, command.sessionId, delivery.payload);
1001
+ if (receipt.kind === "duplicate") {
1002
+ if (receipt.state === "running") {
1003
+ reportFailure(publisher, command, "agent_execution_interrupted", "The local Agent execution was interrupted before it completed", receipt.attemptId);
1004
+ outbox.transitionExecution(command.commandId, "failed");
1005
+ return;
1006
+ }
1007
+ if (receipt.state !== "dispatching") {
1008
+ return;
1009
+ }
1010
+ }
1011
+ const turn = new ActiveDaemonTurn(command, receipt.attemptId);
1012
+ activeTurns.set(command.commandId, turn);
1013
+ if (trackActiveAttempt) {
1014
+ activeAttemptIds.set(command.sessionId, receipt.attemptId);
1015
+ }
1016
+ try {
1017
+ await execute(receipt.attemptId, turn.signal);
1018
+ }
1019
+ finally {
1020
+ activeTurns.delete(command.commandId);
1021
+ if (activeAttemptIds.get(command.sessionId) === receipt.attemptId) {
1022
+ activeAttemptIds.delete(command.sessionId);
1023
+ }
1024
+ turn.finish();
1025
+ }
1026
+ };
1027
+ const processDeliveredPayload = async (delivery) => {
1028
+ const kind = decodeMessageKind(delivery.payload);
1029
+ if (kind === SESSION_CANCEL_KIND) {
1030
+ const cancellation = decodeSessionCancelCommand(delivery.payload);
1031
+ if (cancellation.sessionId !== delivery.sessionId) {
1032
+ throw new Error("Runtime delivery Session identity does not match its payload");
1033
+ }
1034
+ const turn = activeTurns.get(cancellation.commandId);
1035
+ if (turn === undefined || !turn.matches(cancellation) || !turn.requestCancellation()) {
1036
+ return;
1037
+ }
1038
+ audit.record({
1039
+ commandId: cancellation.commandId,
1040
+ event: "session_cancellation_requested",
1041
+ sessionId: cancellation.sessionId,
1042
+ });
1043
+ await sessions.cancelPrompt(cancellation.sessionId);
1044
+ return;
1045
+ }
1046
+ if (kind === SESSION_CONFIGURE_KIND) {
1047
+ const command = decodeSessionConfigureCommand(delivery.payload);
1048
+ if (command.sessionId !== delivery.sessionId) {
1049
+ throw new Error("Runtime delivery Session identity does not match its payload");
1050
+ }
1051
+ await executeSessionConfigure(publisher, socket, command, config, directories, audit, sessions, connectionController.signal);
1052
+ return;
1053
+ }
1054
+ if (kind === SESSION_PERMISSION_RESPOND_KIND) {
1055
+ const command = decodeSessionPermissionRespondCommand(delivery.payload);
1056
+ if (command.sessionId !== delivery.sessionId) {
1057
+ throw new Error("Runtime delivery Session identity does not match its payload");
1058
+ }
1059
+ try {
1060
+ await sessions.respondPermission(command);
1061
+ audit.record({
1062
+ commandId: command.interactionId,
1063
+ event: "session_permission_resolved",
1064
+ sessionId: command.sessionId,
1065
+ });
1066
+ }
1067
+ catch (error) {
1068
+ reportOperationFailure(socket, command.interactionId, command.sessionId, error);
1069
+ }
1070
+ return;
1071
+ }
1072
+ if (kind === SESSION_ELICITATION_RESPOND_KIND) {
1073
+ const command = decodeSessionElicitationRespondCommand(delivery.payload);
1074
+ if (command.sessionId !== delivery.sessionId) {
1075
+ throw new Error("Runtime delivery Session identity does not match its payload");
1076
+ }
1077
+ try {
1078
+ await sessions.respondElicitation(command);
1079
+ audit.record({
1080
+ commandId: command.interactionId,
1081
+ event: "session_elicitation_resolved",
1082
+ sessionId: command.sessionId,
1083
+ });
1084
+ }
1085
+ catch (error) {
1086
+ reportOperationFailure(socket, command.interactionId, command.sessionId, error);
1087
+ }
1088
+ return;
1089
+ }
1090
+ if (kind === BOT_SESSION_PROMPT_KIND) {
1091
+ const command = decodeBotSessionPromptCommand(delivery.payload);
1092
+ await processDeliveredPrompt(delivery, command, true, (attemptId, executionSignal) => executeBotPrompt(outbox, publisher, command, config, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, timer, executionSignal));
1093
+ return;
1094
+ }
1095
+ if (kind === SESSION_PROMPT_KIND) {
1096
+ const command = decodeSessionPromptCommand(delivery.payload);
1097
+ await processDeliveredPrompt(delivery, command, command.disposition !== SessionPromptDisposition.Steer, (attemptId, executionSignal) => executePrompt(outbox, publisher, command, config, directories, audit, sessions, attemptId, timer, executionSignal));
1098
+ return;
1099
+ }
1100
+ throw new Error("Runtime delivery contains an unsupported protocol message");
1101
+ };
1102
+ const processRuntimeDelivery = (delivery, state) => {
1103
+ if (state === "processed" || processingDeliveryIds.has(delivery.deliveryId)) {
1104
+ return;
1105
+ }
1106
+ processingDeliveryIds.add(delivery.deliveryId);
1107
+ void processDeliveredPayload(delivery)
1108
+ .then(() => {
1109
+ outbox.markRuntimeDeliveryProcessed(delivery.deliveryId, delivery.sessionId);
1110
+ })
1111
+ .catch((error) => {
1112
+ if (connectionController.signal.aborted) {
1113
+ return;
1114
+ }
1115
+ console.error("Mobius daemon could not process a durable Runtime delivery", error);
1116
+ failAndClose(new Error("Runtime delivery processing failed", { cause: error }), 4008, "Invalid Runtime delivery", "delivery");
1117
+ })
1118
+ .finally(() => processingDeliveryIds.delete(delivery.deliveryId));
1119
+ };
1120
+ const receiveRuntimeDelivery = (delivery) => {
1121
+ const payloadKind = decodeMessageKind(delivery.payload);
1122
+ let payloadSessionId = "";
1123
+ if (payloadKind === SESSION_CANCEL_KIND) {
1124
+ payloadSessionId = decodeSessionCancelCommand(delivery.payload).sessionId;
1125
+ }
1126
+ else if (payloadKind === SESSION_CONFIGURE_KIND) {
1127
+ payloadSessionId = decodeSessionConfigureCommand(delivery.payload).sessionId;
1128
+ }
1129
+ else if (payloadKind === SESSION_PERMISSION_RESPOND_KIND) {
1130
+ payloadSessionId = decodeSessionPermissionRespondCommand(delivery.payload).sessionId;
1131
+ }
1132
+ else if (payloadKind === SESSION_ELICITATION_RESPOND_KIND) {
1133
+ payloadSessionId = decodeSessionElicitationRespondCommand(delivery.payload).sessionId;
1134
+ }
1135
+ else if (payloadKind === SESSION_PROMPT_KIND) {
1136
+ payloadSessionId = decodeSessionPromptCommand(delivery.payload).sessionId;
1137
+ }
1138
+ else if (payloadKind === BOT_SESSION_PROMPT_KIND) {
1139
+ payloadSessionId = decodeBotSessionPromptCommand(delivery.payload).sessionId;
1140
+ }
1141
+ else {
1142
+ throw new Error("Runtime delivery contains an unsupported protocol message");
1143
+ }
1144
+ if (payloadSessionId !== delivery.sessionId) {
1145
+ throw new Error("Runtime delivery Session identity does not match its payload");
1146
+ }
1147
+ const stored = outbox.claimRuntimeDelivery(delivery);
1148
+ send(socket, encodeRuntimeDeliveryReceived({
1149
+ deliveryId: delivery.deliveryId,
1150
+ kind: RUNTIME_DELIVERY_RECEIVED_KIND,
1151
+ sessionId: delivery.sessionId,
1152
+ }));
1153
+ processRuntimeDelivery(delivery, stored.state);
1154
+ };
1155
+ const resumeRuntimeDeliveries = () => {
1156
+ for (const stored of outbox.runtimeDeliveries()) {
1157
+ const delivery = {
1158
+ deliveryId: stored.deliveryId,
1159
+ kind: RUNTIME_DELIVERY_KIND,
1160
+ payload: stored.payload,
1161
+ sessionId: stored.sessionId,
1162
+ };
1163
+ if (stored.state !== "relay_committed") {
1164
+ send(socket, encodeRuntimeDeliveryReceived({
1165
+ deliveryId: stored.deliveryId,
1166
+ kind: RUNTIME_DELIVERY_RECEIVED_KIND,
1167
+ sessionId: stored.sessionId,
1168
+ }));
1169
+ }
1170
+ processRuntimeDelivery(delivery, stored.state);
1171
+ }
1172
+ };
916
1173
  const connectionLiveness = new RelayConnectionLiveness(timer, {
917
1174
  send: (message) => send(socket, message),
918
1175
  unresponsive: (heartbeatId, timeoutMilliseconds) => {
@@ -946,6 +1203,17 @@ async function runRelayConnection(config, directories, audit, sessions, installa
946
1203
  }
947
1204
  try {
948
1205
  const kind = decodeMessageKind(encoded);
1206
+ if (kind === RUNTIME_DELIVERY_COMMITTED_KIND) {
1207
+ const committed = decodeRuntimeDeliveryCommitted(encoded);
1208
+ if (!outbox.markRuntimeDeliveryCommitted(committed.deliveryId, committed.sessionId)) {
1209
+ console.warn("Mobius daemon ignored confirmation for an unknown Runtime delivery");
1210
+ }
1211
+ return;
1212
+ }
1213
+ if (kind === RUNTIME_DELIVERY_KIND) {
1214
+ receiveRuntimeDelivery(decodeRuntimeDelivery(encoded));
1215
+ return;
1216
+ }
949
1217
  if (kind === SESSION_EVENT_ACK_KIND) {
950
1218
  publisher.acknowledge(decodeSessionEventAcknowledgement(encoded));
951
1219
  return;
@@ -1291,6 +1559,10 @@ async function runRelayConnection(config, directories, audit, sessions, installa
1291
1559
  }
1292
1560
  catch (error) {
1293
1561
  console.error("Mobius daemon rejected a message", error);
1562
+ if (error instanceof RuntimeDeliveryInboxCapacityError) {
1563
+ failAndClose(error, 4013, "Runtime delivery queue full", "delivery_backpressure");
1564
+ return;
1565
+ }
1294
1566
  failAndClose(new Error("Relay sent an invalid protocol message", { cause: error }), 4008, "Invalid protocol message", "protocol");
1295
1567
  }
1296
1568
  };
@@ -1302,6 +1574,7 @@ async function runRelayConnection(config, directories, audit, sessions, installa
1302
1574
  return;
1303
1575
  }
1304
1576
  publisher.connect(socket);
1577
+ resumeRuntimeDeliveries();
1305
1578
  }, (error) => {
1306
1579
  if (finished) {
1307
1580
  return;
@@ -1348,7 +1621,7 @@ export async function runDaemon(config, audit, signal, timer = systemDaemonTimer
1348
1621
  }
1349
1622
  publisher.recordAttempt(sessionId, payload, attemptId);
1350
1623
  };
1351
- const sessions = new AcpSessionManager(new DaemonAcpObserver(recordSessionEvent), sessionStore);
1624
+ const sessions = new AcpSessionManager(new DaemonAcpObserver(config, recordSessionEvent), sessionStore);
1352
1625
  const directories = new WorkspaceDirectoryService(config.bindingId, config.defaultWorkspace);
1353
1626
  const installationObserver = new DaemonInstallationObserver();
1354
1627
  const diagnostics = new RelayConnectionDiagnostics(audit, config.channelId, config.relayUrl);