@moxt-ai/mobius 0.0.149 → 0.0.152

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 (56) hide show
  1. package/dist/acp/agent-runtime-manager.d.ts.map +1 -1
  2. package/dist/acp/agent-runtime-manager.js +8 -1
  3. package/dist/acp/agent-runtime-manager.js.map +1 -1
  4. package/dist/acp/agent-session.d.ts.map +1 -1
  5. package/dist/acp/agent-session.js +8 -4
  6. package/dist/acp/agent-session.js.map +1 -1
  7. package/dist/acp/session-contracts.d.ts +2 -1
  8. package/dist/acp/session-contracts.d.ts.map +1 -1
  9. package/dist/acp/session-contracts.js +3 -1
  10. package/dist/acp/session-contracts.js.map +1 -1
  11. package/dist/acp/session-store.d.ts +2 -1
  12. package/dist/acp/session-store.d.ts.map +1 -1
  13. package/dist/acp/session-store.js +6 -2
  14. package/dist/acp/session-store.js.map +1 -1
  15. package/dist/cli.js +662 -60
  16. package/dist/cli.js.map +4 -4
  17. package/dist/daemon-command-handler.d.ts.map +1 -1
  18. package/dist/daemon-command-handler.js +1 -0
  19. package/dist/daemon-command-handler.js.map +1 -1
  20. package/dist/daemon-prompt-executor.d.ts.map +1 -1
  21. package/dist/daemon-prompt-executor.js +28 -8
  22. package/dist/daemon-prompt-executor.js.map +1 -1
  23. package/dist/generated/acp-session-store-migrations.d.ts +3 -0
  24. package/dist/generated/acp-session-store-migrations.d.ts.map +1 -1
  25. package/dist/generated/acp-session-store-migrations.js +5 -1
  26. package/dist/generated/acp-session-store-migrations.js.map +1 -1
  27. package/dist/index.js +661 -59
  28. package/dist/index.js.map +4 -4
  29. package/dist/native-sessions/handle-native-requests.d.ts +2 -0
  30. package/dist/native-sessions/handle-native-requests.d.ts.map +1 -1
  31. package/dist/native-sessions/handle-native-requests.js +12 -1
  32. package/dist/native-sessions/handle-native-requests.js.map +1 -1
  33. package/dist/native-sessions/service.d.ts.map +1 -1
  34. package/dist/native-sessions/service.js +47 -0
  35. package/dist/native-sessions/service.js.map +1 -1
  36. package/dist/relay-connection/run-relay-connection.d.ts.map +1 -1
  37. package/dist/relay-connection/run-relay-connection.js +15 -2
  38. package/dist/relay-connection/run-relay-connection.js.map +1 -1
  39. package/dist/relay-connection/session-event-outbox.d.ts +20 -0
  40. package/dist/relay-connection/session-event-outbox.d.ts.map +1 -0
  41. package/dist/relay-connection/session-event-outbox.js +243 -0
  42. package/dist/relay-connection/session-event-outbox.js.map +1 -0
  43. package/dist/relay-connection/session-event-publisher.d.ts +11 -4
  44. package/dist/relay-connection/session-event-publisher.d.ts.map +1 -1
  45. package/dist/relay-connection/session-event-publisher.js +148 -32
  46. package/dist/relay-connection/session-event-publisher.js.map +1 -1
  47. package/dist/run-daemon.d.ts.map +1 -1
  48. package/dist/run-daemon.js +3 -1
  49. package/dist/run-daemon.js.map +1 -1
  50. package/dist/runtime.js +669 -67
  51. package/dist/runtime.js.map +4 -4
  52. package/dist/schema/acp-session-store/acp-session.d.ts +19 -0
  53. package/dist/schema/acp-session-store/acp-session.d.ts.map +1 -1
  54. package/dist/schema/acp-session-store/acp-session.js +1 -0
  55. package/dist/schema/acp-session-store/acp-session.js.map +1 -1
  56. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -3381,7 +3381,7 @@ var program = new Command();
3381
3381
  // package.json
