@moxt-ai/mobius 0.0.25 → 0.0.27

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 (38) hide show
  1. package/dist/agent-catalog/publish-agent-catalog.js +3 -3
  2. package/dist/agent-catalog/publish-agent-catalog.js.map +1 -1
  3. package/dist/cli.js +1194 -205
  4. package/dist/cli.js.map +4 -4
  5. package/dist/index.js +1186 -204
  6. package/dist/index.js.map +4 -4
  7. package/dist/network/environment-proxy.d.ts +1 -0
  8. package/dist/network/environment-proxy.d.ts.map +1 -1
  9. package/dist/network/environment-proxy.js +47 -0
  10. package/dist/network/environment-proxy.js.map +1 -1
  11. package/dist/prompt-artifact-resolver.d.ts.map +1 -1
  12. package/dist/prompt-artifact-resolver.js +7 -4
  13. package/dist/prompt-artifact-resolver.js.map +1 -1
  14. package/dist/relay-connection/connect.d.ts.map +1 -1
  15. package/dist/relay-connection/connect.js +19 -14
  16. package/dist/relay-connection/connect.js.map +1 -1
  17. package/dist/relay-connection/diagnostics.d.ts +11 -0
  18. package/dist/relay-connection/diagnostics.d.ts.map +1 -0
  19. package/dist/relay-connection/diagnostics.js +122 -0
  20. package/dist/relay-connection/diagnostics.js.map +1 -0
  21. package/dist/relay-connection/liveness.d.ts +1 -1
  22. package/dist/relay-connection/liveness.d.ts.map +1 -1
  23. package/dist/relay-connection/liveness.js +2 -1
  24. package/dist/relay-connection/liveness.js.map +1 -1
  25. package/dist/relay-connection/runtime-authentication.d.ts +3 -0
  26. package/dist/relay-connection/runtime-authentication.d.ts.map +1 -0
  27. package/dist/relay-connection/runtime-authentication.js +14 -0
  28. package/dist/relay-connection/runtime-authentication.js.map +1 -0
  29. package/dist/relay-connection/session-event-outbox.d.ts +34 -3
  30. package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
  31. package/dist/relay-connection/session-event-outbox.js +404 -13
  32. package/dist/relay-connection/session-event-outbox.js.map +1 -1
  33. package/dist/run-daemon.d.ts.map +1 -1
  34. package/dist/run-daemon.js +437 -151
  35. package/dist/run-daemon.js.map +1 -1
  36. package/dist/runtime.js +1186 -204
  37. package/dist/runtime.js.map +4 -4
  38. package/package.json +1 -1
package/dist/runtime.js CHANGED
@@ -1450,7 +1450,53 @@ import { fetch } from "undici";
1450
1450
 
1451
1451
  // src/network/environment-proxy.ts
1452
1452
  import { EnvHttpProxyAgent } from "undici";
1453
+ var configuredHttpProxy = process.env["http_proxy"] ?? process.env["HTTP_PROXY"] ?? "";
1454
+ var configuredHttpsProxy = process.env["https_proxy"] ?? process.env["HTTPS_PROXY"] ?? configuredHttpProxy;
1453
1455
  var environmentProxyDispatcher = new EnvHttpProxyAgent();
1456
+ function bypassesProxy(target) {
1457
+ const noProxy = process.env["no_proxy"] ?? process.env["NO_PROXY"] ?? "";
1458
+ if (noProxy === "*") {
1459
+ return true;
1460
+ }
1461
+ const hostname4 = target.hostname.replace(/^\[|\]$/gu, "").toLowerCase();
1462
+ const defaultPort = target.protocol === "wss:" || target.protocol === "https:" ? 443 : 80;
1463
+ const port = Number.parseInt(target.port, 10) || defaultPort;
1464
+ for (const rawEntry of noProxy.split(/[,\s]/u)) {
1465
+ if (rawEntry.length === 0) {
1466
+ continue;
1467
+ }
1468
+ const parsed = rawEntry.match(/^(.+):(\d+)$/u);
1469
+ const entryHostname = (parsed?.[1] ?? rawEntry).replace(/^\*?\./u, "").toLowerCase();
1470
+ const entryPort = parsed?.[2] === void 0 ? 0 : Number.parseInt(parsed[2], 10);
1471
+ if (entryPort !== 0 && entryPort !== port) {
1472
+ continue;
1473
+ }
1474
+ if (hostname4 === entryHostname || hostname4.endsWith(`.${entryHostname}`)) {
1475
+ return true;
1476
+ }
1477
+ }
1478
+ return false;
1479
+ }
1480
+ function sanitizedProxyOrigin(proxyUrl) {
1481
+ try {
1482
+ return new URL(proxyUrl).origin;
1483
+ } catch {
1484
+ return "configured-invalid-url";
1485
+ }
1486
+ }
1487
+ function describeEnvironmentProxyRoute(target) {
1488
+ if (bypassesProxy(target)) {
1489
+ return { proxyRoute: "direct" };
1490
+ }
1491
+ const proxyUrl = target.protocol === "wss:" || target.protocol === "https:" ? configuredHttpsProxy : configuredHttpProxy;
1492
+ if (proxyUrl.length === 0) {
1493
+ return { proxyRoute: "direct" };
1494
+ }
1495
+ return {
1496
+ proxyOrigin: sanitizedProxyOrigin(proxyUrl),
1497
+ proxyRoute: "proxy"
1498
+ };
1499
+ }
1454
1500
 
1455
1501
  // src/agent-installation/managed-agent-archive.ts
1456
1502
  var ARCHIVE_DOWNLOAD_LIMIT_BYTES = 128 * 1024 * 1024;
@@ -21810,6 +21856,9 @@ var SESSION_CANCEL_KIND = "session.cancel";
21810
21856
  var RUNTIME_LIVENESS_READY_KIND = "runtime.liveness.ready";
21811
21857
  var RUNTIME_HEARTBEAT_KIND = "runtime.heartbeat";
21812
21858
  var RUNTIME_HEARTBEAT_ACK_KIND = "runtime.heartbeat.ack";
21859
+ var RUNTIME_DELIVERY_KIND = "runtime.delivery";
21860
+ var RUNTIME_DELIVERY_RECEIVED_KIND = "runtime.delivery.received";
21861
+ var RUNTIME_DELIVERY_COMMITTED_KIND = "runtime.delivery.committed";
21813
21862
  var SESSION_STARTED_KIND = "session.started";
21814
21863
  var AGENT_MESSAGE_DELTA_KIND = "agent.message.delta";
21815
21864
  var AGENT_ACTIVITY_KIND = "agent.activity";
@@ -21832,6 +21881,7 @@ var DIRECTORY_IDENTIFIER_PATTERN2 = /^[a-zA-Z0-9][a-zA-Z0-9_-]{0,2047}$/;
21832
21881
  var MAX_PROMPT_LENGTH = 1e4;
21833
21882
  var MAX_PROMPT_ATTACHMENT_LENGTH = 6e6;
21834
21883
  var MAX_EVENT_TEXT_LENGTH = 1e5;
21884
+ var MAX_RUNTIME_DELIVERY_PAYLOAD_LENGTH = 8 * 1024 * 1024;
21835
21885
  var ACTIVITY_TYPES = /* @__PURE__ */ new Set(["plan", "progress", "reasoning", "tool"]);
21836
21886
  var ACTIVITY_STATUSES = /* @__PURE__ */ new Set(["pending", "running", "completed", "failed"]);
21837
21887
  var TOOL_KINDS = /* @__PURE__ */ new Set([
@@ -22110,6 +22160,37 @@ function decodeRuntimeHeartbeatAckEvent(encoded) {
22110
22160
  kind: RUNTIME_HEARTBEAT_ACK_KIND
22111
22161
  };
22112
22162
  }
22163
+ function decodeRuntimeDelivery(encoded) {
22164
+ const value = parseJson2(encoded);
22165
+ assertExactKeys(value, ["deliveryId", "kind", "payload", "sessionId"]);
22166
+ assertKind(value, RUNTIME_DELIVERY_KIND);
22167
+ return {
22168
+ deliveryId: readIdentifier(value, "deliveryId"),
22169
+ kind: RUNTIME_DELIVERY_KIND,
22170
+ payload: readString(value, "payload", MAX_RUNTIME_DELIVERY_PAYLOAD_LENGTH),
22171
+ sessionId: readIdentifier(value, "sessionId")
22172
+ };
22173
+ }
22174
+ function decodeRuntimeDeliveryReceived(encoded) {
22175
+ const value = parseJson2(encoded);
22176
+ assertExactKeys(value, ["deliveryId", "kind", "sessionId"]);
22177
+ assertKind(value, RUNTIME_DELIVERY_RECEIVED_KIND);
22178
+ return {
22179
+ deliveryId: readIdentifier(value, "deliveryId"),
22180
+ kind: RUNTIME_DELIVERY_RECEIVED_KIND,
22181
+ sessionId: readIdentifier(value, "sessionId")
22182
+ };
22183
+ }
22184
+ function decodeRuntimeDeliveryCommitted(encoded) {
22185
+ const value = parseJson2(encoded);
22186
+ assertExactKeys(value, ["deliveryId", "kind", "sessionId"]);
22187
+ assertKind(value, RUNTIME_DELIVERY_COMMITTED_KIND);
22188
+ return {
22189
+ deliveryId: readIdentifier(value, "deliveryId"),
22190
+ kind: RUNTIME_DELIVERY_COMMITTED_KIND,
22191
+ sessionId: readIdentifier(value, "sessionId")
22192
+ };
22193
+ }
22113
22194
  function decodeSessionStartedEvent(encoded) {
22114
22195
  const value = parseJson2(encoded);
22115
22196
  assertExactKeys(value, ["commandId", "kind", "attemptId", "sessionId"]);
@@ -22197,6 +22278,9 @@ function decodeSessionFailedEvent(encoded) {
22197
22278
  function encodeRuntimeHeartbeatEvent(message) {
22198
22279
  return JSON.stringify(decodeRuntimeHeartbeatEvent(JSON.stringify(message)));
22199
22280
  }
22281
+ function encodeRuntimeDeliveryReceived(message) {
22282
+ return JSON.stringify(decodeRuntimeDeliveryReceived(JSON.stringify(message)));
22283
+ }
22200
22284
  function encodeSessionStartedEvent(message) {
22201
22285
  return JSON.stringify(decodeSessionStartedEvent(JSON.stringify(message)));
22202
22286
  }
@@ -22895,7 +22979,8 @@ var SESSION_EVENT_COMMITTED_KIND = "session.event.committed";
22895
22979
  var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
22896
22980
  var SessionEventRejectionCode = {
22897
22981
  SessionDeleted: "session_deleted",
22898
- SessionNotFound: "session_not_found"
22982
+ SessionNotFound: "session_not_found",
22983
+ SessionRejected: "session_rejected"
22899
22984
  };
22900
22985
  var IDENTIFIER_PATTERN4 = /^[a-zA-Z0-9][a-zA-Z0-9._:-]{0,127}$/;
22901
22986
  var CURSOR_PATTERN = /^[A-Za-z0-9_-]{1,512}$/;
@@ -22967,6 +23052,7 @@ var encode4 = (message, schema) => {
22967
23052
  }
22968
23053
  return JSON.stringify(decoded.data);
22969
23054
  };
23055
+ var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
22970
23056
  var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
22971
23057
  var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
22972
23058
  var decodeSessionEventRejection = (encoded) => decode5(encoded, eventRejectionSchema);
@@ -26371,6 +26457,23 @@ var AcpSessionManager = class {
26371
26457
 
26372
26458
  // src/agent-catalog/publish-agent-catalog.ts
26373
26459
  import { fetch as fetch2 } from "undici";
26460
+
26461
+ // src/relay-connection/runtime-authentication.ts
26462
+ function runtimeCredential(relayUrl) {
26463
+ return relayUrl.searchParams.get("token") ?? "";
26464
+ }
26465
+ function runtimeAuthorization(relayUrl) {
26466
+ const credential = runtimeCredential(relayUrl);
26467
+ return credential.length === 0 ? {} : { authorization: `Bearer ${credential}` };
26468
+ }
26469
+ function relayHttpEndpoint(relayUrl) {
26470
+ const endpoint = new URL(relayUrl);
26471
+ endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
26472
+ endpoint.search = "";
26473
+ return endpoint;
26474
+ }
26475
+
26476
+ // src/agent-catalog/publish-agent-catalog.ts
26374
26477
  var CATALOG_UPDATE_TIMEOUT_MILLISECONDS = 2e4;
26375
26478
  var AgentCatalogPublishError = class extends Error {
26376
26479
  code = "agent_catalog_update_failed";
@@ -26380,8 +26483,7 @@ var AgentCatalogPublishError = class extends Error {
26380
26483
  }
26381
26484
  };
26382
26485
  function catalogEndpoint(config2) {
26383
- const endpoint = new URL(config2.relayUrl);
26384
- endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
26486
+ const endpoint = relayHttpEndpoint(config2.relayUrl);
26385
26487
  if (endpoint.pathname.endsWith("/daemon")) {
26386
26488
  endpoint.pathname = `${endpoint.pathname.slice(0, -"/daemon".length)}/catalog`;
26387
26489
  return endpoint;
@@ -26412,7 +26514,7 @@ async function publishAgentCatalog(config2, signal) {
26412
26514
  installableAgents: catalog.installableAgents
26413
26515
  }),
26414
26516
  dispatcher: environmentProxyDispatcher,
26415
- headers: { "content-type": "application/json" },
26517
+ headers: { ...runtimeAuthorization(config2.relayUrl), "content-type": "application/json" },
26416
26518
  method: "PUT",
26417
26519
  signal: requestSignal
26418
26520
  });
@@ -28454,8 +28556,7 @@ function parseReference(resource) {
28454
28556
  return new PromptArtifactReference(artifactId, byteLength, sha256, resource);
28455
28557
  }
28456
28558
  function downloadUrl(config2, command, reference) {
28457
- const url2 = new URL(config2.relayUrl);
28458
- url2.protocol = url2.protocol === "wss:" ? "https:" : "http:";
28559
+ const url2 = relayHttpEndpoint(config2.relayUrl);
28459
28560
  let channelPath = "";
28460
28561
  if (url2.pathname.endsWith("/daemon")) {
28461
28562
  channelPath = url2.pathname.slice(0, -"/daemon".length);
@@ -28467,7 +28568,6 @@ function downloadUrl(config2, command, reference) {
28467
28568
  channelPath = `${url2.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
28468
28569
  }
28469
28570
  url2.pathname = `${channelPath}/sessions/${encodeURIComponent(command.sessionId)}/commands/${encodeURIComponent(command.commandId)}/artifacts/${encodeURIComponent(reference.artifactId)}/content`;
28470
- url2.searchParams.delete("liveness");
28471
28571
  return url2;
28472
28572
  }
28473
28573
  async function responseBytes(response, expectedByteLength, signal) {
@@ -28518,7 +28618,11 @@ async function download(config2, command, reference, signal) {
28518
28618
  try {
28519
28619
  response = await fetch4(downloadUrl(config2, command, reference), {
28520
28620
  dispatcher: environmentProxyDispatcher,
28521
- headers: { accept: reference.resource.mimeType },
28621
+ headers: {
28622
+ accept: reference.resource.mimeType,
28623
+ "accept-encoding": "identity",
28624
+ ...runtimeAuthorization(config2.relayUrl)
28625
+ },
28522
28626
  signal: requestSignal
28523
28627
  });
28524
28628
  if (!response.ok) {
@@ -28616,9 +28720,11 @@ import { WebSocket } from "undici";
28616
28720
  var CONNECTION_TIMEOUT_MILLISECONDS = 15e3;
28617
28721
  var RelayConnectionError = class extends Error {
28618
28722
  code;
28619
- constructor(code, message, options = {}) {
28723
+ diagnosticDetails;
28724
+ constructor(code, message, diagnosticDetails = {}, options = {}) {
28620
28725
  super(message, options);
28621
28726
  this.code = code;
28727
+ this.diagnosticDetails = diagnosticDetails;
28622
28728
  this.name = "RelayConnectionError";
28623
28729
  }
28624
28730
  };
@@ -28626,12 +28732,8 @@ function connectRelay(url2, signal) {
28626
28732
  signal.throwIfAborted();
28627
28733
  return new Promise((resolve6, reject) => {
28628
28734
  const endpoint = new URL(url2);
28629
- const headers = {};
28630
- if (endpoint.pathname.endsWith("/v1/runtimes/connect")) {
28631
- const runtimeCredential = endpoint.searchParams.get("token") ?? "";
28632
- endpoint.searchParams.delete("token");
28633
- headers["authorization"] = `Bearer ${runtimeCredential}`;
28634
- }
28735
+ const headers = runtimeAuthorization(endpoint);
28736
+ endpoint.searchParams.delete("token");
28635
28737
  const socket = new WebSocket(endpoint, {
28636
28738
  dispatcher: environmentProxyDispatcher,
28637
28739
  headers
@@ -28656,16 +28758,34 @@ function connectRelay(url2, signal) {
28656
28758
  };
28657
28759
  const onAbort = () => {
28658
28760
  fail(
28659
- new RelayConnectionError("relay_connection_cancelled", "Relay connection was cancelled", {
28660
- cause: signal.reason
28661
- })
28761
+ new RelayConnectionError(
28762
+ "relay_connection_cancelled",
28763
+ "Relay connection was cancelled",
28764
+ {
28765
+ cancellation: "requested"
28766
+ },
28767
+ { cause: signal.reason }
28768
+ )
28662
28769
  );
28663
28770
  };
28664
- const onClose = () => {
28665
- fail(new RelayConnectionError("relay_connection_closed", "Relay connection closed before it was ready"));
28771
+ const onClose = (event) => {
28772
+ fail(
28773
+ new RelayConnectionError("relay_connection_closed", "Relay connection closed before it was ready", {
28774
+ closeCode: event.code.toString(),
28775
+ closeReason: event.reason
28776
+ })
28777
+ );
28666
28778
  };
28667
- const onError = () => {
28668
- fail(new RelayConnectionError("relay_connection_failed", "Relay connection failed"));
28779
+ const onError = (event) => {
28780
+ const cause = Reflect.get(event, "error");
28781
+ fail(
28782
+ new RelayConnectionError(
28783
+ "relay_connection_failed",
28784
+ "Relay connection failed",
28785
+ {},
28786
+ { cause: cause ?? event }
28787
+ )
28788
+ );
28669
28789
  };
28670
28790
  const onOpen = () => {
28671
28791
  if (finished) {
@@ -28676,7 +28796,11 @@ function connectRelay(url2, signal) {
28676
28796
  resolve6(socket);
28677
28797
  };
28678
28798
  function onTimeout() {
28679
- fail(new RelayConnectionError("relay_connection_timeout", "Relay connection timed out"));
28799
+ fail(
28800
+ new RelayConnectionError("relay_connection_timeout", "Relay connection timed out", {
28801
+ timeoutMilliseconds: CONNECTION_TIMEOUT_MILLISECONDS.toString()
28802
+ })
28803
+ );
28680
28804
  }
28681
28805
  signal.addEventListener("abort", onAbort, { once: true });
28682
28806
  socket.addEventListener("close", onClose, { once: true });
@@ -28688,6 +28812,142 @@ function connectRelay(url2, signal) {
28688
28812
  });
28689
28813
  }
28690
28814
 
28815
+ // src/relay-connection/diagnostics.ts
28816
+ var MAX_DIAGNOSTIC_TEXT_LENGTH = 256;
28817
+ function sanitizeDiagnosticText(value) {
28818
+ const printable = Array.from(value, (character) => {
28819
+ const code = character.charCodeAt(0);
28820
+ return code < 32 || code === 127 ? " " : character;
28821
+ }).join("");
28822
+ const withoutSecrets = printable.replace(/([?&](?:access_token|authorization|credential|key|secret|token)=)[^&\s]*/giu, "$1[redacted]").replace(/(?:https?|wss?):\/\/[^\s)]+/giu, (encodedUrl) => {
28823
+ try {
28824
+ return new URL(encodedUrl).origin;
28825
+ } catch {
28826
+ return "[redacted-url]";
28827
+ }
28828
+ });
28829
+ return withoutSecrets.slice(0, MAX_DIAGNOSTIC_TEXT_LENGTH);
28830
+ }
28831
+ function addFailureDetails(details, failure) {
28832
+ let current = failure;
28833
+ for (let depth = 0; depth < 3 && current instanceof Error; depth += 1) {
28834
+ const prefix = depth === 0 ? "failure" : `cause${depth.toString()}`;
28835
+ details[`${prefix}Name`] = current.name;
28836
+ details[`${prefix}Message`] = sanitizeDiagnosticText(current.message);
28837
+ const code = Reflect.get(current, "code");
28838
+ if (typeof code === "string" || typeof code === "number") {
28839
+ details[`${prefix}Code`] = String(code);
28840
+ }
28841
+ const statusCode = Reflect.get(current, "statusCode");
28842
+ if (typeof statusCode === "string" || typeof statusCode === "number") {
28843
+ details[`${prefix}StatusCode`] = String(statusCode);
28844
+ }
28845
+ const diagnosticDetails = Reflect.get(current, "diagnosticDetails");
28846
+ if (typeof diagnosticDetails === "object" && diagnosticDetails !== null) {
28847
+ for (const [key, value] of Object.entries(diagnosticDetails)) {
28848
+ if (typeof value === "string") {
28849
+ details[key] = sanitizeDiagnosticText(value);
28850
+ }
28851
+ }
28852
+ }
28853
+ current = current.cause;
28854
+ }
28855
+ }
28856
+ var RelayConnectionDiagnostics = class {
28857
+ #audit;
28858
+ #channelId;
28859
+ #lastClosedEpoch = 0;
28860
+ #lastFailedEpoch = 0;
28861
+ #relayOrigin;
28862
+ #route;
28863
+ constructor(audit, channelId, relayUrl) {
28864
+ this.#audit = audit;
28865
+ this.#channelId = channelId;
28866
+ this.#relayOrigin = relayUrl.origin;
28867
+ this.#route = describeEnvironmentProxyRoute(relayUrl);
28868
+ }
28869
+ catalogFailed = (failure) => {
28870
+ const details = { ...this.#route, relayOrigin: this.#relayOrigin };
28871
+ addFailureDetails(details, failure);
28872
+ this.#record("agent_catalog_publish_failed", "catalog", details, true);
28873
+ };
28874
+ connectionClosed = (connectionEpoch, code, reason, source) => {
28875
+ if (this.#lastClosedEpoch === connectionEpoch) {
28876
+ return;
28877
+ }
28878
+ this.#lastClosedEpoch = connectionEpoch;
28879
+ this.#record(
28880
+ "relay_connection_closed",
28881
+ connectionEpoch.toString(),
28882
+ {
28883
+ closeCode: code.toString(),
28884
+ closeReason: sanitizeDiagnosticText(reason),
28885
+ connectionEpoch: connectionEpoch.toString(),
28886
+ relayOrigin: this.#relayOrigin,
28887
+ source,
28888
+ ...this.#route
28889
+ },
28890
+ true
28891
+ );
28892
+ };
28893
+ connectionFailed = (connectionEpoch, failure, source) => {
28894
+ if (this.#lastFailedEpoch === connectionEpoch) {
28895
+ return;
28896
+ }
28897
+ this.#lastFailedEpoch = connectionEpoch;
28898
+ const details = {
28899
+ connectionEpoch: connectionEpoch.toString(),
28900
+ relayOrigin: this.#relayOrigin,
28901
+ source,
28902
+ ...this.#route
28903
+ };
28904
+ addFailureDetails(details, failure);
28905
+ this.#record("relay_connection_failed", connectionEpoch.toString(), details, true);
28906
+ };
28907
+ connectionOpened = (connectionEpoch) => {
28908
+ this.#record(
28909
+ "relay_connection_opened",
28910
+ connectionEpoch.toString(),
28911
+ {
28912
+ connectionEpoch: connectionEpoch.toString(),
28913
+ relayOrigin: this.#relayOrigin,
28914
+ ...this.#route
28915
+ },
28916
+ false
28917
+ );
28918
+ };
28919
+ heartbeatTimedOut = (connectionEpoch, heartbeatId, timeoutMilliseconds) => {
28920
+ this.#record(
28921
+ "relay_heartbeat_timed_out",
28922
+ connectionEpoch.toString(),
28923
+ {
28924
+ connectionEpoch: connectionEpoch.toString(),
28925
+ heartbeatId,
28926
+ relayOrigin: this.#relayOrigin,
28927
+ timeoutMilliseconds: timeoutMilliseconds.toString(),
28928
+ ...this.#route
28929
+ },
28930
+ true
28931
+ );
28932
+ };
28933
+ #record = (event, commandId, details, warning) => {
28934
+ this.#audit.record({
28935
+ commandId,
28936
+ details,
28937
+ event,
28938
+ sessionId: this.#channelId
28939
+ });
28940
+ const encoded = JSON.stringify({ channelId: this.#channelId, event, ...details });
28941
+ if (warning) {
28942
+ process.stderr.write(`Mobius relay diagnostic ${encoded}
28943
+ `);
28944
+ return;
28945
+ }
28946
+ process.stdout.write(`Mobius relay diagnostic ${encoded}
28947
+ `);
28948
+ };
28949
+ };
28950
+
28691
28951
  // src/relay-connection/liveness.ts
28692
28952
  import { randomUUID as randomUUID3 } from "node:crypto";
28693
28953
  var HEARTBEAT_ACK_TIMEOUT_MILLISECONDS = 1e4;
@@ -28764,9 +29024,10 @@ var RelayConnectionLiveness = class {
28764
29024
  if (!(this.#state instanceof AwaitingHeartbeatAckState)) {
28765
29025
  throw new Error("Heartbeat timeout occurred in an invalid state");
28766
29026
  }
29027
+ const heartbeatId = this.#state.heartbeatId;
28767
29028
  this.#state.timeout.cancel();
28768
29029
  this.#state = new FinishedRelayLivenessState();
28769
- this.#transport.unresponsive();
29030
+ this.#transport.unresponsive(heartbeatId, HEARTBEAT_ACK_TIMEOUT_MILLISECONDS);
28770
29031
  };
28771
29032
  #sendHeartbeat = () => {
28772
29033
  if (this.#state instanceof WaitingForHeartbeatState) {
@@ -28795,6 +29056,42 @@ var RelayConnectionLiveness = class {
28795
29056
  import { mkdirSync as mkdirSync3 } from "node:fs";
28796
29057
  import { dirname as dirname7 } from "node:path";
28797
29058
  import { DatabaseSync as DatabaseSync4 } from "node:sqlite";
29059
+ var DEFAULT_OUTBOX_LIMITS = {
29060
+ criticalEventBytes: 136 * 1024 * 1024,
29061
+ criticalEventCount: 10128,
29062
+ runtimeDeliveryBytes: 64 * 1024 * 1024,
29063
+ runtimeDeliveryCount: 64,
29064
+ standardEventBytes: 128 * 1024 * 1024,
29065
+ standardEventCount: 1e4
29066
+ };
29067
+ var SessionEventOutboxCapacityError = class extends Error {
29068
+ code = "session_event_outbox_full";
29069
+ constructor() {
29070
+ super("The durable Session event outbox is full");
29071
+ this.name = "SessionEventOutboxCapacityError";
29072
+ }
29073
+ };
29074
+ var RuntimeDeliveryInboxCapacityError = class extends Error {
29075
+ code = "runtime_delivery_inbox_full";
29076
+ constructor() {
29077
+ super("The durable Runtime delivery inbox is full");
29078
+ this.name = "RuntimeDeliveryInboxCapacityError";
29079
+ }
29080
+ };
29081
+ var StoredRuntimeDelivery = class {
29082
+ deliveryId;
29083
+ duplicate;
29084
+ payload;
29085
+ sessionId;
29086
+ state;
29087
+ constructor(deliveryId, sessionId, payload, state, duplicate) {
29088
+ this.deliveryId = deliveryId;
29089
+ this.duplicate = duplicate;
29090
+ this.payload = payload;
29091
+ this.sessionId = sessionId;
29092
+ this.state = state;
29093
+ }
29094
+ };
28798
29095
  function readPendingEnvelope(row) {
28799
29096
  if (typeof row !== "object" || row === null || Array.isArray(row)) {
28800
29097
  throw new Error("SQLite returned an invalid session event outbox row");
@@ -28805,9 +29102,26 @@ function readPendingEnvelope(row) {
28805
29102
  }
28806
29103
  return envelope2;
28807
29104
  }
29105
+ function rowNumber(row, field) {
29106
+ const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
29107
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
29108
+ throw new Error(`SQLite returned an invalid ${field} value`);
29109
+ }
29110
+ return value;
29111
+ }
29112
+ function rowString2(row, field) {
29113
+ const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
29114
+ if (typeof value !== "string" || value.length === 0) {
29115
+ throw new Error(`SQLite returned an invalid ${field} value`);
29116
+ }
29117
+ return value;
29118
+ }
28808
29119
  var SessionEventOutbox = class {
28809
29120
  #database;
28810
- constructor(path2) {
29121
+ #limits;
29122
+ constructor(path2, limits = DEFAULT_OUTBOX_LIMITS) {
29123
+ validateLimits(limits);
29124
+ this.#limits = limits;
28811
29125
  mkdirSync3(dirname7(path2), { recursive: true });
28812
29126
  this.#database = new DatabaseSync4(path2);
28813
29127
  this.#database.exec(`
@@ -28815,6 +29129,8 @@ var SessionEventOutbox = class {
28815
29129
  ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
28816
29130
  event_id TEXT NOT NULL UNIQUE,
28817
29131
  session_id TEXT NOT NULL,
29132
+ source_key TEXT NOT NULL,
29133
+ byte_length INTEGER NOT NULL,
28818
29134
  envelope_json TEXT NOT NULL
28819
29135
  ) STRICT;
28820
29136
  CREATE INDEX IF NOT EXISTS relay_session_event_outbox_order
@@ -28825,7 +29141,7 @@ var SessionEventOutbox = class {
28825
29141
  session_id TEXT NOT NULL,
28826
29142
  envelope_json TEXT NOT NULL,
28827
29143
  rejection_code TEXT NOT NULL CHECK (
28828
- rejection_code IN ('session_deleted', 'session_not_found')
29144
+ rejection_code IN ('session_deleted', 'session_not_found', 'session_rejected')
28829
29145
  )
28830
29146
  ) STRICT;
28831
29147
  CREATE TABLE IF NOT EXISTS relay_session_event_sources (
@@ -28841,6 +29157,92 @@ var SessionEventOutbox = class {
28841
29157
  state IN ('dispatching', 'running', 'succeeded', 'failed', 'cancelled')
28842
29158
  )
28843
29159
  ) STRICT;
29160
+ CREATE TABLE IF NOT EXISTS relay_session_event_outbox_state (
29161
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
29162
+ event_count INTEGER NOT NULL,
29163
+ total_bytes INTEGER NOT NULL
29164
+ ) STRICT;
29165
+ INSERT OR IGNORE INTO relay_session_event_outbox_state (
29166
+ singleton, event_count, total_bytes
29167
+ ) VALUES (1, 0, 0);
29168
+ CREATE TABLE IF NOT EXISTS relay_runtime_deliveries (
29169
+ delivery_id TEXT PRIMARY KEY,
29170
+ session_id TEXT NOT NULL,
29171
+ payload_json TEXT NOT NULL,
29172
+ payload_bytes INTEGER NOT NULL,
29173
+ state TEXT NOT NULL CHECK (
29174
+ state IN ('received', 'relay_committed', 'processed')
29175
+ ),
29176
+ received_at INTEGER NOT NULL
29177
+ ) STRICT;
29178
+ `);
29179
+ const deadLetterTable = this.#database.prepare(
29180
+ `SELECT sql FROM sqlite_master
29181
+ WHERE type = 'table' AND name = 'relay_session_event_dead_letters'`
29182
+ ).get();
29183
+ const deadLetterSql = typeof deadLetterTable === "object" && deadLetterTable !== null ? Reflect.get(deadLetterTable, "sql") : void 0;
29184
+ if (typeof deadLetterSql !== "string" || !deadLetterSql.includes("'session_rejected'")) {
29185
+ this.#database.exec(`
29186
+ BEGIN IMMEDIATE;
29187
+ ALTER TABLE relay_session_event_dead_letters
29188
+ RENAME TO relay_session_event_dead_letters_legacy;
29189
+ CREATE TABLE relay_session_event_dead_letters (
29190
+ ordinal INTEGER PRIMARY KEY,
29191
+ event_id TEXT NOT NULL UNIQUE,
29192
+ session_id TEXT NOT NULL,
29193
+ envelope_json TEXT NOT NULL,
29194
+ rejection_code TEXT NOT NULL CHECK (
29195
+ rejection_code IN ('session_deleted', 'session_not_found', 'session_rejected')
29196
+ )
29197
+ ) STRICT;
29198
+ INSERT INTO relay_session_event_dead_letters (
29199
+ ordinal, event_id, session_id, envelope_json, rejection_code
29200
+ )
29201
+ SELECT ordinal, event_id, session_id, envelope_json, rejection_code
29202
+ FROM relay_session_event_dead_letters_legacy;
29203
+ DROP TABLE relay_session_event_dead_letters_legacy;
29204
+ COMMIT;
29205
+ `);
29206
+ }
29207
+ const outboxColumns = this.#database.prepare("PRAGMA table_info(relay_session_event_outbox)").all();
29208
+ const hasSourceKey = outboxColumns.some(
29209
+ (column) => typeof column === "object" && column !== null && Reflect.get(column, "name") === "source_key"
29210
+ );
29211
+ if (!hasSourceKey) {
29212
+ this.#database.exec(`
29213
+ ALTER TABLE relay_session_event_outbox
29214
+ ADD COLUMN source_key TEXT NOT NULL DEFAULT '';
29215
+ `);
29216
+ }
29217
+ const hasByteLength = outboxColumns.some(
29218
+ (column) => typeof column === "object" && column !== null && Reflect.get(column, "name") === "byte_length"
29219
+ );
29220
+ if (!hasByteLength) {
29221
+ this.#database.exec(`
29222
+ ALTER TABLE relay_session_event_outbox
29223
+ ADD COLUMN byte_length INTEGER NOT NULL DEFAULT 0;
29224
+ `);
29225
+ }
29226
+ this.#database.exec(`
29227
+ UPDATE relay_session_event_outbox
29228
+ SET source_key = CASE json_extract(envelope_json, '$.source.kind')
29229
+ WHEN 'attempt' THEN
29230
+ 'attempt:' || session_id || ':' || json_extract(envelope_json, '$.source.attemptId')
29231
+ ELSE 'runtime:' || session_id
29232
+ END
29233
+ WHERE source_key = '';
29234
+ UPDATE relay_session_event_outbox
29235
+ SET byte_length = length(CAST(envelope_json AS BLOB))
29236
+ WHERE byte_length = 0;
29237
+ UPDATE relay_session_event_outbox_state
29238
+ SET event_count = (SELECT COUNT(*) FROM relay_session_event_outbox),
29239
+ total_bytes = (
29240
+ SELECT COALESCE(SUM(byte_length), 0)
29241
+ FROM relay_session_event_outbox
29242
+ )
29243
+ WHERE singleton = 1;
29244
+ CREATE INDEX IF NOT EXISTS relay_session_event_outbox_source_order
29245
+ ON relay_session_event_outbox(source_key, ordinal);
28844
29246
  `);
28845
29247
  }
28846
29248
  claimExecution = (commandId, sessionId, commandJson) => {
@@ -28899,15 +29301,19 @@ var SessionEventOutbox = class {
28899
29301
  const state = typeof row === "object" && row !== null ? Reflect.get(row, "state") : void 0;
28900
29302
  return state === "succeeded" || state === "failed" || state === "cancelled";
28901
29303
  };
28902
- acknowledge = (acknowledgement) => {
28903
- this.#database.prepare(
28904
- `DELETE FROM relay_session_event_outbox
28905
- WHERE event_id = ? AND session_id = ?`
28906
- ).run(acknowledgement.eventId, acknowledgement.sessionId);
28907
- };
29304
+ acknowledge = (acknowledgement) => this.#removeAcknowledgedEvent(acknowledgement.eventId, acknowledgement.sessionId);
28908
29305
  quarantine = (rejection) => {
28909
29306
  this.#database.exec("BEGIN IMMEDIATE");
28910
29307
  try {
29308
+ const stored = this.#database.prepare(
29309
+ `SELECT byte_length FROM relay_session_event_outbox
29310
+ WHERE event_id = ? AND session_id = ?`
29311
+ ).get(rejection.eventId, rejection.sessionId);
29312
+ if (stored === void 0) {
29313
+ this.#database.exec("COMMIT");
29314
+ return false;
29315
+ }
29316
+ const byteLength = rowNumber(stored, "byte_length");
28911
29317
  const insertion = this.#database.prepare(
28912
29318
  `INSERT INTO relay_session_event_dead_letters (
28913
29319
  ordinal, event_id, session_id, envelope_json, rejection_code
@@ -28927,6 +29333,7 @@ var SessionEventOutbox = class {
28927
29333
  if (deletion.changes !== 1) {
28928
29334
  throw new Error("The rejected session event could not be removed from the durable outbox");
28929
29335
  }
29336
+ this.#decrementEventState(byteLength);
28930
29337
  this.#database.exec("COMMIT");
28931
29338
  return true;
28932
29339
  } catch (error61) {
@@ -28938,7 +29345,129 @@ var SessionEventOutbox = class {
28938
29345
  this.#database.close();
28939
29346
  };
28940
29347
  enqueue = (sessionId, payload, attemptId = null) => {
28941
- const sourceKey = attemptId === null ? `runtime:${sessionId}` : `attempt:${sessionId}:${attemptId}`;
29348
+ return this.#enqueueBounded(
29349
+ sessionId,
29350
+ payload,
29351
+ attemptId ?? "",
29352
+ this.#limits.standardEventCount,
29353
+ this.#limits.standardEventBytes
29354
+ );
29355
+ };
29356
+ enqueueCritical = (sessionId, payload, attemptId = "") => this.#enqueueBounded(
29357
+ sessionId,
29358
+ payload,
29359
+ attemptId,
29360
+ this.#limits.criticalEventCount,
29361
+ this.#limits.criticalEventBytes
29362
+ );
29363
+ claimRuntimeDelivery = (delivery) => {
29364
+ const payloadBytes = new TextEncoder().encode(delivery.payload).byteLength;
29365
+ this.#database.exec("BEGIN IMMEDIATE");
29366
+ try {
29367
+ const existing = this.#database.prepare(
29368
+ `SELECT session_id, payload_json, state
29369
+ FROM relay_runtime_deliveries
29370
+ WHERE delivery_id = ?`
29371
+ ).get(delivery.deliveryId);
29372
+ if (existing !== void 0) {
29373
+ const sessionId = rowString2(existing, "session_id");
29374
+ const payload = rowString2(existing, "payload_json");
29375
+ const state = runtimeDeliveryState(existing);
29376
+ if (sessionId !== delivery.sessionId || payload !== delivery.payload) {
29377
+ throw new Error("The Runtime delivery identity conflicts with its durable receipt");
29378
+ }
29379
+ this.#database.exec("COMMIT");
29380
+ return new StoredRuntimeDelivery(delivery.deliveryId, sessionId, payload, state, true);
29381
+ }
29382
+ const usage = this.#database.prepare(
29383
+ `SELECT COUNT(*) AS delivery_count,
29384
+ COALESCE(SUM(payload_bytes), 0) AS delivery_bytes
29385
+ FROM relay_runtime_deliveries`
29386
+ ).get();
29387
+ if (rowNumber(usage, "delivery_count") + 1 > this.#limits.runtimeDeliveryCount || rowNumber(usage, "delivery_bytes") + payloadBytes > this.#limits.runtimeDeliveryBytes) {
29388
+ throw new RuntimeDeliveryInboxCapacityError();
29389
+ }
29390
+ this.#database.prepare(
29391
+ `INSERT INTO relay_runtime_deliveries (
29392
+ delivery_id, session_id, payload_json, payload_bytes, state, received_at
29393
+ ) VALUES (?, ?, ?, ?, 'received', ?)`
29394
+ ).run(delivery.deliveryId, delivery.sessionId, delivery.payload, payloadBytes, Date.now());
29395
+ this.#database.exec("COMMIT");
29396
+ return new StoredRuntimeDelivery(
29397
+ delivery.deliveryId,
29398
+ delivery.sessionId,
29399
+ delivery.payload,
29400
+ "received",
29401
+ false
29402
+ );
29403
+ } catch (error61) {
29404
+ this.#database.exec("ROLLBACK");
29405
+ throw error61;
29406
+ }
29407
+ };
29408
+ markRuntimeDeliveryCommitted = (deliveryId, sessionId) => {
29409
+ this.#database.exec("BEGIN IMMEDIATE");
29410
+ try {
29411
+ const row = this.#database.prepare(
29412
+ `SELECT state FROM relay_runtime_deliveries
29413
+ WHERE delivery_id = ? AND session_id = ?`
29414
+ ).get(deliveryId, sessionId);
29415
+ if (row === void 0) {
29416
+ this.#database.exec("COMMIT");
29417
+ return false;
29418
+ }
29419
+ const state = runtimeDeliveryState(row);
29420
+ if (state === "processed") {
29421
+ this.#database.prepare(`DELETE FROM relay_runtime_deliveries WHERE delivery_id = ?`).run(deliveryId);
29422
+ } else if (state === "received") {
29423
+ this.#database.prepare(`UPDATE relay_runtime_deliveries SET state = 'relay_committed' WHERE delivery_id = ?`).run(deliveryId);
29424
+ }
29425
+ this.#database.exec("COMMIT");
29426
+ return true;
29427
+ } catch (error61) {
29428
+ this.#database.exec("ROLLBACK");
29429
+ throw error61;
29430
+ }
29431
+ };
29432
+ markRuntimeDeliveryProcessed = (deliveryId, sessionId) => {
29433
+ this.#database.exec("BEGIN IMMEDIATE");
29434
+ try {
29435
+ const row = this.#database.prepare(
29436
+ `SELECT state FROM relay_runtime_deliveries
29437
+ WHERE delivery_id = ? AND session_id = ?`
29438
+ ).get(deliveryId, sessionId);
29439
+ if (row === void 0) {
29440
+ this.#database.exec("COMMIT");
29441
+ return false;
29442
+ }
29443
+ const state = runtimeDeliveryState(row);
29444
+ if (state === "relay_committed") {
29445
+ this.#database.prepare(`DELETE FROM relay_runtime_deliveries WHERE delivery_id = ?`).run(deliveryId);
29446
+ } else if (state === "received") {
29447
+ this.#database.prepare(`UPDATE relay_runtime_deliveries SET state = 'processed' WHERE delivery_id = ?`).run(deliveryId);
29448
+ }
29449
+ this.#database.exec("COMMIT");
29450
+ return true;
29451
+ } catch (error61) {
29452
+ this.#database.exec("ROLLBACK");
29453
+ throw error61;
29454
+ }
29455
+ };
29456
+ runtimeDeliveries = () => this.#database.prepare(
29457
+ `SELECT delivery_id, session_id, payload_json, state
29458
+ FROM relay_runtime_deliveries
29459
+ ORDER BY received_at, delivery_id`
29460
+ ).all().map(
29461
+ (row) => new StoredRuntimeDelivery(
29462
+ rowString2(row, "delivery_id"),
29463
+ rowString2(row, "session_id"),
29464
+ rowString2(row, "payload_json"),
29465
+ runtimeDeliveryState(row),
29466
+ true
29467
+ )
29468
+ );
29469
+ #enqueueBounded = (sessionId, payload, attemptId, maximumEventCount, maximumEventBytes) => {
29470
+ const sourceKey = attemptId.length === 0 ? `runtime:${sessionId}` : `attempt:${sessionId}:${attemptId}`;
28942
29471
  this.#database.exec("BEGIN IMMEDIATE");