3382
3382
  var package_default = {
3383
3383
  name: "@moxt-ai/mobius",
3384
- version: "0.0.149",
3384
+ version: "0.0.152",
3385
3385
  description: "Mobius CLI for pairing and running local AI agent services.",
3386
3386
  type: "module",
3387
3387
  bin: {
@@ -29330,9 +29330,17 @@ function decodeSessionQueuedEvent(encoded) {
29330
29330
  }
29331
29331
  function decodeRuntimeLivenessReadyEvent(encoded) {
29332
29332
  const value = parseJson2(encoded);
29333
- assertExactKeys(value, ["kind"]);
29334
29333
  assertKind(value, RUNTIME_LIVENESS_READY_KIND);
29335
- return { kind: RUNTIME_LIVENESS_READY_KIND };
29334
+ if (value["sessionEventDelivery"] === void 0) {
29335
+ assertExactKeys(value, ["kind"]);
29336
+ return { kind: RUNTIME_LIVENESS_READY_KIND };
29337
+ }
29338
+ assertExactKeys(value, ["kind", "sessionEventDelivery"]);
29339
+ const sessionEventDelivery = readString2(value, "sessionEventDelivery", 32);
29340
+ if (sessionEventDelivery !== "reliable-v1") {
29341
+ throw new ProtocolMessageError("Unsupported Session event delivery mode");
29342
+ }
29343
+ return { kind: RUNTIME_LIVENESS_READY_KIND, sessionEventDelivery };
29336
29344
  }
29337
29345
  function decodeRuntimeHeartbeatEvent(encoded) {
29338
29346
  const value = parseJson2(encoded);
@@ -29664,6 +29672,12 @@ var nativeSessionRequestSchema = external_exports.discriminatedUnion("action", [
29664
29672
  entryId: identifierStringSchema,
29665
29673
  targetSessionId: identifierStringSchema
29666
29674
  }).readonly(),
29675
+ external_exports.strictObject({
29676
+ ...identity,
29677
+ action: external_exports.literal("fork-recorded"),
29678
+ targetSessionId: identifierStringSchema,
29679
+ throughCommandId: identifierStringSchema
29680
+ }).readonly(),
29667
29681
  external_exports.strictObject({
29668
29682
  ...identity,
29669
29683
  action: external_exports.literal("delete"),
@@ -29687,6 +29701,12 @@ var nativeSessionCommandSchema = external_exports.discriminatedUnion("action", [
29687
29701
  entryId: identifierStringSchema,
29688
29702
  targetSessionId: identifierStringSchema
29689
29703
  }).readonly(),
29704
+ external_exports.strictObject({
29705
+ ...identity,
29706
+ action: external_exports.literal("fork-recorded"),
29707
+ targetSessionId: identifierStringSchema,
29708
+ throughCommandId: identifierStringSchema
29709
+ }).readonly(),
29690
29710
  external_exports.strictObject({
29691
29711
  ...identity,
29692
29712
  action: external_exports.literal("delete"),
@@ -29736,7 +29756,7 @@ var nativeSessionResultSchema = external_exports.discriminatedUnion("action", [
29736
29756
  }).readonly(),
29737
29757
  external_exports.strictObject({
29738
29758
  ...resultEnvelope,
29739
- action: external_exports.enum(["import", "fork"]),
29759
+ action: external_exports.enum(["import", "fork", "fork-recorded"]),
29740
29760
  targetSessionId: identifierStringSchema,
29741
29761
  ready: sessionReadyEventSchema,
29742
29762
  title: external_exports.string().max(500)
@@ -29782,14 +29802,68 @@ var encodeNativeSessionCancel = (value) => JSON.stringify(nativeSessionCancelSch
29782
29802
 
29783
29803
  // ../protocol/src/session-events.ts
29784
29804
  var SESSION_EVENT_KIND = "session.event";
29805
+ var SESSION_EVENT_ACK_KIND = "session.event.ack";
29806
+ var SESSION_EVENT_DEFERRED_KIND = "session.event.deferred";
29807
+ var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
29808
+ var SESSION_FORKED_KIND = "session.forked";
29809
+ var SessionEventRejectionCode = {
29810
+ IdempotencyConflict: "idempotency_conflict",
29811
+ InvalidEvent: "invalid_event",
29812
+ RecordingRejected: "recording_rejected"
29813
+ };
29785
29814
  var MAX_RECORDED_EVENT_LENGTH = 8 * 1024 * 1024;
29786
- var eventEnvelopeSchema = external_exports.strictObject({
29815
+ var ephemeralEventEnvelopeSchema = external_exports.strictObject({
29787
29816
  kind: external_exports.literal(SESSION_EVENT_KIND),
29788
29817
  payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
29789
29818
  sessionId: identifierStringSchema
29790
29819
  }).readonly();
29820
+ var reliableDeliverySchema = external_exports.strictObject({
29821
+ eventId: identifierStringSchema,
29822
+ kind: external_exports.literal("reliable"),
29823
+ sourceSeq: external_exports.string().regex(/^(?:0|[1-9][0-9]*)$/u).max(40),
29824
+ streamId: identifierStringSchema
29825
+ }).readonly();
29826
+ var reliableSessionEventEnvelopeSchema = external_exports.strictObject({
29827
+ delivery: reliableDeliverySchema,
29828
+ kind: external_exports.literal(SESSION_EVENT_KIND),
29829
+ payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
29830
+ sessionId: identifierStringSchema
29831
+ }).readonly();
29832
+ var eventEnvelopeSchema = external_exports.union([ephemeralEventEnvelopeSchema, reliableSessionEventEnvelopeSchema]).readonly();
29833
+ var acknowledgementOutcomeSchema = external_exports.discriminatedUnion("kind", [
29834
+ external_exports.strictObject({ kind: external_exports.literal("relayed") }).readonly(),
29835
+ external_exports.strictObject({
29836
+ kind: external_exports.literal("recorded"),
29837
+ providerId: identifierStringSchema,
29838
+ recordingEpoch: identifierStringSchema
29839
+ }).readonly()
29840
+ ]);
29841
+ var eventAcknowledgementSchema = external_exports.strictObject({
29842
+ eventId: identifierStringSchema,
29843
+ kind: external_exports.literal(SESSION_EVENT_ACK_KIND),
29844
+ outcome: acknowledgementOutcomeSchema,
29845
+ sessionId: identifierStringSchema
29846
+ }).readonly();
29847
+ var eventDeferredSchema = external_exports.strictObject({
29848
+ eventId: identifierStringSchema,
29849
+ kind: external_exports.literal(SESSION_EVENT_DEFERRED_KIND),
29850
+ retryAfterMilliseconds: external_exports.number().int().min(100).max(6e4),
29851
+ sessionId: identifierStringSchema
29852
+ }).readonly();
29853
+ var eventRejectedSchema = external_exports.strictObject({
29854
+ code: external_exports.enum(SessionEventRejectionCode),
29855
+ eventId: identifierStringSchema,
29856
+ kind: external_exports.literal(SESSION_EVENT_REJECTED_KIND),
29857
+ sessionId: identifierStringSchema
29858
+ }).readonly();
29791
29859
  var SESSION_QUEUE_CHANGED_KIND = "session.queue.changed";
29792
29860
  var sessionQueueChangedSchema = external_exports.strictObject({ kind: external_exports.literal(SESSION_QUEUE_CHANGED_KIND), sessionId: identifierStringSchema }).readonly();
29861
+ var sessionForkedSchema = external_exports.strictObject({
29862
+ kind: external_exports.literal(SESSION_FORKED_KIND),
29863
+ sessionId: identifierStringSchema,
29864
+ sourceSessionId: identifierStringSchema,
29865
+ throughCommandId: identifierStringSchema
29866
+ }).readonly();
29793
29867
  function decode5(encoded, schema) {
29794
29868
  let value;
29795
29869
  try {
@@ -29816,11 +29890,19 @@ var encode4 = (message, schema) => {
29816
29890
  };
29817
29891
  var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
29818
29892
  var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
29893
+ var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
29894
+ var encodeSessionEventAcknowledgement = (message) => encode4(message, eventAcknowledgementSchema);
29895
+ var decodeSessionEventDeferred = (encoded) => decode5(encoded, eventDeferredSchema);
29896
+ var encodeSessionEventDeferred = (message) => encode4(message, eventDeferredSchema);
29897
+ var decodeSessionEventRejected = (encoded) => decode5(encoded, eventRejectedSchema);
29898
+ var encodeSessionEventRejected = (message) => encode4(message, eventRejectedSchema);
29819
29899
  function decodeRecordedSessionEvent(encoded) {
29820
29900
  const kind = decodeMessageKind(encoded);
29821
29901
  switch (kind) {
29822
29902
  case SESSION_QUEUE_CHANGED_KIND:
29823
29903
  return decode5(encoded, sessionQueueChangedSchema);
29904
+ case SESSION_FORKED_KIND:
29905
+ return decode5(encoded, sessionForkedSchema);
29824
29906
  case AGENT_ACTIVITY_KIND:
29825
29907
  return decodeAgentActivityEvent(encoded);
29826
29908
  case AGENT_ACTIVITY_NARRATION_KIND:
@@ -29987,6 +30069,21 @@ var serviceToDaemonSignatures = {
29987
30069
  encodeSessionElicitationRespondCommand,
29988
30070
  decodeSessionElicitationRespondCommand
29989
30071
  ),
30072
+ [SESSION_EVENT_ACK_KIND]: defineMessageSignature(
30073
+ SESSION_EVENT_ACK_KIND,
30074
+ encodeSessionEventAcknowledgement,
30075
+ decodeSessionEventAcknowledgement
30076
+ ),
30077
+ [SESSION_EVENT_DEFERRED_KIND]: defineMessageSignature(
30078
+ SESSION_EVENT_DEFERRED_KIND,
30079
+ encodeSessionEventDeferred,
30080
+ decodeSessionEventDeferred
30081
+ ),
30082
+ [SESSION_EVENT_REJECTED_KIND]: defineMessageSignature(
30083
+ SESSION_EVENT_REJECTED_KIND,
30084
+ encodeSessionEventRejected,
30085
+ decodeSessionEventRejected
30086
+ ),
29990
30087
  [SESSION_LIVE_HISTORY_LOAD_KIND]: defineMessageSignature(
29991
30088
  SESSION_LIVE_HISTORY_LOAD_KIND,
29992
30089
  encodeSessionLiveHistoryLoad,
@@ -31045,7 +31142,7 @@ var AuditStore = class {
31045
31142
  };
31046
31143
 
31047
31144
  // src/run-daemon.ts
31048
- import { createHash as createHash6, randomUUID as randomUUID7 } from "node:crypto";
31145
+ import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
31049
31146
  import { join as join18 } from "node:path";
31050
31147
  import { styleText } from "node:util";
31051
31148
 
@@ -32610,13 +32707,15 @@ var AcpPreparedSession = class {
32610
32707
  capabilities;
32611
32708
  commands;
32612
32709
  configOptions;
32710
+ prompted;
32613
32711
  restored;
32614
- constructor(agentName, agentVersion, capabilities, commands, configOptions, restored) {
32712
+ constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored) {
32615
32713
  this.agentName = agentName;
32616
32714
  this.agentVersion = agentVersion;
32617
32715
  this.capabilities = capabilities;
32618
32716
  this.commands = commands;
32619
32717
  this.configOptions = configOptions;
32718
+ this.prompted = prompted;
32620
32719
  this.restored = restored;
32621
32720
  }
32622
32721
  };
@@ -38786,6 +38885,10 @@ var acpSessionStoreMigrations = [
38786
38885
  {
38787
38886
  "name": "0000_slimy_captain_midlands.sql",
38788
38887
  "sql": "CREATE TABLE `acp_session` (\n `session_id` text PRIMARY KEY NOT NULL,\n `agent_id` text NOT NULL,\n `native_session_id` text NOT NULL,\n `cwd` text NOT NULL,\n `additional_directories` text NOT NULL,\n `prompted` integer NOT NULL,\n `title` text NOT NULL,\n `updated_at` text NOT NULL\n);\n--> statement-breakpoint\nCREATE TABLE `deleted_acp_session` (\n `session_id` text PRIMARY KEY NOT NULL\n);\n"
38888
+ },
38889
+ {
38890
+ "name": "0001_regular_ultimates.sql",
38891
+ "sql": "ALTER TABLE `acp_session` ADD `last_terminal_command_id` text;"
38789
38892
  }
38790
38893
  ];
38791
38894
 
@@ -38803,6 +38906,7 @@ var acpSession = sqliteTable("acp_session", {
38803
38906
  nativeSessionId: text("native_session_id").notNull(),
38804
38907
  cwd: text("cwd").notNull(),
38805
38908
  additionalDirectories: text("additional_directories").notNull(),
38909
+ lastTerminalCommandId: text("last_terminal_command_id"),
38806
38910
  prompted: integer2("prompted", { mode: "boolean" }).notNull(),
38807
38911
  title: text("title").notNull(),
38808
38912
  updatedAt: text("updated_at").notNull()
@@ -38818,15 +38922,17 @@ var AcpStoredSession = class {
38818
38922
  additionalDirectories;
38819
38923
  agentId;
38820
38924
  cwd;
38925
+ lastTerminalCommandId;
38821
38926
  nativeSessionId;
38822
38927
  prompted;
38823
38928
  sessionId;
38824
38929
  title;
38825
38930
  updatedAt;
38826
- constructor(sessionId, agentId, nativeSessionId, cwd, additionalDirectories, prompted, title2, updatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
38931
+ constructor(sessionId, agentId, nativeSessionId, cwd, additionalDirectories, prompted, title2, updatedAt = (/* @__PURE__ */ new Date()).toISOString(), lastTerminalCommandId = null) {
38827
38932
  this.additionalDirectories = [...additionalDirectories];
38828
38933
  this.agentId = agentId;
38829
38934
  this.cwd = cwd;
38935
+ this.lastTerminalCommandId = lastTerminalCommandId;
38830
38936
  this.nativeSessionId = nativeSessionId;
38831
38937
  this.prompted = prompted;
38832
38938
  this.sessionId = sessionId;
@@ -38887,6 +38993,7 @@ var storedSessionRowSchema = external_exports.object({
38887
38993
  additionalDirectories: external_exports.string(),
38888
38994
  agentId: external_exports.string(),
38889
38995
  cwd: external_exports.string(),
38996
+ lastTerminalCommandId: external_exports.string().nullable(),
38890
38997
  nativeSessionId: external_exports.string(),
38891
38998
  prompted: external_exports.boolean(),
38892
38999
  sessionId: external_exports.string(),
@@ -38906,7 +39013,8 @@ function decodeStoredSession(value) {
38906
39013
  additionalDirectories,
38907
39014
  row.prompted,
38908
39015
  row.title,
38909
- row.updatedAt
39016
+ row.updatedAt,
39017
+ row.lastTerminalCommandId
38910
39018
  );
38911
39019
  }
38912
39020
  var SqliteAcpSessionStore = class {
@@ -38973,6 +39081,7 @@ var SqliteAcpSessionStore = class {
38973
39081
  additionalDirectories: JSON.stringify(session.additionalDirectories),
38974
39082
  agentId: session.agentId,
38975
39083
  cwd: session.cwd,
39084
+ lastTerminalCommandId: session.lastTerminalCommandId,
38976
39085
  nativeSessionId: session.nativeSessionId,
38977
39086
  prompted: session.prompted,
38978
39087
  sessionId: session.sessionId,
@@ -39025,6 +39134,7 @@ var AgentSession = class _AgentSession {
39025
39134
  #commands = [];
39026
39135
  #configRevision = 0;
39027
39136
  #configOptions = [];
39137
+ #lastTerminalCommandId = null;
39028
39138
  #prompted = false;
39029
39139
  #nativePrompt = null;
39030
39140
  #sessionLoad = { kind: "not-requested" };
@@ -39128,6 +39238,7 @@ var AgentSession = class _AgentSession {
39128
39238
  this.#capabilities,
39129
39239
  this.#commands,
39130
39240
  this.#configOptions,
39241
+ this.#prompted,
39131
39242
  this.#restored
39132
39243
  );
39133
39244
  loadHistory = async (signal) => {
@@ -39361,6 +39472,7 @@ var AgentSession = class _AgentSession {
39361
39472
  } else if (stored instanceof FoundAcpStoredSession && stored.session.agentId === this.#agentId && stored.session.cwd === this.#cwd && arraysEqual(stored.session.additionalDirectories, this.#additionalDirectories)) {
39362
39473
  this.selectNativeSession(stored.session.nativeSessionId);
39363
39474
  this.#prompted = stored.session.prompted;
39475
+ this.#lastTerminalCommandId = stored.session.lastTerminalCommandId;
39364
39476
  this.#sessionTitle = stored.session.title;
39365
39477
  this.#restored = true;
39366
39478
  if (this.#capabilities.resumeSession) {
@@ -39434,7 +39546,9 @@ var AgentSession = class _AgentSession {
39434
39546
  this.#cwd,
39435
39547
  this.#additionalDirectories,
39436
39548
  this.#prompted,
39437
- this.#sessionTitle ?? ""
39549
+ this.#sessionTitle ?? "",
39550
+ (/* @__PURE__ */ new Date()).toISOString(),
39551
+ this.#lastTerminalCommandId
39438
39552
  )
39439
39553
  );
39440
39554
  };
@@ -39634,7 +39748,9 @@ var AgentSession = class _AgentSession {
39634
39748
  this.#cwd,
39635
39749
  this.#additionalDirectories,
39636
39750
  true,
39637
- this.#sessionTitle
39751
+ this.#sessionTitle,
39752
+ (/* @__PURE__ */ new Date()).toISOString(),
39753
+ this.#lastTerminalCommandId
39638
39754
  )
39639
39755
  );
39640
39756
  this.#prompted = true;
@@ -39686,6 +39802,20 @@ var AgentSession = class _AgentSession {
39686
39802
  usage: result.usage ?? null
39687
39803
  });
39688
39804
  }
39805
+ this.#lastTerminalCommandId = options.commandId;
39806
+ this.#store.write(
39807
+ new AcpStoredSession(
39808
+ this.#mobiusSessionId,
39809
+ this.#agentId,
39810
+ this.#updates.sessionId(),
39811
+ this.#cwd,
39812
+ this.#additionalDirectories,
39813
+ true,
39814
+ this.#sessionTitle ?? "",
39815
+ (/* @__PURE__ */ new Date()).toISOString(),
39816
+ this.#lastTerminalCommandId
39817
+ )
39818
+ );
39689
39819
  return result;
39690
39820
  } catch (error62) {
39691
39821
  await running.activities.finish();
@@ -39878,7 +40008,9 @@ var AgentSession = class _AgentSession {
39878
40008
  this.#cwd,
39879
40009
  this.#additionalDirectories,
39880
40010
  this.#prompted,
39881
- this.#sessionTitle ?? ""
40011
+ this.#sessionTitle ?? "",
40012
+ (/* @__PURE__ */ new Date()).toISOString(),
40013
+ this.#lastTerminalCommandId
39882
40014
  )
39883
40015
  );
39884
40016
  }
@@ -40924,9 +41056,17 @@ var AgentRuntimeManager = class {
40924
41056
  const openedKey = `${options2.agent.id}:${runtime.nativeSessionId()}`;
40925
41057
  this.#nativeOperations.add(openedKey);
40926
41058
  try {
41059
+ const prepared = runtime.prepared();
41060
+ if (origin.kind === "fork" && options2.agent.id === "claude") {
41061
+ await this.#retireSessionProcess(
41062
+ options2.sessionId,
41063
+ runtime,
41064
+ async () => await runtime.closeProcess()
41065
+ );
41066
+ }
40927
41067
  return {
40928
41068
  sessionId: options2.sessionId,
40929
- prepared: runtime.prepared()
41069
+ prepared
40930
41070
  };
40931
41071
  } finally {
40932
41072
  this.#nativeOperations.delete(openedKey);
@@ -43337,6 +43477,7 @@ var readCodexFullHistory = async (agent, cwd, nativeSessionId, signal, nativeTur
43337
43477
 
43338
43478
  // src/native-sessions/service.ts
43339
43479
  import { randomUUID as randomUUID3 } from "node:crypto";
43480
+ import { basename as basename5 } from "node:path";
43340
43481
 
43341
43482
  // src/native-sessions/acp-history.ts
43342
43483
  async function readAcpHistory(sessions, directories, bindingId, options, nativeSessionId) {
@@ -43477,6 +43618,64 @@ var NativeSessionService = class {
43477
43618
  sessionId: command.sessionId
43478
43619
  };
43479
43620
  }
43621
+ if (command.action === "fork-recorded") {
43622
+ const source = this.#sessions.findSessions([command.sessionId])[0];
43623
+ if (source === void 0 || !source.prompted) {
43624
+ throw new AcpExecutionError(
43625
+ "The source conversation has no resumable native Agent session",
43626
+ {},
43627
+ "native_session_not_found"
43628
+ );
43629
+ }
43630
+ if (source.agentId !== command.agentId || source.cwd !== cwd || source.additionalDirectories.length !== additionalDirectories.length || source.additionalDirectories.some((path3, index) => path3 !== additionalDirectories[index])) {
43631
+ throw new AcpExecutionError(
43632
+ "The source conversation execution target does not match the fork request",
43633
+ {},
43634
+ "native_session_target_changed"
43635
+ );
43636
+ }
43637
+ if (source.lastTerminalCommandId !== command.throughCommandId) {
43638
+ throw new AcpExecutionError(
43639
+ "The selected Turn is no longer the source conversation head",
43640
+ {},
43641
+ "fork_point_stale"
43642
+ );
43643
+ }
43644
+ const opened2 = await this.#sessions.openNativeSession(
43645
+ { ...options, sessionId: command.targetSessionId },
43646
+ { kind: "fork", nativeSessionId: source.nativeSessionId }
43647
+ );
43648
+ signal.throwIfAborted();
43649
+ const directory = await this.#directories.list(this.#config.bindingId, command.directoryId, signal);
43650
+ const ready2 = {
43651
+ additionalDirectories: additionalDirectories.map((path3, index) => ({
43652
+ directoryId: command.additionalDirectoryIds[index] ?? command.directoryId,
43653
+ name: basename5(path3) || path3
43654
+ })),
43655
+ agentId: command.agentId,
43656
+ agentName: opened2.prepared.agentName,
43657
+ agentVersion: opened2.prepared.agentVersion,
43658
+ capabilities: opened2.prepared.capabilities,
43659
+ commands: opened2.prepared.commands,
43660
+ configOptions: opened2.prepared.configOptions,
43661
+ directoryId: command.directoryId,
43662
+ directoryLabel: directory.directoryLabel,
43663
+ kind: "session.ready",
43664
+ requestId: command.requestId,
43665
+ restored: true,
43666
+ sessionId: opened2.sessionId
43667
+ };
43668
+ this.record(command, "native_recorded_session_forked");
43669
+ return {
43670
+ action: "fork-recorded",
43671
+ kind: "native.session.result",
43672
+ ready: ready2,
43673
+ requestId: command.requestId,
43674
+ sessionId: command.sessionId,
43675
+ targetSessionId: command.targetSessionId,
43676
+ title: source.title || "Untitled session"
43677
+ };
43678
+ }
43480
43679
  const prepared = await this.#sessions.prepareNativeSessionBrowser(options);
43481
43680
  if (!prepared.capabilities.listSessions) {
43482
43681
  throw new AcpExecutionError(
@@ -44798,7 +44997,7 @@ function createAgentSettingsDiscoveryHandler(dependencies) {
44798
44997
  // src/artifacts/local-file-streamer.ts
44799
44998
  import { createReadStream as createReadStream2 } from "node:fs";
44800
44999
  import { realpath as realpath9, stat as stat8 } from "node:fs/promises";
44801
- import { basename as basename5, isAbsolute as isAbsolute10, normalize as normalize4, resolve as resolve7 } from "node:path";
45000
+ import { basename as basename6, isAbsolute as isAbsolute10, normalize as normalize4, resolve as resolve7 } from "node:path";
44802
45001
  import { fileURLToPath as fileURLToPath2 } from "node:url";
44803
45002
  var CHUNK_BYTES = 192 * 1024;
44804
45003
  function requestedPath(value) {
@@ -44849,7 +45048,7 @@ async function streamLocalFile(command, send2, signal, workingDirectory) {
44849
45048
  kind: FILE_BEGIN_KIND,
44850
45049
  mediaType: raster?.mimeType ?? "application/octet-stream",
44851
45050
  modifiedAt: before.mtime.toISOString(),
44852
- name: basename5(target) || "file",
45051
+ name: basename6(target) || "file",
44853
45052
  requestId: command.requestId,
44854
45053
  sessionId: command.sessionId
44855
45054
  });
@@ -44879,7 +45078,7 @@ async function streamLocalFile(command, send2, signal, workingDirectory) {
44879
45078
  }
44880
45079
 
44881
45080
  // src/authorization/resolve-session-directories.ts
44882
- import { basename as basename6 } from "node:path";
45081
+ import { basename as basename7 } from "node:path";
44883
45082
  var ResolvedSessionDirectories = class {
44884
45083
  additionalDirectories;
44885
45084
  additionalDirectoryEntries;
@@ -44904,7 +45103,7 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
44904
45103
  additionalDirectories.push(directory);
44905
45104
  additionalDirectoryEntries.push({
44906
45105
  directoryId: additionalDirectoryId,
44907
- name: basename6(directory)
45106
+ name: basename7(directory)
44908
45107
  });
44909
45108
  }
44910
45109
  return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
@@ -45641,14 +45840,15 @@ async function executePrompt({
45641
45840
  const appliedSettings = command.sessionConfigurations === void 0 ? void 0 : agentForSessionConfiguration(agent, command.sessionConfigurations);
45642
45841
  const executionAgent = appliedSettings?.agent ?? agent;
45643
45842
  const sessionConfigurations = appliedSettings?.sessionConfigurations;
45843
+ const preparation = {
45844
+ additionalDirectories: resolved.additionalDirectories,
45845
+ agent: executionAgent,
45846
+ cwd: resolved.cwd,
45847
+ sessionId: command.sessionId,
45848
+ signal
45849
+ };
45850
+ let prepared = await sessions.prepareExecutionSession(preparation);
45644
45851
  if (command.sessionConfigurations !== void 0) {
45645
- const prepared = await sessions.prepareExecutionSession({
45646
- additionalDirectories: resolved.additionalDirectories,
45647
- agent: executionAgent,
45648
- cwd: resolved.cwd,
45649
- sessionId: command.sessionId,
45650
- signal
45651
- });
45652
45852
  if (availability.disposition === SessionPromptDisposition.Steer) {
45653
45853
  assertAppliedSessionConfiguration(prepared.configOptions, sessionConfigurations ?? []);
45654
45854
  } else {
@@ -45664,6 +45864,7 @@ async function executePrompt({
45664
45864
  command.sessionId,
45665
45865
  signal
45666
45866
  );
45867
+ prepared = await sessions.prepareExecutionSession(preparation);
45667
45868
  }
45668
45869
  }
45669
45870
  audit.record({
@@ -45682,6 +45883,24 @@ async function executePrompt({
45682
45883
  }
45683
45884
  }
45684
45885
  const stream = activateAttempt();
45886
+ if (!prepared.prompted) {
45887
+ const listing = await directories.list(config2.bindingId, command.directoryId, signal);
45888
+ publisher.publish(command.sessionId, {
45889
+ additionalDirectories: resolved.additionalDirectoryEntries,
45890
+ agentId: command.agentId,
45891
+ agentName: prepared.agentName,
45892
+ agentVersion: prepared.agentVersion,
45893
+ capabilities: prepared.capabilities,
45894
+ commands: prepared.commands,
45895
+ configOptions: prepared.configOptions,
45896
+ directoryId: command.directoryId,
45897
+ directoryLabel: listing.directoryLabel,
45898
+ kind: SESSION_READY_KIND,
45899
+ requestId: command.commandId,
45900
+ restored: prepared.restored,
45901
+ sessionId: command.sessionId
45902
+ });
45903
+ }
45685
45904
  publisher.publish(command.sessionId, {
45686
45905
  commandId: command.commandId,
45687
45906
  epoch: stream.epoch,
@@ -45935,7 +46154,7 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
45935
46154
  // src/native-sessions/handle-native-requests.ts
45936
46155
  import { WebSocket as WebSocket5 } from "undici";
45937
46156
  function createNativeSessionRequestHandler(dependencies) {
45938
- const { audit, diagnostics, nativeSessions, signal, socket, timer } = dependencies;
46157
+ const { audit, diagnostics, nativeSessions, publisher, signal, socket, timer } = dependencies;
45939
46158
  const pending = /* @__PURE__ */ new Map();
45940
46159
  const cancel = (command) => {
45941
46160
  const request = pending.get(command.requestId);
@@ -45987,6 +46206,17 @@ function createNativeSessionRequestHandler(dependencies) {
45987
46206
  }
45988
46207
  operationSignal.throwIfAborted();
45989
46208
  const result = await nativeSessions.execute(command, operationSignal);
46209
+ if (command.action === "fork-recorded" && result.action === "fork-recorded") {
46210
+ publisher.publishBatch(result.ready.sessionId, [
46211
+ result.ready,
46212
+ {
46213
+ kind: "session.forked",
46214
+ sessionId: result.ready.sessionId,
46215
+ sourceSessionId: command.sessionId,
46216
+ throughCommandId: command.throughCommandId
46217
+ }
46218
+ ]);
46219
+ }
45990
46220
  if (socket.readyState === WebSocket5.OPEN) {
45991
46221
  send(socket, result);
45992
46222
  }
@@ -46072,6 +46302,7 @@ function createDaemonCommandHandler({
46072
46302
  audit,
46073
46303
  diagnostics,
46074
46304
  nativeSessions,
46305
+ publisher,
46075
46306
  signal,
46076
46307
  socket,
46077
46308
  timer
@@ -46594,6 +46825,7 @@ async function runRelayConnection({
46594
46825
  }) {
46595
46826
  const relayUrl = new URL(config2.relayUrl);
46596
46827
  relayUrl.searchParams.set("liveness", "1");
46828
+ relayUrl.searchParams.set("sessionEventDelivery", "reliable-v1");
46597
46829
  const socket = await connectRelay(relayUrl, signal);
46598
46830
  diagnostics.connectionOpened(connectionEpoch);
46599
46831
  const connectionController = new AbortController();
@@ -46713,6 +46945,10 @@ async function runRelayConnection({
46713
46945
  const message = decodeServiceToDaemonMessage(encoded);
46714
46946
  const kind = message.kind;
46715
46947
  if (kind === RUNTIME_LIVENESS_READY_KIND) {
46948
+ publisher.connect(
46949
+ socket,
46950
+ message.sessionEventDelivery === "reliable-v1" ? "reliable" : "ephemeral"
46951
+ );
46716
46952
  connectionLiveness.start();
46717
46953
  return;
46718
46954
  }
@@ -46720,6 +46956,18 @@ async function runRelayConnection({
46720
46956
  connectionLiveness.acknowledge(message);
46721
46957
  return;
46722
46958
  }
46959
+ if (kind === SESSION_EVENT_ACK_KIND) {
46960
+ publisher.acknowledge(message);
46961
+ return;
46962
+ }
46963
+ if (kind === SESSION_EVENT_DEFERRED_KIND) {
46964
+ publisher.defer(message);
46965
+ return;
46966
+ }
46967
+ if (kind === SESSION_EVENT_REJECTED_KIND) {
46968
+ publisher.quarantine(message);
46969
+ return;
46970
+ }
46723
46971
  commands.handle(message);
46724
46972
  } catch (error62) {
46725
46973
  console.error("Mobius daemon rejected a message", error62);
@@ -46734,7 +46982,6 @@ async function runRelayConnection({
46734
46982
  socket.addEventListener("close", handleClose);
46735
46983
  socket.addEventListener("error", handleError);
46736
46984
  socket.addEventListener("message", handleMessage);
46737
- publisher.connect(socket);
46738
46985
  if (signal.aborted) {
46739
46986
  stop();
46740
46987
  }
@@ -46745,54 +46992,405 @@ async function runRelayConnection({
46745
46992
  }
46746
46993
  }
46747
46994
 
46995
+ // src/relay-connection/session-event-outbox.ts
46996
+ import { randomUUID as randomUUID7 } from "node:crypto";
46997
+ import { mkdirSync as mkdirSync3 } from "node:fs";
46998
+ import { dirname as dirname6 } from "node:path";
46999
+ import { DatabaseSync } from "node:sqlite";
47000
+ var DEFAULT_STANDARD_EVENT_BYTES = 96 * 1024 * 1024;
47001
+ var DEFAULT_CRITICAL_EVENT_BYTES = 128 * 1024 * 1024;
47002
+ var MAX_DEAD_LETTERS = 1024;
47003
+ var DEFAULT_LIMITS = {
47004
+ criticalEventBytes: DEFAULT_CRITICAL_EVENT_BYTES,
47005
+ standardEventBytes: DEFAULT_STANDARD_EVENT_BYTES
47006
+ };
47007
+ var SessionEventOutboxCapacityError = class extends Error {
47008
+ code = "session_event_outbox_full";
47009
+ constructor() {
47010
+ super("The persistent Session event outbox is full");
47011
+ this.name = "SessionEventOutboxCapacityError";
47012
+ }
47013
+ };
47014
+ function rowNumber(row, field) {
47015
+ const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
47016
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
47017
+ throw new Error(`SQLite returned an invalid ${field} value`);
47018
+ }
47019
+ return value;
47020
+ }
47021
+ function rowString(row, field) {
47022
+ const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
47023
+ if (typeof value !== "string" || value.length === 0) {
47024
+ throw new Error(`SQLite returned an invalid ${field} value`);
47025
+ }
47026
+ return value;
47027
+ }
47028
+ function validateLimits(limits) {
47029
+ if (!Number.isSafeInteger(limits.standardEventBytes) || limits.standardEventBytes < 1 || !Number.isSafeInteger(limits.criticalEventBytes) || limits.criticalEventBytes < limits.standardEventBytes) {
47030
+ throw new Error("Session event outbox limits are invalid");
47031
+ }
47032
+ }
47033
+ var SessionEventOutbox = class {
47034
+ #database;
47035
+ #limits;
47036
+ constructor(path3, limits = DEFAULT_LIMITS) {
47037
+ validateLimits(limits);
47038
+ mkdirSync3(dirname6(path3), { mode: 448, recursive: true });
47039
+ this.#limits = limits;
47040
+ this.#database = new DatabaseSync(path3);
47041
+ this.#database.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL; PRAGMA busy_timeout = 5000;");
47042
+ this.#database.exec(`
47043
+ CREATE TABLE IF NOT EXISTS relay_session_event_outbox (
47044
+ ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
47045
+ event_id TEXT NOT NULL UNIQUE,
47046
+ session_id TEXT NOT NULL,
47047
+ source_seq INTEGER NOT NULL UNIQUE,
47048
+ byte_length INTEGER NOT NULL,
47049
+ envelope_json TEXT NOT NULL
47050
+ ) STRICT;
47051
+ CREATE INDEX IF NOT EXISTS relay_session_event_outbox_order
47052
+ ON relay_session_event_outbox(source_seq);
47053
+ CREATE TABLE IF NOT EXISTS relay_session_event_outbox_state (
47054
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
47055
+ stream_id TEXT NOT NULL,
47056
+ next_source_seq INTEGER NOT NULL,
47057
+ total_bytes INTEGER NOT NULL
47058
+ ) STRICT;
47059
+ CREATE TABLE IF NOT EXISTS relay_session_event_dead_letters (
47060
+ ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
47061
+ event_id TEXT NOT NULL UNIQUE,
47062
+ session_id TEXT NOT NULL,
47063
+ rejection_code TEXT NOT NULL,
47064
+ envelope_json TEXT NOT NULL
47065
+ ) STRICT;
47066
+ `);
47067
+ this.#database.prepare(
47068
+ `INSERT OR IGNORE INTO relay_session_event_outbox_state (
47069
+ singleton, stream_id, next_source_seq, total_bytes
47070
+ ) VALUES (1, ?, 1, 0)`
47071
+ ).run(randomUUID7());
47072
+ }
47073
+ acknowledge = (acknowledgement) => this.#remove(acknowledgement.eventId, acknowledgement.sessionId);
47074
+ close = () => {
47075
+ this.#database.close();
47076
+ };
47077
+ enqueue = (sessionId, payload, critical) => {
47078
+ const envelope2 = this.enqueueBatch(sessionId, [payload], critical)[0];
47079
+ if (envelope2 === void 0) {
47080
+ throw new Error("The Session event outbox did not enqueue the event");
47081
+ }
47082
+ return envelope2;
47083
+ };
47084
+ enqueueBatch = (sessionId, payloads, critical) => {
47085
+ if (payloads.length < 1 || payloads.length > 32) {
47086
+ throw new Error("A Session event batch must contain between 1 and 32 events");
47087
+ }
47088
+ this.#database.exec("BEGIN IMMEDIATE");
47089
+ try {
47090
+ const state = this.#database.prepare(
47091
+ `SELECT stream_id, next_source_seq, total_bytes
47092
+ FROM relay_session_event_outbox_state
47093
+ WHERE singleton = 1`
47094
+ ).get();
47095
+ const sourceSeq = rowNumber(state, "next_source_seq");
47096
+ if (sourceSeq < 1 || !Number.isSafeInteger(sourceSeq + payloads.length)) {
47097
+ throw new Error("The Session event source sequence is exhausted");
47098
+ }
47099
+ const streamId = rowString(state, "stream_id");
47100
+ const stored = payloads.map((payload, index) => {
47101
+ const envelope2 = {
47102
+ delivery: {
47103
+ eventId: randomUUID7(),
47104
+ kind: "reliable",
47105
+ sourceSeq: (sourceSeq + index).toString(),
47106
+ streamId
47107
+ },
47108
+ kind: SESSION_EVENT_KIND,
47109
+ payload,
47110
+ sessionId
47111
+ };
47112
+ const encoded = encodeSessionEventEnvelope(envelope2);
47113
+ return { byteLength: Buffer.byteLength(encoded, "utf8"), encoded, envelope: envelope2 };
47114
+ });
47115
+ const byteLength = stored.reduce((total, entry) => total + entry.byteLength, 0);
47116
+ const maximumBytes = critical ? this.#limits.criticalEventBytes : this.#limits.standardEventBytes;
47117
+ if (rowNumber(state, "total_bytes") + byteLength > maximumBytes) {
47118
+ throw new SessionEventOutboxCapacityError();
47119
+ }
47120
+ const insert = this.#database.prepare(
47121
+ `INSERT INTO relay_session_event_outbox (
47122
+ event_id, session_id, source_seq, byte_length, envelope_json
47123
+ ) VALUES (?, ?, ?, ?, ?)`
47124
+ );
47125
+ for (const entry of stored) {
47126
+ insert.run(
47127
+ entry.envelope.delivery.eventId,
47128
+ sessionId,
47129
+ Number(entry.envelope.delivery.sourceSeq),
47130
+ entry.byteLength,
47131
+ entry.encoded
47132
+ );
47133
+ }
47134
+ this.#database.prepare(
47135
+ `UPDATE relay_session_event_outbox_state
47136
+ SET next_source_seq = next_source_seq + ?,
47137
+ total_bytes = total_bytes + ?
47138
+ WHERE singleton = 1`
47139
+ ).run(stored.length, byteLength);
47140
+ this.#database.exec("COMMIT");
47141
+ return stored.map((entry) => entry.envelope);
47142
+ } catch (error62) {
47143
+ this.#database.exec("ROLLBACK");
47144
+ throw error62;
47145
+ }
47146
+ };
47147
+ pending = (limit, excludedEventIds) => {
47148
+ if (!Number.isSafeInteger(limit) || limit < 1) {
47149
+ throw new Error("Session event replay limit must be a positive integer");
47150
+ }
47151
+ const exclusion = excludedEventIds.length === 0 ? "" : `WHERE event_id NOT IN (${excludedEventIds.map(() => "?").join(", ")})`;
47152
+ return this.#database.prepare(
47153
+ `SELECT envelope_json
47154
+ FROM relay_session_event_outbox
47155
+ ${exclusion}
47156
+ ORDER BY source_seq
47157
+ LIMIT ?`
47158
+ ).all(...excludedEventIds, limit).map((row) => {
47159
+ const envelope2 = decodeSessionEventEnvelope(rowString(row, "envelope_json"));
47160
+ if (!("delivery" in envelope2)) {
47161
+ throw new Error("SQLite returned an invalid reliable Session event envelope");
47162
+ }
47163
+ return envelope2;
47164
+ });
47165
+ };
47166
+ quarantine = (rejection) => {
47167
+ this.#database.exec("BEGIN IMMEDIATE");
47168
+ try {
47169
+ const stored = this.#database.prepare(
47170
+ `SELECT byte_length, envelope_json
47171
+ FROM relay_session_event_outbox
47172
+ WHERE event_id = ? AND session_id = ?`
47173
+ ).get(rejection.eventId, rejection.sessionId);
47174
+ if (stored === void 0) {
47175
+ this.#database.exec("COMMIT");
47176
+ return false;
47177
+ }
47178
+ this.#database.prepare(
47179
+ `INSERT INTO relay_session_event_dead_letters (
47180
+ event_id, session_id, rejection_code, envelope_json
47181
+ ) VALUES (?, ?, ?, ?)`
47182
+ ).run(rejection.eventId, rejection.sessionId, rejection.code, rowString(stored, "envelope_json"));
47183
+ this.#deleteStored(rejection.eventId, rejection.sessionId, rowNumber(stored, "byte_length"));
47184
+ this.#database.prepare(
47185
+ `DELETE FROM relay_session_event_dead_letters
47186
+ WHERE ordinal NOT IN (
47187
+ SELECT ordinal FROM relay_session_event_dead_letters
47188
+ ORDER BY ordinal DESC LIMIT ?
47189
+ )`
47190
+ ).run(MAX_DEAD_LETTERS);
47191
+ this.#database.exec("COMMIT");
47192
+ return true;
47193
+ } catch (error62) {
47194
+ this.#database.exec("ROLLBACK");
47195
+ throw error62;
47196
+ }
47197
+ };
47198
+ #deleteStored = (eventId, sessionId, byteLength) => {
47199
+ const deletion = this.#database.prepare(
47200
+ `DELETE FROM relay_session_event_outbox
47201
+ WHERE event_id = ? AND session_id = ?`
47202
+ ).run(eventId, sessionId);
47203
+ if (deletion.changes !== 1) {
47204
+ throw new Error("The Session event could not be removed from the outbox");
47205
+ }
47206
+ const state = this.#database.prepare(
47207
+ `UPDATE relay_session_event_outbox_state
47208
+ SET total_bytes = total_bytes - ?
47209
+ WHERE singleton = 1 AND total_bytes >= ?`
47210
+ ).run(byteLength, byteLength);
47211
+ if (state.changes !== 1) {
47212
+ throw new Error("The Session event outbox capacity state is inconsistent");
47213
+ }
47214
+ };
47215
+ #remove = (eventId, sessionId) => {
47216
+ this.#database.exec("BEGIN IMMEDIATE");
47217
+ try {
47218
+ const stored = this.#database.prepare(
47219
+ `SELECT byte_length
47220
+ FROM relay_session_event_outbox
47221
+ WHERE event_id = ? AND session_id = ?`
47222
+ ).get(eventId, sessionId);
47223
+ if (stored === void 0) {
47224
+ this.#database.exec("COMMIT");
47225
+ return false;
47226
+ }
47227
+ this.#deleteStored(eventId, sessionId, rowNumber(stored, "byte_length"));
47228
+ this.#database.exec("COMMIT");
47229
+ return true;
47230
+ } catch (error62) {
47231
+ this.#database.exec("ROLLBACK");
47232
+ throw error62;
47233
+ }
47234
+ };
47235
+ };
47236
+
46748
47237
  // src/relay-connection/session-event-publisher.ts
46749
47238
  import { WebSocket as WebSocket9 } from "undici";
46750
- var MAX_LIVE_EVENT_BUFFERED_BYTES = 16 * 1024 * 1024;
47239
+ var SESSION_EVENT_REPLAY_WINDOW = 16;
47240
+ var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
47241
+ function reservesSessionEventCapacity(event) {
47242
+ if (event.kind === SESSION_COMPLETED_KIND || event.kind === SESSION_FAILED_KIND || event.kind === SESSION_FORKED_KIND || event.kind === SESSION_READY_KIND) {
47243
+ return true;
47244
+ }
47245
+ return event.kind === AGENT_ACTIVITY_KIND && (event.activityType === "progress" || event.activityType === "reasoning") && (event.status === "completed" || event.status === "failed");
47246
+ }
46751
47247
  var DaemonSessionEventPublisher = class {
46752
- #socket = null;
46753
- #reportedDrop = false;
46754
- connect = (socket) => {
46755
- this.#socket = socket;
46756
- this.#reportedDrop = false;
47248
+ #inFlight = /* @__PURE__ */ new Map();
47249
+ #outbox;
47250
+ #reportedCapacityKinds = /* @__PURE__ */ new Set();
47251
+ #timer;
47252
+ #connection = { kind: "disconnected" };
47253
+ #deferredReplay = null;
47254
+ #inFlightBytes = 0;
47255
+ constructor(outbox, timer) {
47256
+ this.#outbox = outbox;
47257
+ this.#timer = timer;
47258
+ }
47259
+ acknowledge = (acknowledgement) => {
47260
+ if (!this.#outbox.acknowledge(acknowledgement)) {
47261
+ return;
47262
+ }
47263
+ this.#removeInFlight(acknowledgement.eventId);
47264
+ this.#publishNextBatch();
47265
+ };
47266
+ close = () => {
47267
+ this.#deferredReplay?.cancel();
47268
+ this.#deferredReplay = null;
47269
+ this.#connection = { kind: "disconnected" };
47270
+ this.#clearInFlight();
47271
+ this.#outbox.close();
47272
+ };
47273
+ connect = (socket, delivery) => {
47274
+ this.#connection = { delivery, kind: "connected", socket };
47275
+ this.#clearInFlight();
47276
+ this.#publishNextBatch();
47277
+ };
47278
+ defer = (deferred) => {
47279
+ if (!this.#inFlight.has(deferred.eventId)) {
47280
+ return;
47281
+ }
47282
+ this.#clearInFlight();
47283
+ if (this.#deferredReplay !== null) {
47284
+ return;
47285
+ }
47286
+ this.#deferredReplay = this.#timer.schedule(() => {
47287
+ this.#deferredReplay = null;
47288
+ this.#publishNextBatch();
47289
+ }, deferred.retryAfterMilliseconds);
46757
47290
  };
46758
47291
  disconnect = (socket) => {
46759
- if (this.#socket === socket) {
46760
- this.#socket = null;
47292
+ if (this.#connection.kind !== "connected" || this.#connection.socket !== socket) {
47293
+ return;
46761
47294
  }
47295
+ this.#connection = { kind: "disconnected" };
47296
+ this.#clearInFlight();
46762
47297
  };
46763
47298
  publish = (sessionId, event) => {
46764
- const socket = this.#socket;
46765
- if (socket === null || socket.readyState !== WebSocket9.OPEN) {
46766
- this.#reportDrop();
47299
+ try {
47300
+ this.#outbox.enqueue(sessionId, encodeRecordedSessionEvent(event), reservesSessionEventCapacity(event));
47301
+ } catch (error62) {
47302
+ if (!(error62 instanceof SessionEventOutboxCapacityError)) {
47303
+ throw error62;
47304
+ }
47305
+ if (!this.#reportedCapacityKinds.has(event.kind)) {
47306
+ this.#reportedCapacityKinds.add(event.kind);
47307
+ console.warn(
47308
+ `Mobius daemon could not persist "${event.kind}" because the Session event outbox is full`
47309
+ );
47310
+ }
46767
47311
  return;
46768
47312
  }
46769
- if (socket.bufferedAmount >= MAX_LIVE_EVENT_BUFFERED_BYTES) {
46770
- this.#socket = null;
46771
- socket.close(1013, "Live event backpressure");
46772
- this.#reportDrop();
47313
+ this.#publishNextBatch();
47314
+ };
47315
+ publishBatch = (sessionId, events) => {
47316
+ const critical = events.some(reservesSessionEventCapacity);
47317
+ this.#outbox.enqueueBatch(sessionId, events.map(encodeRecordedSessionEvent), critical);
47318
+ this.#publishNextBatch();
47319
+ };
47320
+ quarantine = (rejection) => {
47321
+ if (!this.#outbox.quarantine(rejection)) {
46773
47322
  return;
46774
47323
  }
46775
- try {
46776
- socket.send(
46777
- encodeDaemonToServiceMessage({
46778
- kind: SESSION_EVENT_KIND,
46779
- payload: encodeRecordedSessionEvent(event),
46780
- sessionId
46781
- })
46782
- );
46783
- } catch {
46784
- this.#reportDrop();
46785
- }
47324
+ this.#removeInFlight(rejection.eventId);
47325
+ console.error(`Mobius Relay rejected Session event ${rejection.eventId} with code ${rejection.code}`);
47326
+ this.#publishNextBatch();
46786
47327
  };
46787
- close = () => {
46788
- this.#socket = null;
47328
+ #clearInFlight = () => {
47329
+ this.#inFlight.clear();
47330
+ this.#inFlightBytes = 0;
47331
+ };
47332
+ #publishNextBatch = () => {
47333
+ if (this.#deferredReplay !== null || this.#connection.kind !== "connected") {
47334
+ return;
47335
+ }
47336
+ while (this.#inFlight.size < SESSION_EVENT_REPLAY_WINDOW) {
47337
+ const batch = this.#outbox.pending(SESSION_EVENT_REPLAY_WINDOW - this.#inFlight.size, [
47338
+ ...this.#inFlight.keys()
47339
+ ]);
47340
+ let sent = 0;
47341
+ for (const envelope2 of batch) {
47342
+ const encoded = encodeDaemonToServiceMessage(
47343
+ this.#connection.delivery === "reliable" ? envelope2 : {
47344
+ kind: envelope2.kind,
47345
+ payload: envelope2.payload,
47346
+ sessionId: envelope2.sessionId
47347
+ }
47348
+ );
47349
+ const byteLength = Buffer.byteLength(encoded, "utf8");
47350
+ if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
47351
+ return;
47352
+ }
47353
+ if (!this.#send(encoded)) {
47354
+ this.#clearInFlight();
47355
+ return;
47356
+ }
47357
+ if (this.#connection.delivery === "ephemeral") {
47358
+ this.#outbox.acknowledge({
47359
+ eventId: envelope2.delivery.eventId,
47360
+ kind: "session.event.ack",
47361
+ outcome: { kind: "relayed" },
47362
+ sessionId: envelope2.sessionId
47363
+ });
47364
+ sent += 1;
47365
+ continue;
47366
+ }
47367
+ this.#inFlight.set(envelope2.delivery.eventId, { byteLength });
47368
+ this.#inFlightBytes += byteLength;
47369
+ sent += 1;
47370
+ }
47371
+ if (sent === 0) {
47372
+ return;
47373
+ }
47374
+ }
46789
47375
  };
46790
- #reportDrop = () => {
46791
- if (this.#reportedDrop) {
47376
+ #removeInFlight = (eventId) => {
47377
+ const inFlight = this.#inFlight.get(eventId);
47378
+ if (inFlight === void 0) {
46792
47379
  return;
46793
47380
  }
46794
- this.#reportedDrop = true;
46795
- console.warn("Mobius daemon dropped live Session events while the Relay was unavailable");
47381
+ this.#inFlight.delete(eventId);
47382
+ this.#inFlightBytes -= inFlight.byteLength;
47383
+ };
47384
+ #send = (encoded) => {
47385
+ if (this.#connection.kind !== "connected" || this.#connection.socket.readyState !== WebSocket9.OPEN) {
47386
+ return false;
47387
+ }
47388
+ try {
47389
+ this.#connection.socket.send(encoded);
47390
+ return true;
47391
+ } catch {
47392
+ return false;
47393
+ }
46796
47394
  };
46797
47395
  };
46798
47396
 
@@ -46853,7 +47451,7 @@ async function installDetectedAgentIntegrations(config2, installations, signal)
46853
47451
  {
46854
47452
  agentId: agent.id,
46855
47453
  kind: AGENT_INSTALL_REQUEST_KIND,
46856
- requestId: `automatic-integration-${randomUUID7()}`
47454
+ requestId: `automatic-integration-${randomUUID8()}`
46857
47455
  },
46858
47456
  signal
46859
47457
  );
@@ -46875,7 +47473,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
46875
47473
  join18(stateDirectory, "sessions", `${createHash6("sha256").update(config2.channelId).digest("hex")}.sqlite`)
46876
47474
  );
46877
47475
  const operations = new DaemonOperationState();
46878
- const publisher = new DaemonSessionEventPublisher();
47476
+ const channelHash = createHash6("sha256").update(config2.channelId).digest("hex");
47477
+ const publisher = new DaemonSessionEventPublisher(
47478
+ new SessionEventOutbox(join18(stateDirectory, "events", `${channelHash}.sqlite`)),
47479
+ timer
47480
+ );
46879
47481
  const activeTurns = new DaemonTurnRegistry(operations);
46880
47482
  const recordSessionEvent = (sessionId, event) => {
46881
47483
  publisher.publish(sessionId, activeTurns.recordEvent(event));