28943
29472
  try {
28944
29473
  const row = this.#database.prepare(
@@ -28955,13 +29484,22 @@ var SessionEventOutbox = class {
28955
29484
  kind: SESSION_EVENT_KIND,
28956
29485
  payload,
28957
29486
  sessionId,
28958
- source: attemptId === null ? { kind: "runtime", sourceSeq: nextSourceSeq.toString() } : {
29487
+ source: attemptId.length === 0 ? { kind: "runtime", sourceSeq: nextSourceSeq.toString() } : {
28959
29488
  attemptId,
28960
29489
  kind: "attempt",
28961
29490
  sourceSeq: nextSourceSeq.toString()
28962
29491
  }
28963
29492
  };
28964
29493
  const encoded = encodeSessionEventEnvelope(envelope2);
29494
+ const byteLength = new TextEncoder().encode(encoded).byteLength;
29495
+ const state = this.#database.prepare(
29496
+ `SELECT event_count, total_bytes
29497
+ FROM relay_session_event_outbox_state
29498
+ WHERE singleton = 1`
29499
+ ).get();
29500
+ if (rowNumber(state, "event_count") + 1 > maximumEventCount || rowNumber(state, "total_bytes") + byteLength > maximumEventBytes) {
29501
+ throw new SessionEventOutboxCapacityError();
29502
+ }
28965
29503
  this.#database.prepare(
28966
29504
  `INSERT INTO relay_session_event_sources (
28967
29505
  source_key, next_source_seq
@@ -28971,9 +29509,15 @@ var SessionEventOutbox = class {
28971
29509
  ).run(sourceKey, nextSourceSeq + 1);
28972
29510
  this.#database.prepare(
28973
29511
  `INSERT INTO relay_session_event_outbox (
28974
- event_id, session_id, envelope_json
28975
- ) VALUES (?, ?, ?)`
28976
- ).run(envelope2.eventId, envelope2.sessionId, encoded);
29512
+ event_id, session_id, source_key, byte_length, envelope_json
29513
+ ) VALUES (?, ?, ?, ?, ?)`
29514
+ ).run(envelope2.eventId, envelope2.sessionId, sourceKey, byteLength, encoded);
29515
+ this.#database.prepare(
29516
+ `UPDATE relay_session_event_outbox_state
29517
+ SET event_count = event_count + 1,
29518
+ total_bytes = total_bytes + ?
29519
+ WHERE singleton = 1`
29520
+ ).run(byteLength);
28977
29521
  this.#database.exec("COMMIT");
28978
29522
  return encoded;
28979
29523
  } catch (error61) {
@@ -28981,12 +29525,132 @@ var SessionEventOutbox = class {
28981
29525
  throw error61;
28982
29526
  }
28983
29527
  };
29528
+ #removeAcknowledgedEvent = (eventId, sessionId) => {
29529
+ this.#database.exec("BEGIN IMMEDIATE");
29530
+ try {
29531
+ const stored = this.#database.prepare(
29532
+ `SELECT byte_length FROM relay_session_event_outbox
29533
+ WHERE event_id = ? AND session_id = ?`
29534
+ ).get(eventId, sessionId);
29535
+ if (stored === void 0) {
29536
+ this.#database.exec("COMMIT");
29537
+ return false;
29538
+ }
29539
+ const byteLength = rowNumber(stored, "byte_length");
29540
+ const deletion = this.#database.prepare(
29541
+ `DELETE FROM relay_session_event_outbox
29542
+ WHERE event_id = ? AND session_id = ?`
29543
+ ).run(eventId, sessionId);
29544
+ if (deletion.changes !== 1) {
29545
+ throw new Error("The acknowledged session event could not be removed from the durable outbox");
29546
+ }
29547
+ this.#decrementEventState(byteLength);
29548
+ this.#database.exec("COMMIT");
29549
+ return true;
29550
+ } catch (error61) {
29551
+ this.#database.exec("ROLLBACK");
29552
+ throw error61;
29553
+ }
29554
+ };
29555
+ #decrementEventState = (byteLength) => {
29556
+ const result = this.#database.prepare(
29557
+ `UPDATE relay_session_event_outbox_state
29558
+ SET event_count = event_count - 1,
29559
+ total_bytes = total_bytes - ?
29560
+ WHERE singleton = 1 AND event_count > 0 AND total_bytes >= ?`
29561
+ ).run(byteLength, byteLength);
29562
+ if (result.changes !== 1) {
29563
+ throw new Error("The Session event outbox capacity state is inconsistent");
29564
+ }
29565
+ };
28984
29566
  pending = () => this.#database.prepare(
28985
29567
  `SELECT envelope_json
28986
29568
  FROM relay_session_event_outbox
28987
29569
  ORDER BY ordinal`
28988
29570
  ).all().map(readPendingEnvelope);
28989
- };
29571
+ pendingBatch = (limit) => {
29572
+ if (!Number.isSafeInteger(limit) || limit < 1) {
29573
+ throw new Error("Session event replay batch limit must be a positive integer");
29574
+ }
29575
+ return this.#database.prepare(
29576
+ `WITH ranked_events AS (
29577
+ SELECT ordinal,
29578
+ ROW_NUMBER() OVER (
29579
+ PARTITION BY source_key
29580
+ ORDER BY ordinal
29581
+ ) AS source_position
29582
+ FROM relay_session_event_outbox
29583
+ )
29584
+ SELECT outbox.envelope_json
29585
+ FROM ranked_events AS ranked
29586
+ INNER JOIN relay_session_event_outbox AS outbox
29587
+ ON outbox.ordinal = ranked.ordinal
29588
+ ORDER BY ranked.source_position, outbox.ordinal
29589
+ LIMIT ?`
29590
+ ).all(limit).map(readPendingEnvelope);
29591
+ };
29592
+ pendingSourceHeads = (limit, excludedEventIds, excludedSourceKeys) => {
29593
+ if (!Number.isSafeInteger(limit) || limit < 1) {
29594
+ throw new Error("Session event replay batch limit must be a positive integer");
29595
+ }
29596
+ const exclusions = [];
29597
+ const parameters = [];
29598
+ if (excludedEventIds.length > 0) {
29599
+ exclusions.push(`event_id NOT IN (${excludedEventIds.map(() => "?").join(", ")})`);
29600
+ parameters.push(...excludedEventIds);
29601
+ }
29602
+ if (excludedSourceKeys.length > 0) {
29603
+ exclusions.push(`source_key NOT IN (${excludedSourceKeys.map(() => "?").join(", ")})`);
29604
+ parameters.push(...excludedSourceKeys);
29605
+ }
29606
+ parameters.push(limit.toString());
29607
+ const where = exclusions.length === 0 ? "" : `WHERE ${exclusions.join(" AND ")}`;
29608
+ return this.#database.prepare(
29609
+ `WITH available_events AS (
29610
+ SELECT ordinal, source_key
29611
+ FROM relay_session_event_outbox
29612
+ ${where}
29613
+ ), ranked_events AS (
29614
+ SELECT ordinal,
29615
+ ROW_NUMBER() OVER (
29616
+ PARTITION BY source_key
29617
+ ORDER BY ordinal
29618
+ ) AS source_position
29619
+ FROM available_events
29620
+ )
29621
+ SELECT outbox.envelope_json
29622
+ FROM ranked_events AS ranked
29623
+ INNER JOIN relay_session_event_outbox AS outbox
29624
+ ON outbox.ordinal = ranked.ordinal
29625
+ WHERE ranked.source_position = 1
29626
+ ORDER BY outbox.ordinal
29627
+ LIMIT ?`
29628
+ ).all(...parameters).map(readPendingEnvelope);
29629
+ };
29630
+ };
29631
+ function validateLimits(limits) {
29632
+ const values = [
29633
+ limits.criticalEventBytes,
29634
+ limits.criticalEventCount,
29635
+ limits.runtimeDeliveryBytes,
29636
+ limits.runtimeDeliveryCount,
29637
+ limits.standardEventBytes,
29638
+ limits.standardEventCount
29639
+ ];
29640
+ if (values.some((value) => !Number.isSafeInteger(value) || value < 1)) {
29641
+ throw new Error("Session event outbox limits must be positive integers");
29642
+ }
29643
+ if (limits.criticalEventBytes < limits.standardEventBytes || limits.criticalEventCount < limits.standardEventCount) {
29644
+ throw new Error("Critical Session event capacity must include the standard capacity");
29645
+ }
29646
+ }
29647
+ function runtimeDeliveryState(row) {
29648
+ const state = rowString2(row, "state");
29649
+ if (state !== "received" && state !== "relay_committed" && state !== "processed") {
29650
+ throw new Error("SQLite returned an invalid Runtime delivery state");
29651
+ }
29652
+ return state;
29653
+ }
28990
29654
  function isDaemonExecutionState(value) {
28991
29655
  return value === "cancelled" || value === "dispatching" || value === "failed" || value === "running" || value === "succeeded";
28992
29656
  }
@@ -29002,7 +29666,7 @@ var systemDaemonTimer = new SystemDaemonTimer();
29002
29666
 
29003
29667
  // src/run-daemon.ts
29004
29668
  var RELAY_RECONNECT_DELAY_MILLISECONDS = 500;
29005
- var RELAY_CONNECTION_REFRESH_MILLISECONDS = 20 * 60 * 1e3;
29669
+ var AGENT_ACTIVITY_COALESCING_MILLISECONDS = 100;
29006
29670
  var ACTIVE_TURN_RECONCILIATION_TIMEOUT_MILLISECONDS = 2e4;
29007
29671
  var ACTIVE_TURN_RECONCILIATION_CLOSE_CODE = 4009;
29008
29672
  function botIpcSocketPath() {
@@ -29011,54 +29675,6 @@ function botIpcSocketPath() {
29011
29675
  }
29012
29676
  return join15("/tmp", `mobius-bot-${randomUUID4()}.sock`);
29013
29677
  }
29014
- var RelayConnectionRefreshState = class {
29015
- };
29016
- var ScheduledRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
29017
- };
29018
- var DueRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
29019
- };
29020
- var FinishedRelayConnectionRefreshState = class extends RelayConnectionRefreshState {
29021
- };
29022
- var RelayConnectionRefresh = class {
29023
- #isSafe;
29024
- #refresh;
29025
- #scheduledTask;
29026
- #state = new ScheduledRelayConnectionRefreshState();
29027
- constructor(timer, isSafe, refresh) {
29028
- this.#isSafe = isSafe;
29029
- this.#refresh = refresh;
29030
- this.#scheduledTask = timer.schedule(this.#request, RELAY_CONNECTION_REFRESH_MILLISECONDS);
29031
- }
29032
- afterTurnFinished = () => {
29033
- if (this.#state instanceof DueRelayConnectionRefreshState) {
29034
- this.#request();
29035
- return;
29036
- }
29037
- if (this.#state instanceof ScheduledRelayConnectionRefreshState || this.#state instanceof FinishedRelayConnectionRefreshState) {
29038
- return;
29039
- }
29040
- throw new Error("Unknown relay connection refresh state");
29041
- };
29042
- close = () => {
29043
- this.#scheduledTask.cancel();
29044
- this.#state = new FinishedRelayConnectionRefreshState();
29045
- };
29046
- #request = () => {
29047
- if (this.#state instanceof FinishedRelayConnectionRefreshState) {
29048
- return;
29049
- }
29050
- if (!(this.#state instanceof ScheduledRelayConnectionRefreshState) && !(this.#state instanceof DueRelayConnectionRefreshState)) {
29051
- throw new Error("Unknown relay connection refresh state");
29052
- }
29053
- if (!this.#isSafe()) {
29054
- this.#state = new DueRelayConnectionRefreshState();
29055
- return;
29056
- }
29057
- this.#state = new FinishedRelayConnectionRefreshState();
29058
- this.#scheduledTask.cancel();
29059
- this.#refresh();
29060
- };
29061
- };
29062
29678
  var DaemonAcpObserver = class {
29063
29679
  #record;
29064
29680
  constructor(record2) {
@@ -29253,8 +29869,7 @@ var ActiveTurnReconciliationError = class extends Error {
29253
29869
  }
29254
29870
  };
29255
29871
  function activeTurnReconciliationEndpoint(config2, turn) {
29256
- const endpoint = new URL(config2.relayUrl);
29257
- endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
29872
+ const endpoint = relayHttpEndpoint(config2.relayUrl);
29258
29873
  let channelPath = "";
29259
29874
  if (endpoint.pathname.endsWith("/daemon")) {
29260
29875
  channelPath = endpoint.pathname.slice(0, -"/daemon".length);
@@ -29266,7 +29881,6 @@ function activeTurnReconciliationEndpoint(config2, turn) {
29266
29881
  channelPath = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config2.channelId)}`;
29267
29882
  }
29268
29883
  endpoint.pathname = `${channelPath}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
29269
- endpoint.searchParams.delete("liveness");
29270
29884
  return endpoint;
29271
29885
  }
29272
29886
  async function reconcileActiveTurn(config2, turn, signal) {
@@ -29278,7 +29892,7 @@ async function reconcileActiveTurn(config2, turn, signal) {
29278
29892
  response = await fetch5(activeTurnReconciliationEndpoint(config2, turn), {
29279
29893
  body: JSON.stringify({ outcome: { kind: "running" } }),
29280
29894
  dispatcher: environmentProxyDispatcher,
29281
- headers: { "content-type": "application/json" },
29895
+ headers: { ...runtimeAuthorization(config2.relayUrl), "content-type": "application/json" },
29282
29896
  method: "POST",
29283
29897
  signal: requestSignal
29284
29898
  });
@@ -29323,7 +29937,8 @@ var DaemonSessionEventConnectionState = class {
29323
29937
  };
29324
29938
  var DisconnectedDaemonSessionEventState = class extends DaemonSessionEventConnectionState {
29325
29939
  disconnect = () => this;
29326
- send = () => void 0;
29940
+ matches = () => false;
29941
+ send = () => false;
29327
29942
  };
29328
29943
  var ConnectedDaemonSessionEventState = class extends DaemonSessionEventConnectionState {
29329
29944
  #socket;
@@ -29332,38 +29947,210 @@ var ConnectedDaemonSessionEventState = class extends DaemonSessionEventConnectio
29332
29947
  this.#socket = socket;
29333
29948
  }
29334
29949
  disconnect = (socket) => socket === this.#socket ? new DisconnectedDaemonSessionEventState() : this;
29950
+ matches = (socket) => socket === this.#socket;
29335
29951
  send = (message) => {
29336
- if (this.#socket.readyState === WebSocket2.OPEN) {
29337
- try {
29338
- this.#socket.send(message);
29339
- } catch {
29340
- }
29952
+ if (this.#socket.readyState !== WebSocket2.OPEN) {
29953
+ return false;
29954
+ }
29955
+ try {
29956
+ this.#socket.send(message);
29957
+ return true;
29958
+ } catch {
29959
+ return false;
29341
29960
  }
29342
29961
  };
29343
29962
  };
29963
+ var SESSION_EVENT_REPLAY_WINDOW = 16;
29964
+ var SESSION_EVENT_SOURCE_REPLAY_WINDOW = 4;
29965
+ var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
29966
+ var InFlightSessionEvent = class {
29967
+ byteLength;
29968
+ sourceKey;
29969
+ constructor(sourceKey, byteLength) {
29970
+ this.byteLength = byteLength;
29971
+ this.sourceKey = sourceKey;
29972
+ }
29973
+ };
29974
+ function sessionEventSourceKey(envelope2) {
29975
+ return envelope2.source.kind === "attempt" ? `attempt:${envelope2.sessionId}:${envelope2.source.attemptId}` : `runtime:${envelope2.sessionId}`;
29976
+ }
29344
29977
  var DaemonSessionEventPublisher = class {
29345
29978
  #outbox;
29979
+ #inFlightEvents = /* @__PURE__ */ new Map();
29980
+ #inFlightBytes = 0;
29346
29981
  #connection = new DisconnectedDaemonSessionEventState();
29347
29982
  constructor(outbox) {
29348
29983
  this.#outbox = outbox;
29349
29984
  }
29350
29985
  connect = (socket) => {
29351
- const connection = new ConnectedDaemonSessionEventState(socket);
29352
- this.#connection = connection;
29353
- for (const pending of this.#outbox.pending()) {
29354
- connection.send(pending);
29355
- }
29986
+ this.#connection = new ConnectedDaemonSessionEventState(socket);
29987
+ this.#clearInFlight();
29988
+ this.#publishNextBatch();
29356
29989
  };
29357
29990
  disconnect = (socket) => {
29991
+ if (!this.#connection.matches(socket)) {
29992
+ return;
29993
+ }
29358
29994
  this.#connection = this.#connection.disconnect(socket);
29995
+ this.#clearInFlight();
29359
29996
  };
29360
29997
  recordAttempt = (sessionId, payload, attemptId) => {
29361
- this.#connection.send(this.#outbox.enqueue(sessionId, payload, attemptId));
29998
+ const kind = decodeMessageKind(payload);
29999
+ if (kind === SESSION_COMPLETED_KIND || kind === SESSION_FAILED_KIND) {
30000
+ this.#outbox.enqueueCritical(sessionId, payload, attemptId);
30001
+ } else {
30002
+ this.#outbox.enqueue(sessionId, payload, attemptId);
30003
+ }
30004
+ this.#publishNextBatch();
29362
30005
  };
29363
30006
  recordRuntime = (sessionId, payload) => {
29364
- this.#connection.send(this.#outbox.enqueue(sessionId, payload));
30007
+ this.#outbox.enqueue(sessionId, payload);
30008
+ this.#publishNextBatch();
30009
+ };
30010
+ acknowledge = (acknowledgement) => {
30011
+ if (!this.#outbox.acknowledge(acknowledgement)) {
30012
+ return;
30013
+ }
30014
+ this.#removeInFlight(acknowledgement.eventId);
30015
+ this.#publishNextBatch();
30016
+ };
30017
+ quarantine = (rejection) => {
30018
+ const quarantined = this.#outbox.quarantine(rejection);
30019
+ if (!quarantined) {
30020
+ return false;
30021
+ }
30022
+ this.#removeInFlight(rejection.eventId);
30023
+ this.#publishNextBatch();
30024
+ return true;
30025
+ };
30026
+ #publishNextBatch = () => {
30027
+ while (this.#inFlightEvents.size < SESSION_EVENT_REPLAY_WINDOW) {
30028
+ const sourceCounts = /* @__PURE__ */ new Map();
30029
+ for (const inFlight of this.#inFlightEvents.values()) {
30030
+ sourceCounts.set(inFlight.sourceKey, (sourceCounts.get(inFlight.sourceKey) ?? 0) + 1);
30031
+ }
30032
+ const saturatedSources = [...sourceCounts.entries()].filter(([, count]) => count >= SESSION_EVENT_SOURCE_REPLAY_WINDOW).map(([sourceKey]) => sourceKey);
30033
+ const batch = this.#outbox.pendingSourceHeads(
30034
+ SESSION_EVENT_REPLAY_WINDOW - this.#inFlightEvents.size,
30035
+ [...this.#inFlightEvents.keys()],
30036
+ saturatedSources
30037
+ );
30038
+ let sent = 0;
30039
+ for (const encoded of batch) {
30040
+ const envelope2 = decodeSessionEventEnvelope(encoded);
30041
+ const sourceKey = sessionEventSourceKey(envelope2);
30042
+ const sourceCount = sourceCounts.get(sourceKey) ?? 0;
30043
+ if (sourceCount >= SESSION_EVENT_SOURCE_REPLAY_WINDOW) {
30044
+ continue;
30045
+ }
30046
+ const byteLength = new TextEncoder().encode(encoded).byteLength;
30047
+ if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
30048
+ continue;
30049
+ }
30050
+ if (!this.#connection.send(encoded)) {
30051
+ this.#clearInFlight();
30052
+ return;
30053
+ }
30054
+ this.#inFlightEvents.set(envelope2.eventId, new InFlightSessionEvent(sourceKey, byteLength));
30055
+ this.#inFlightBytes += byteLength;
30056
+ sourceCounts.set(sourceKey, sourceCount + 1);
30057
+ sent += 1;
30058
+ }
30059
+ if (sent === 0) {
30060
+ return;
30061
+ }
30062
+ }
30063
+ };
30064
+ #removeInFlight = (eventId) => {
30065
+ const inFlight = this.#inFlightEvents.get(eventId);
30066
+ if (inFlight === void 0) {
30067
+ return;
30068
+ }
30069
+ this.#inFlightEvents.delete(eventId);
30070
+ this.#inFlightBytes -= inFlight.byteLength;
30071
+ };
30072
+ #clearInFlight = () => {
30073
+ this.#inFlightEvents.clear();
30074
+ this.#inFlightBytes = 0;
29365
30075
  };
29366
30076
  };
30077
+ var CoalescingDaemonActivityPublisher = class {
30078
+ #pending = /* @__PURE__ */ new Map();
30079
+ #publish;
30080
+ #publishedActivityIds = /* @__PURE__ */ new Set();
30081
+ #scheduled = /* @__PURE__ */ new Map();
30082
+ #timer;
30083
+ constructor(timer, publish) {
30084
+ this.#publish = publish;
30085
+ this.#timer = timer;
30086
+ }
30087
+ flush = () => {
30088
+ const activities = [...this.#pending.values()].sort((left, right) => left.sequence - right.sequence);
30089
+ for (const task of this.#scheduled.values()) {
30090
+ task.cancel();
30091
+ }
30092
+ this.#pending.clear();
30093
+ this.#scheduled.clear();
30094
+ for (const activity of activities) {
30095
+ this.#publish(activity);
30096
+ }
30097
+ };
30098
+ record = (activity) => {
30099
+ if (activity.status !== "pending" && activity.status !== "running") {
30100
+ this.#discardPending(activity.activityId);
30101
+ this.#publishedActivityIds.add(activity.activityId);
30102
+ this.#publish(activity);
30103
+ return Promise.resolve();
30104
+ }
30105
+ if (!this.#publishedActivityIds.has(activity.activityId)) {
30106
+ this.#publishedActivityIds.add(activity.activityId);
30107
+ this.#publish(activity);
30108
+ return Promise.resolve();
30109
+ }
30110
+ this.#pending.set(activity.activityId, activity);
30111
+ if (!this.#scheduled.has(activity.activityId)) {
30112
+ this.#scheduled.set(
30113
+ activity.activityId,
30114
+ this.#timer.schedule(
30115
+ () => this.#flushActivity(activity.activityId),
30116
+ AGENT_ACTIVITY_COALESCING_MILLISECONDS
30117
+ )
30118
+ );
30119
+ }
30120
+ return Promise.resolve();
30121
+ };
30122
+ #discardPending = (activityId) => {
30123
+ this.#scheduled.get(activityId)?.cancel();
30124
+ this.#scheduled.delete(activityId);
30125
+ this.#pending.delete(activityId);
30126
+ };
30127
+ #flushActivity = (activityId) => {
30128
+ const activity = this.#pending.get(activityId);
30129
+ this.#discardPending(activityId);
30130
+ if (activity !== void 0) {
30131
+ this.#publish(activity);
30132
+ }
30133
+ };
30134
+ };
30135
+ function encodeDaemonActivity(command, activity) {
30136
+ return encodeAgentActivityEvent({
30137
+ activityId: activity.activityId,
30138
+ activityType: activity.activityType,
30139
+ commandId: command.commandId,
30140
+ content: activity.content,
30141
+ detail: activity.detail,
30142
+ input: activity.input,
30143
+ kind: AGENT_ACTIVITY_KIND,
30144
+ locations: activity.locations,
30145
+ name: activity.name,
30146
+ output: activity.output,
30147
+ sequence: activity.sequence,
30148
+ sessionId: command.sessionId,
30149
+ status: activity.status,
30150
+ title: activity.title,
30151
+ toolKind: activity.toolKind
30152
+ });
30153
+ }
29367
30154
  function reportFailure(publisher, command, code, message, attemptId) {
29368
30155
  publisher.recordAttempt(
29369
30156
  command.sessionId,
@@ -29537,10 +30324,16 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
29537
30324
  })
29538
30325
  );
29539
30326
  } catch (error61) {
30327
+ if (signal.aborted) {
30328
+ signal.throwIfAborted();
30329
+ }
29540
30330
  reportOperationFailure(socket, command.requestId, command.sessionId, error61);
29541
30331
  }
29542
30332
  }
29543
- async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, signal) {
30333
+ async function executePrompt(outbox, publisher, command, config2, directories, audit, sessions, attemptId, timer, signal) {
30334
+ const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
30335
+ publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
30336
+ });
29544
30337
  try {
29545
30338
  const resolved = await resolveSessionDirectories(
29546
30339
  directories,
@@ -29597,30 +30390,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
29597
30390
  cwd: resolved.cwd,
29598
30391
  disposition: command.disposition,
29599
30392
  images: attachments.images,
29600
- onActivity: (activity) => {
29601
- publisher.recordAttempt(
29602
- command.sessionId,
29603
- encodeAgentActivityEvent({
29604
- activityId: activity.activityId,
29605
- activityType: activity.activityType,
29606
- commandId: command.commandId,
29607
- content: activity.content,
29608
- detail: activity.detail,
29609
- input: activity.input,
29610
- kind: AGENT_ACTIVITY_KIND,
29611
- locations: activity.locations,
29612
- name: activity.name,
29613
- output: activity.output,
29614
- sequence: activity.sequence,
29615
- sessionId: command.sessionId,
29616
- status: activity.status,
29617
- title: activity.title,
29618
- toolKind: activity.toolKind
29619
- }),
29620
- attemptId
29621
- );
29622
- return Promise.resolve();
29623
- },
30393
+ onActivity: activities.record,
29624
30394
  onText: (text, messageId) => {
29625
30395
  publisher.recordAttempt(
29626
30396
  command.sessionId,
@@ -29643,6 +30413,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
29643
30413
  });
29644
30414
  stopReason = result.stopReason;
29645
30415
  }
30416
+ activities.flush();
29646
30417
  publisher.recordAttempt(
29647
30418
  command.sessionId,
29648
30419
  encodeSessionCompletedEvent({
@@ -29660,6 +30431,7 @@ async function executePrompt(outbox, publisher, command, config2, directories, a
29660
30431
  sessionId: command.sessionId
29661
30432
  });
29662
30433
  } catch (error61) {
30434
+ activities.flush();
29663
30435
  audit.record({
29664
30436
  commandId: command.commandId,
29665
30437
  event: "session_failed",
@@ -29698,7 +30470,10 @@ function botExecutionAgent(agent, contextHandle, socketPath) {
29698
30470
  agent.capabilities
29699
30471
  );
29700
30472
  }
29701
- async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, signal) {
30473
+ async function executeBotPrompt(outbox, publisher, command, config2, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, timer, signal) {
30474
+ const activities = new CoalescingDaemonActivityPublisher(timer, (activity) => {
30475
+ publisher.recordAttempt(command.sessionId, encodeDaemonActivity(command, activity), attemptId);
30476
+ });
29702
30477
  try {
29703
30478
  const deadline = Date.parse(command.executionDeadlineAt);
29704
30479
  const remaining = deadline - Date.now();
@@ -29782,30 +30557,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
29782
30557
  commandId: command.commandId,
29783
30558
  cwd: resolved.cwd,
29784
30559
  images: attachments.images,
29785
- onActivity: (activity) => {
29786
- publisher.recordAttempt(
29787
- command.sessionId,
29788
- encodeAgentActivityEvent({
29789
- activityId: activity.activityId,
29790
- activityType: activity.activityType,
29791
- commandId: command.commandId,
29792
- content: activity.content,
29793
- detail: activity.detail,
29794
- input: activity.input,
29795
- kind: AGENT_ACTIVITY_KIND,
29796
- locations: activity.locations,
29797
- name: activity.name,
29798
- output: activity.output,
29799
- sequence: activity.sequence,
29800
- sessionId: command.sessionId,
29801
- status: activity.status,
29802
- title: activity.title,
29803
- toolKind: activity.toolKind
29804
- }),
29805
- attemptId
29806
- );
29807
- return Promise.resolve();
29808
- },
30560
+ onActivity: activities.record,
29809
30561
  onText: (text, messageId) => {
29810
30562
  publisher.recordAttempt(
29811
30563
  command.sessionId,
@@ -29825,6 +30577,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
29825
30577
  sessionId: command.sessionId,
29826
30578
  signal: executionSignal
29827
30579
  });
30580
+ activities.flush();
29828
30581
  publisher.recordAttempt(
29829
30582
  command.sessionId,
29830
30583
  encodeSessionCompletedEvent({
@@ -29849,6 +30602,7 @@ async function executeBotPrompt(outbox, publisher, command, config2, directories
29849
30602
  lease.release();
29850
30603
  }
29851
30604
  } catch (error61) {
30605
+ activities.flush();
29852
30606
  const failure = Date.parse(command.executionDeadlineAt) <= Date.now() ? new LocalBotAuthorizationError("turn_expired", "The Bot execution deadline has expired", {
29853
30607
  cause: error61
29854
30608
  }) : error61;
@@ -29888,15 +30642,15 @@ function waitForRelayRetry(signal, timer) {
29888
30642
  signal.addEventListener("abort", finish, { once: true });
29889
30643
  });
29890
30644
  }
29891
- async function publishAgentCatalogUntilAvailable(config2, signal, timer) {
30645
+ async function publishAgentCatalogUntilAvailable(config2, diagnostics, signal, timer) {
29892
30646
  try {
29893
30647
  await publishAgentCatalog(config2, signal);
29894
30648
  return;
29895
- } catch {
30649
+ } catch (error61) {
29896
30650
  if (signal.aborted) {
29897
30651
  return;
29898
30652
  }
29899
- console.warn("Mobius daemon is waiting to publish its Agent catalog");
30653
+ diagnostics.catalogFailed(error61);
29900
30654
  }
29901
30655
  while (!signal.aborted) {
29902
30656
  await waitForRelayRetry(signal, timer);
@@ -29910,11 +30664,13 @@ async function publishAgentCatalogUntilAvailable(config2, signal, timer) {
29910
30664
  }
29911
30665
  }
29912
30666
  }
29913
- async function runRelayConnection(config2, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, grants, contexts, applicator, ipc, activeTurns, activeAttemptIds, onConnected, signal, timer) {
30667
+ async function runRelayConnection(config2, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, grants, contexts, applicator, ipc, activeTurns, activeAttemptIds, connectionEpoch, diagnostics, onConnected, signal, timer) {
29914
30668
  const relayUrl = new URL(config2.relayUrl);
30669
+ relayUrl.searchParams.set("deliveryReceipts", "1");
29915
30670
  relayUrl.searchParams.set("liveness", "1");
29916
30671
  const socket = await connectRelay(relayUrl, signal);
29917
30672
  const reconnectingTurns = [...activeTurns.values()];
30673
+ diagnostics.connectionOpened(connectionEpoch);
29918
30674
  onConnected();
29919
30675
  const connectionController = new AbortController();
29920
30676
  const nativeRequests = /* @__PURE__ */ new Map();
@@ -29923,7 +30679,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
29923
30679
  await new Promise((resolve6, reject) => {
29924
30680
  let finished = false;
29925
30681
  const cleanup = () => {
29926
- connectionRefresh.close();
29927
30682
  connectionLiveness.close();
29928
30683
  signal.removeEventListener("abort", stop);
29929
30684
  socket.removeEventListener("close", handleClose);
@@ -29941,61 +30696,282 @@ async function runRelayConnection(config2, directories, audit, sessions, install
29941
30696
  cleanup();
29942
30697
  resolve6();
29943
30698
  };
29944
- const rejectConnection = () => {
30699
+ const rejectConnection = (failure) => {
29945
30700
  if (finished) {
29946
30701
  return;
29947
30702
  }
29948
30703
  finished = true;
29949
30704
  cleanup();
29950
- reject(new Error("Relay WebSocket failed"));
30705
+ reject(failure);
29951
30706
  };
29952
30707
  const stop = () => {
30708
+ diagnostics.connectionClosed(connectionEpoch, 1e3, "Daemon stopped", "daemon_stop");
29953
30709
  closeRelaySocket(socket, 1e3, "Daemon stopped");
29954
30710
  resolveConnection();
29955
30711
  };
29956
- const refreshConnection = () => {
29957
- closeRelaySocket(socket, 1e3, "Refreshing idle relay connection");
29958
- resolveConnection();
30712
+ const failAndClose = (failure, code, reason, source) => {
30713
+ diagnostics.connectionFailed(connectionEpoch, failure, source);
30714
+ diagnostics.connectionClosed(connectionEpoch, code, reason, source);
30715
+ closeRelaySocket(socket, code, reason);
30716
+ rejectConnection(failure);
30717
+ };
30718
+ const processingDeliveryIds = /* @__PURE__ */ new Set();
30719
+ const processDeliveredPrompt = async (delivery) => {
30720
+ const command = decodeSessionPromptCommand(delivery.payload);
30721
+ if (command.sessionId !== delivery.sessionId) {
30722
+ throw new Error("Runtime delivery Session identity does not match its payload");
30723
+ }
30724
+ const active = activeTurns.get(command.commandId);
30725
+ if (active !== void 0) {
30726
+ await active.completion;
30727
+ return;
30728
+ }
30729
+ const receipt = outbox.claimExecution(command.commandId, command.sessionId, delivery.payload);
30730
+ if (receipt.kind === "duplicate") {
30731
+ if (receipt.state === "running") {
30732
+ reportFailure(
30733
+ publisher,
30734
+ command,
30735
+ "agent_execution_interrupted",
30736
+ "The local Agent execution was interrupted before it completed",
30737
+ receipt.attemptId
30738
+ );
30739
+ outbox.transitionExecution(command.commandId, "failed");
30740
+ return;
30741
+ }
30742
+ if (receipt.state !== "dispatching") {
30743
+ return;
30744
+ }
30745
+ }
30746
+ const turn = new ActiveDaemonTurn(command, receipt.attemptId);
30747
+ activeTurns.set(command.commandId, turn);
30748
+ if (command.disposition !== SessionPromptDisposition.Steer) {
30749
+ activeAttemptIds.set(command.sessionId, receipt.attemptId);
30750
+ }
30751
+ try {
30752
+ await executePrompt(
30753
+ outbox,
30754
+ publisher,
30755
+ command,
30756
+ config2,
30757
+ directories,
30758
+ audit,
30759
+ sessions,
30760
+ receipt.attemptId,
30761
+ timer,
30762
+ turn.signal
30763
+ );
30764
+ } finally {
30765
+ activeTurns.delete(command.commandId);
30766
+ if (activeAttemptIds.get(command.sessionId) === receipt.attemptId) {
30767
+ activeAttemptIds.delete(command.sessionId);
30768
+ }
30769
+ turn.finish();
30770
+ }
30771
+ };
30772
+ const processDeliveredPayload = async (delivery) => {
30773
+ const kind = decodeMessageKind(delivery.payload);
30774
+ if (kind === SESSION_CANCEL_KIND) {
30775
+ const cancellation = decodeSessionCancelCommand(delivery.payload);
30776
+ if (cancellation.sessionId !== delivery.sessionId) {
30777
+ throw new Error("Runtime delivery Session identity does not match its payload");
30778
+ }
30779
+ const turn = activeTurns.get(cancellation.commandId);
30780
+ if (turn === void 0 || !turn.matches(cancellation) || !turn.requestCancellation()) {
30781
+ return;
30782
+ }
30783
+ audit.record({
30784
+ commandId: cancellation.commandId,
30785
+ event: "session_cancellation_requested",
30786
+ sessionId: cancellation.sessionId
30787
+ });
30788
+ await sessions.cancelPrompt(cancellation.sessionId);
30789
+ return;
30790
+ }
30791
+ if (kind === SESSION_CONFIGURE_KIND) {
30792
+ const command = decodeSessionConfigureCommand(delivery.payload);
30793
+ if (command.sessionId !== delivery.sessionId) {
30794
+ throw new Error("Runtime delivery Session identity does not match its payload");
30795
+ }
30796
+ await executeSessionConfigure(
30797
+ publisher,
30798
+ socket,
30799
+ command,
30800
+ config2,
30801
+ directories,
30802
+ audit,
30803
+ sessions,
30804
+ connectionController.signal
30805
+ );
30806
+ return;
30807
+ }
30808
+ if (kind === SESSION_PERMISSION_RESPOND_KIND) {
30809
+ const command = decodeSessionPermissionRespondCommand(delivery.payload);
30810
+ if (command.sessionId !== delivery.sessionId) {
30811
+ throw new Error("Runtime delivery Session identity does not match its payload");
30812
+ }
30813
+ try {
30814
+ await sessions.respondPermission(command);
30815
+ audit.record({
30816
+ commandId: command.interactionId,
30817
+ event: "session_permission_resolved",
30818
+ sessionId: command.sessionId
30819
+ });
30820
+ } catch (error61) {
30821
+ reportOperationFailure(socket, command.interactionId, command.sessionId, error61);
30822
+ }
30823
+ return;
30824
+ }
30825
+ if (kind === SESSION_ELICITATION_RESPOND_KIND) {
30826
+ const command = decodeSessionElicitationRespondCommand(delivery.payload);
30827
+ if (command.sessionId !== delivery.sessionId) {
30828
+ throw new Error("Runtime delivery Session identity does not match its payload");
30829
+ }
30830
+ try {
30831
+ await sessions.respondElicitation(command);
30832
+ audit.record({
30833
+ commandId: command.interactionId,
30834
+ event: "session_elicitation_resolved",
30835
+ sessionId: command.sessionId
30836
+ });
30837
+ } catch (error61) {
30838
+ reportOperationFailure(socket, command.interactionId, command.sessionId, error61);
30839
+ }
30840
+ return;
30841
+ }
30842
+ if (kind === SESSION_PROMPT_KIND) {
30843
+ await processDeliveredPrompt(delivery);
30844
+ return;
30845
+ }
30846
+ throw new Error("Runtime delivery contains an unsupported protocol message");
30847
+ };
30848
+ const processRuntimeDelivery = (delivery, state) => {
30849
+ if (state === "processed" || processingDeliveryIds.has(delivery.deliveryId)) {
30850
+ return;
30851
+ }
30852
+ processingDeliveryIds.add(delivery.deliveryId);
30853
+ void processDeliveredPayload(delivery).then(() => {
30854
+ outbox.markRuntimeDeliveryProcessed(delivery.deliveryId, delivery.sessionId);
30855
+ }).catch((error61) => {
30856
+ if (connectionController.signal.aborted) {
30857
+ return;
30858
+ }
30859
+ console.error("Mobius daemon could not process a durable Runtime delivery", error61);
30860
+ failAndClose(
30861
+ new Error("Runtime delivery processing failed", { cause: error61 }),
30862
+ 4008,
30863
+ "Invalid Runtime delivery",
30864
+ "delivery"
30865
+ );
30866
+ }).finally(() => processingDeliveryIds.delete(delivery.deliveryId));
30867
+ };
30868
+ const receiveRuntimeDelivery = (delivery) => {
30869
+ const payloadKind = decodeMessageKind(delivery.payload);
30870
+ let payloadSessionId = "";
30871
+ if (payloadKind === SESSION_CANCEL_KIND) {
30872
+ payloadSessionId = decodeSessionCancelCommand(delivery.payload).sessionId;
30873
+ } else if (payloadKind === SESSION_CONFIGURE_KIND) {
30874
+ payloadSessionId = decodeSessionConfigureCommand(delivery.payload).sessionId;
30875
+ } else if (payloadKind === SESSION_PERMISSION_RESPOND_KIND) {
30876
+ payloadSessionId = decodeSessionPermissionRespondCommand(delivery.payload).sessionId;
30877
+ } else if (payloadKind === SESSION_ELICITATION_RESPOND_KIND) {
30878
+ payloadSessionId = decodeSessionElicitationRespondCommand(delivery.payload).sessionId;
30879
+ } else if (payloadKind === SESSION_PROMPT_KIND) {
30880
+ payloadSessionId = decodeSessionPromptCommand(delivery.payload).sessionId;
30881
+ } else {
30882
+ throw new Error("Runtime delivery contains an unsupported protocol message");
30883
+ }
30884
+ if (payloadSessionId !== delivery.sessionId) {
30885
+ throw new Error("Runtime delivery Session identity does not match its payload");
30886
+ }
30887
+ const stored = outbox.claimRuntimeDelivery(delivery);
30888
+ send(
30889
+ socket,
30890
+ encodeRuntimeDeliveryReceived({
30891
+ deliveryId: delivery.deliveryId,
30892
+ kind: RUNTIME_DELIVERY_RECEIVED_KIND,
30893
+ sessionId: delivery.sessionId
30894
+ })
30895
+ );
30896
+ processRuntimeDelivery(delivery, stored.state);
30897
+ };
30898
+ const resumeRuntimeDeliveries = () => {
30899
+ for (const stored of outbox.runtimeDeliveries()) {
30900
+ const delivery = {
30901
+ deliveryId: stored.deliveryId,
30902
+ kind: RUNTIME_DELIVERY_KIND,
30903
+ payload: stored.payload,
30904
+ sessionId: stored.sessionId
30905
+ };
30906
+ if (stored.state !== "relay_committed") {
30907
+ send(
30908
+ socket,
30909
+ encodeRuntimeDeliveryReceived({
30910
+ deliveryId: stored.deliveryId,
30911
+ kind: RUNTIME_DELIVERY_RECEIVED_KIND,
30912
+ sessionId: stored.sessionId
30913
+ })
30914
+ );
30915
+ }
30916
+ processRuntimeDelivery(delivery, stored.state);
30917
+ }
29959
30918
  };
29960
30919
  const connectionLiveness = new RelayConnectionLiveness(timer, {
29961
30920
  send: (message) => send(socket, message),
29962
- unresponsive: () => {
30921
+ unresponsive: (heartbeatId, timeoutMilliseconds) => {
30922
+ diagnostics.heartbeatTimedOut(connectionEpoch, heartbeatId, timeoutMilliseconds);
30923
+ diagnostics.connectionClosed(
30924
+ connectionEpoch,
30925
+ 4001,
30926
+ "Relay liveness check timed out",
30927
+ "heartbeat_timeout"
30928
+ );
29963
30929
  closeRelaySocket(socket, 4001, "Relay liveness check timed out");
29964
30930
  resolveConnection();
29965
30931
  }
29966
30932
  });
29967
- const connectionRefresh = new RelayConnectionRefresh(
29968
- timer,
29969
- () => activeTurns.size === 0,
29970
- refreshConnection
29971
- );
29972
30933
  signal.addEventListener("abort", stop, { once: true });
29973
- const handleClose = () => {
30934
+ const handleClose = (event) => {
30935
+ diagnostics.connectionClosed(connectionEpoch, event.code, event.reason, "relay");
29974
30936
  resolveConnection();
29975
30937
  };
29976
- const handleError = () => {
30938
+ const handleError = (event) => {
29977
30939
  if (signal.aborted) {
29978
30940
  resolveConnection();
29979
30941
  return;
29980
30942
  }
29981
- rejectConnection();
30943
+ const cause = Reflect.get(event, "error");
30944
+ const failure = new Error("Relay WebSocket emitted an error", { cause: cause ?? event });
30945
+ diagnostics.connectionFailed(connectionEpoch, failure, "socket_error");
30946
+ diagnostics.connectionClosed(connectionEpoch, 1006, "WebSocket error", "socket_error");
30947
+ rejectConnection(failure);
29982
30948
  };
29983
30949
  const handleMessage = (event) => {
29984
30950
  const encoded = event.data;
29985
30951
  if (typeof encoded !== "string") {
29986
- closeRelaySocket(socket, 4003, "Text message required");
29987
- rejectConnection();
30952
+ failAndClose(new Error("Relay sent a non-text message"), 4003, "Text message required", "protocol");
29988
30953
  return;
29989
30954
  }
29990
30955
  try {
29991
30956
  const kind = decodeMessageKind(encoded);
30957
+ if (kind === RUNTIME_DELIVERY_COMMITTED_KIND) {
30958
+ const committed = decodeRuntimeDeliveryCommitted(encoded);
30959
+ if (!outbox.markRuntimeDeliveryCommitted(committed.deliveryId, committed.sessionId)) {
30960
+ console.warn("Mobius daemon ignored confirmation for an unknown Runtime delivery");
30961
+ }
30962
+ return;
30963
+ }
30964
+ if (kind === RUNTIME_DELIVERY_KIND) {
30965
+ receiveRuntimeDelivery(decodeRuntimeDelivery(encoded));
30966
+ return;
30967
+ }
29992
30968
  if (kind === SESSION_EVENT_ACK_KIND) {
29993
- outbox.acknowledge(decodeSessionEventAcknowledgement(encoded));
30969
+ publisher.acknowledge(decodeSessionEventAcknowledgement(encoded));
29994
30970
  return;
29995
30971
  }
29996
30972
  if (kind === SESSION_EVENT_REJECTED_KIND) {
29997
30973
  const rejection = decodeSessionEventRejection(encoded);
29998
- if (outbox.quarantine(rejection)) {
30974
+ if (publisher.quarantine(rejection)) {
29999
30975
  audit.record({
30000
30976
  commandId: rejection.eventId,
30001
30977
  details: { code: rejection.code },
@@ -30374,6 +31350,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30374
31350
  applicator,
30375
31351
  ipc,
30376
31352
  receipt2.attemptId,
31353
+ timer,
30377
31354
  turn2.signal
30378
31355
  ).catch((error61) => {
30379
31356
  console.error("Mobius daemon Bot turn failed", error61);
@@ -30383,7 +31360,6 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30383
31360
  activeAttemptIds.delete(command2.sessionId);
30384
31361
  }
30385
31362
  turn2.finish();
30386
- connectionRefresh.afterTurnFinished();
30387
31363
  });
30388
31364
  return;
30389
31365
  }
@@ -30412,6 +31388,7 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30412
31388
  audit,
30413
31389
  sessions,
30414
31390
  receipt.attemptId,
31391
+ timer,
30415
31392
  turn.signal
30416
31393
  ).catch((error61) => {
30417
31394
  console.error("Mobius daemon turn failed", error61);
@@ -30421,12 +31398,19 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30421
31398
  activeAttemptIds.delete(command.sessionId);
30422
31399
  }
30423
31400
  turn.finish();
30424
- connectionRefresh.afterTurnFinished();
30425
31401
  });
30426
31402
  } catch (error61) {
30427
31403
  console.error("Mobius daemon rejected a message", error61);
30428
- closeRelaySocket(socket, 4008, "Invalid protocol message");
30429
- rejectConnection();
31404
+ if (error61 instanceof RuntimeDeliveryInboxCapacityError) {
31405
+ failAndClose(error61, 4013, "Runtime delivery queue full", "delivery_backpressure");
31406
+ return;
31407
+ }
31408
+ failAndClose(
31409
+ new Error("Relay sent an invalid protocol message", { cause: error61 }),
31410
+ 4008,
31411
+ "Invalid protocol message",
31412
+ "protocol"
31413
+ );
30430
31414
  }
30431
31415
  };
30432
31416
  socket.addEventListener("close", handleClose);
@@ -30438,21 +31422,19 @@ async function runRelayConnection(config2, directories, audit, sessions, install
30438
31422
  return;
30439
31423
  }
30440
31424
  publisher.connect(socket);
30441
- void Promise.all(reconnectingTurns.map((turn) => turn.completion)).then(
30442
- connectionRefresh.afterTurnFinished
30443
- );
31425
+ resumeRuntimeDeliveries();
30444
31426
  },
30445
31427
  (error61) => {
30446
31428
  if (finished) {
30447
31429
  return;
30448
31430
  }
30449
31431
  console.error("Mobius daemon could not reconcile active Agent turns", error61);
30450
- closeRelaySocket(
30451
- socket,
31432
+ failAndClose(
31433
+ new Error("Active Agent turn reconciliation failed", { cause: error61 }),
30452
31434
  ACTIVE_TURN_RECONCILIATION_CLOSE_CODE,
30453
- "Active Agent turn reconciliation failed"
31435
+ "Active Agent turn reconciliation failed",
31436
+ "reconciliation"
30454
31437
  );
30455
- rejectConnection();
30456
31438
  }
30457
31439
  );
30458
31440
  if (signal.aborted) {
@@ -30496,6 +31478,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
30496
31478
  const sessions = new AcpSessionManager(new DaemonAcpObserver(recordSessionEvent), sessionStore);
30497
31479
  const directories = new WorkspaceDirectoryService(config2.bindingId, config2.defaultWorkspace);
30498
31480
  const installationObserver = new DaemonInstallationObserver();
31481
+ const diagnostics = new RelayConnectionDiagnostics(audit, config2.channelId, config2.relayUrl);
30499
31482
  const nativeSessions = new NativeSessionService(config2, directories, sessions, audit);
30500
31483
  const installations = new AgentInstallationManager(
30501
31484
  specifications,
@@ -30503,7 +31486,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
30503
31486
  installationObserver,
30504
31487
  installationAudit
30505
31488
  );
30506
- let retryAnnounced = false;
31489
+ let connectionEpoch = 0;
30507
31490
  let connectionAnnounced = false;
30508
31491
  const announceConnection = () => {
30509
31492
  if (connectionAnnounced) {
@@ -30519,10 +31502,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
30519
31502
  `
30520
31503
  );
30521
31504
  };
30522
- const catalogPublication = publishAgentCatalogUntilAvailable(config2, signal, timer);
31505
+ const catalogPublication = publishAgentCatalogUntilAvailable(config2, diagnostics, signal, timer);
30523
31506
  try {
30524
31507
  await ipc.listen(signal);
30525
31508
  while (!signal.aborted) {
31509
+ connectionEpoch += 1;
30526
31510
  try {
30527
31511
  await runRelayConnection(
30528
31512
  config2,
@@ -30540,19 +31524,17 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer) {
30540
31524
  ipc,
30541
31525
  activeTurns,
30542
31526
  activeAttemptIds,
31527
+ connectionEpoch,
31528
+ diagnostics,
30543
31529
  announceConnection,
30544
31530
  signal,
30545
31531
  timer
30546
31532
  );
30547
- retryAnnounced = false;
30548
- } catch {
31533
+ } catch (error61) {
30549
31534
  if (signal.aborted) {
30550
31535
  return;
30551
31536
  }
30552
- if (!retryAnnounced) {
30553
- console.warn("Mobius daemon is waiting for the relay");
30554
- retryAnnounced = true;
30555
- }
31537
+ diagnostics.connectionFailed(connectionEpoch, error61, "connection");
30556
31538
  }
30557
31539
  await waitForRelayRetry(signal, timer);
30558
31540
  }
@@ -30708,12 +31690,12 @@ function decodePairingResponse(value, credentialField, expectedKeys) {
30708
31690
  throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
30709
31691
  }
30710
31692
  const channelId = value["channelId"];
30711
- const runtimeCredential = value[credentialField];
31693
+ const runtimeCredential2 = value[credentialField];
30712
31694
  const runtimeId = value["runtimeId"];
30713
- if (typeof channelId !== "string" || !IDENTIFIER_PATTERN7.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN7.test(runtimeId)) {
31695
+ if (typeof channelId !== "string" || !IDENTIFIER_PATTERN7.test(channelId) || typeof runtimeCredential2 !== "string" || !TOKEN_PATTERN2.test(runtimeCredential2) || typeof runtimeId !== "string" || !IDENTIFIER_PATTERN7.test(runtimeId)) {
30714
31696
  throw new PairingClientError("invalid_relay_response", "The relay returned an invalid pairing response");
30715
31697
  }
30716
- return { channelId, runtimeCredential, runtimeId };
31698
+ return { channelId, runtimeCredential: runtimeCredential2, runtimeId };
30717
31699
  }
30718
31700
  async function claimPairing(options, defaultWorkspace, localAgents, installableAgents, signal) {
30719
31701
  const agents = localAgents.map((agent) => ({
@@ -30748,7 +31730,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
30748
31730
  }
30749
31731
  return options.pairingId.length > 0 ? decodePairingResponse(value, "runtimeCredential", REDEEM_RESPONSE_KEYS) : decodePairingResponse(value, "daemonToken", CLAIM_RESPONSE_KEYS);
30750
31732
  }
30751
- async function persistConnection(channelId, relayOrigin, runtimeCredential, defaultWorkspace, environment) {
31733
+ async function persistConnection(channelId, relayOrigin, runtimeCredential2, defaultWorkspace, environment) {
30752
31734
  const connectionsDirectory = join16(readStateDirectory(environment), "connections");
30753
31735
  await mkdir4(connectionsDirectory, { mode: 448, recursive: true });
30754
31736
  const connectionPath = join16(connectionsDirectory, `${channelId}.json`);
@@ -30760,7 +31742,7 @@ async function persistConnection(channelId, relayOrigin, runtimeCredential, defa
30760
31742
  defaultWorkspace,
30761
31743
  channelId,
30762
31744
  relayOrigin: relayOrigin.toString(),
30763
- runtimeCredential
31745
+ runtimeCredential: runtimeCredential2
30764
31746
  },
30765
31747
  null,
30766
31748
  2
@@ -30777,16 +31759,16 @@ function decodeSavedConnection(value, expectedChannelId) {
30777
31759
  }
30778
31760
  const defaultWorkspace = value["defaultWorkspace"];
30779
31761
  const channelId = value["channelId"];
30780
- const runtimeCredential = value["runtimeCredential"] ?? value["daemonToken"];
31762
+ const runtimeCredential2 = value["runtimeCredential"] ?? value["daemonToken"];
30781
31763
  const relayOrigin = value["relayOrigin"];
30782
- if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN7.test(channelId) || typeof runtimeCredential !== "string" || !TOKEN_PATTERN2.test(runtimeCredential) || typeof relayOrigin !== "string") {
31764
+ if (typeof defaultWorkspace !== "string" || typeof channelId !== "string" || channelId !== expectedChannelId || !IDENTIFIER_PATTERN7.test(channelId) || typeof runtimeCredential2 !== "string" || !TOKEN_PATTERN2.test(runtimeCredential2) || typeof relayOrigin !== "string") {
30783
31765
  throw new PairingClientError("invalid_connection", "The saved connection is invalid");
30784
31766
  }
30785
31767
  return {
30786
31768
  defaultWorkspace,
30787
31769
  channelId,
30788
31770
  relayOrigin: parseRelayOrigin(relayOrigin),
30789
- runtimeCredential
31771
+ runtimeCredential: runtimeCredential2
30790
31772
  };
30791
31773
  }
30792
31774
  async function readSavedConnection(channelId, environment) {