@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/runtime.js CHANGED
@@ -20740,9 +20740,17 @@ function decodeSessionQueuedEvent(encoded) {
20740
20740
  }
20741
20741
  function decodeRuntimeLivenessReadyEvent(encoded) {
20742
20742
  const value = parseJson2(encoded);
20743
- assertExactKeys(value, ["kind"]);
20744
20743
  assertKind(value, RUNTIME_LIVENESS_READY_KIND);
20745
- return { kind: RUNTIME_LIVENESS_READY_KIND };
20744
+ if (value["sessionEventDelivery"] === void 0) {
20745
+ assertExactKeys(value, ["kind"]);
20746
+ return { kind: RUNTIME_LIVENESS_READY_KIND };
20747
+ }
20748
+ assertExactKeys(value, ["kind", "sessionEventDelivery"]);
20749
+ const sessionEventDelivery = readString(value, "sessionEventDelivery", 32);
20750
+ if (sessionEventDelivery !== "reliable-v1") {
20751
+ throw new ProtocolMessageError("Unsupported Session event delivery mode");
20752
+ }
20753
+ return { kind: RUNTIME_LIVENESS_READY_KIND, sessionEventDelivery };
20746
20754
  }
20747
20755
  function decodeRuntimeHeartbeatEvent(encoded) {
20748
20756
  const value = parseJson2(encoded);
@@ -21074,6 +21082,12 @@ var nativeSessionRequestSchema = external_exports.discriminatedUnion("action", [
21074
21082
  entryId: identifierStringSchema,
21075
21083
  targetSessionId: identifierStringSchema
21076
21084
  }).readonly(),
21085
+ external_exports.strictObject({
21086
+ ...identity,
21087
+ action: external_exports.literal("fork-recorded"),
21088
+ targetSessionId: identifierStringSchema,
21089
+ throughCommandId: identifierStringSchema
21090
+ }).readonly(),
21077
21091
  external_exports.strictObject({
21078
21092
  ...identity,
21079
21093
  action: external_exports.literal("delete"),
@@ -21097,6 +21111,12 @@ var nativeSessionCommandSchema = external_exports.discriminatedUnion("action", [
21097
21111
  entryId: identifierStringSchema,
21098
21112
  targetSessionId: identifierStringSchema
21099
21113
  }).readonly(),
21114
+ external_exports.strictObject({
21115
+ ...identity,
21116
+ action: external_exports.literal("fork-recorded"),
21117
+ targetSessionId: identifierStringSchema,
21118
+ throughCommandId: identifierStringSchema
21119
+ }).readonly(),
21100
21120
  external_exports.strictObject({
21101
21121
  ...identity,
21102
21122
  action: external_exports.literal("delete"),
@@ -21146,7 +21166,7 @@ var nativeSessionResultSchema = external_exports.discriminatedUnion("action", [
21146
21166
  }).readonly(),
21147
21167
  external_exports.strictObject({
21148
21168
  ...resultEnvelope,
21149
- action: external_exports.enum(["import", "fork"]),
21169
+ action: external_exports.enum(["import", "fork", "fork-recorded"]),
21150
21170
  targetSessionId: identifierStringSchema,
21151
21171
  ready: sessionReadyEventSchema,
21152
21172
  title: external_exports.string().max(500)
@@ -21192,14 +21212,68 @@ var encodeNativeSessionCancel = (value) => JSON.stringify(nativeSessionCancelSch
21192
21212
 
21193
21213
  // ../protocol/src/session-events.ts
21194
21214
  var SESSION_EVENT_KIND = "session.event";
21215
+ var SESSION_EVENT_ACK_KIND = "session.event.ack";
21216
+ var SESSION_EVENT_DEFERRED_KIND = "session.event.deferred";
21217
+ var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
21218
+ var SESSION_FORKED_KIND = "session.forked";
21219
+ var SessionEventRejectionCode = {
21220
+ IdempotencyConflict: "idempotency_conflict",
21221
+ InvalidEvent: "invalid_event",
21222
+ RecordingRejected: "recording_rejected"
21223
+ };
21195
21224
  var MAX_RECORDED_EVENT_LENGTH = 8 * 1024 * 1024;
21196
- var eventEnvelopeSchema = external_exports.strictObject({
21225
+ var ephemeralEventEnvelopeSchema = external_exports.strictObject({
21197
21226
  kind: external_exports.literal(SESSION_EVENT_KIND),
21198
21227
  payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
21199
21228
  sessionId: identifierStringSchema
21200
21229
  }).readonly();
21230
+ var reliableDeliverySchema = external_exports.strictObject({
21231
+ eventId: identifierStringSchema,
21232
+ kind: external_exports.literal("reliable"),
21233
+ sourceSeq: external_exports.string().regex(/^(?:0|[1-9][0-9]*)$/u).max(40),
21234
+ streamId: identifierStringSchema
21235
+ }).readonly();
21236
+ var reliableSessionEventEnvelopeSchema = external_exports.strictObject({
21237
+ delivery: reliableDeliverySchema,
21238
+ kind: external_exports.literal(SESSION_EVENT_KIND),
21239
+ payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
21240
+ sessionId: identifierStringSchema
21241
+ }).readonly();
21242
+ var eventEnvelopeSchema = external_exports.union([ephemeralEventEnvelopeSchema, reliableSessionEventEnvelopeSchema]).readonly();
21243
+ var acknowledgementOutcomeSchema = external_exports.discriminatedUnion("kind", [
21244
+ external_exports.strictObject({ kind: external_exports.literal("relayed") }).readonly(),
21245
+ external_exports.strictObject({
21246
+ kind: external_exports.literal("recorded"),
21247
+ providerId: identifierStringSchema,
21248
+ recordingEpoch: identifierStringSchema
21249
+ }).readonly()
21250
+ ]);
21251
+ var eventAcknowledgementSchema = external_exports.strictObject({
21252
+ eventId: identifierStringSchema,
21253
+ kind: external_exports.literal(SESSION_EVENT_ACK_KIND),
21254
+ outcome: acknowledgementOutcomeSchema,
21255
+ sessionId: identifierStringSchema
21256
+ }).readonly();
21257
+ var eventDeferredSchema = external_exports.strictObject({
21258
+ eventId: identifierStringSchema,
21259
+ kind: external_exports.literal(SESSION_EVENT_DEFERRED_KIND),
21260
+ retryAfterMilliseconds: external_exports.number().int().min(100).max(6e4),
21261
+ sessionId: identifierStringSchema
21262
+ }).readonly();
21263
+ var eventRejectedSchema = external_exports.strictObject({
21264
+ code: external_exports.enum(SessionEventRejectionCode),
21265
+ eventId: identifierStringSchema,
21266
+ kind: external_exports.literal(SESSION_EVENT_REJECTED_KIND),
21267
+ sessionId: identifierStringSchema
21268
+ }).readonly();
21201
21269
  var SESSION_QUEUE_CHANGED_KIND = "session.queue.changed";
21202
21270
  var sessionQueueChangedSchema = external_exports.strictObject({ kind: external_exports.literal(SESSION_QUEUE_CHANGED_KIND), sessionId: identifierStringSchema }).readonly();
21271
+ var sessionForkedSchema = external_exports.strictObject({
21272
+ kind: external_exports.literal(SESSION_FORKED_KIND),
21273
+ sessionId: identifierStringSchema,
21274
+ sourceSessionId: identifierStringSchema,
21275
+ throughCommandId: identifierStringSchema
21276
+ }).readonly();
21203
21277
  function decode5(encoded, schema) {
21204
21278
  let value;
21205
21279
  try {
@@ -21226,11 +21300,19 @@ var encode4 = (message, schema) => {
21226
21300
  };
21227
21301
  var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
21228
21302
  var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
21303
+ var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
21304
+ var encodeSessionEventAcknowledgement = (message) => encode4(message, eventAcknowledgementSchema);
21305
+ var decodeSessionEventDeferred = (encoded) => decode5(encoded, eventDeferredSchema);
21306
+ var encodeSessionEventDeferred = (message) => encode4(message, eventDeferredSchema);
21307
+ var decodeSessionEventRejected = (encoded) => decode5(encoded, eventRejectedSchema);
21308
+ var encodeSessionEventRejected = (message) => encode4(message, eventRejectedSchema);
21229
21309
  function decodeRecordedSessionEvent(encoded) {
21230
21310
  const kind = decodeMessageKind(encoded);
21231
21311
  switch (kind) {
21232
21312
  case SESSION_QUEUE_CHANGED_KIND:
21233
21313
  return decode5(encoded, sessionQueueChangedSchema);
21314
+ case SESSION_FORKED_KIND:
21315
+ return decode5(encoded, sessionForkedSchema);
21234
21316
  case AGENT_ACTIVITY_KIND:
21235
21317
  return decodeAgentActivityEvent(encoded);
21236
21318
  case AGENT_ACTIVITY_NARRATION_KIND:
@@ -21397,6 +21479,21 @@ var serviceToDaemonSignatures = {
21397
21479
  encodeSessionElicitationRespondCommand,
21398
21480
  decodeSessionElicitationRespondCommand
21399
21481
  ),
21482
+ [SESSION_EVENT_ACK_KIND]: defineMessageSignature(
21483
+ SESSION_EVENT_ACK_KIND,
21484
+ encodeSessionEventAcknowledgement,
21485
+ decodeSessionEventAcknowledgement
21486
+ ),
21487
+ [SESSION_EVENT_DEFERRED_KIND]: defineMessageSignature(
21488
+ SESSION_EVENT_DEFERRED_KIND,
21489
+ encodeSessionEventDeferred,
21490
+ decodeSessionEventDeferred
21491
+ ),
21492
+ [SESSION_EVENT_REJECTED_KIND]: defineMessageSignature(
21493
+ SESSION_EVENT_REJECTED_KIND,
21494
+ encodeSessionEventRejected,
21495
+ decodeSessionEventRejected
21496
+ ),
21400
21497
  [SESSION_LIVE_HISTORY_LOAD_KIND]: defineMessageSignature(
21401
21498
  SESSION_LIVE_HISTORY_LOAD_KIND,
21402
21499
  encodeSessionLiveHistoryLoad,
@@ -26691,7 +26788,7 @@ function createDaemonConfig(connection, agents = [], installableAgents = [], loc
26691
26788
  }
26692
26789
 
26693
26790
  // src/run-daemon.ts
26694
- import { createHash as createHash5, randomUUID as randomUUID6 } from "node:crypto";
26791
+ import { createHash as createHash5, randomUUID as randomUUID7 } from "node:crypto";
26695
26792
  import { join as join17 } from "node:path";
26696
26793
  import { styleText } from "node:util";
26697
26794
 
@@ -28256,13 +28353,15 @@ var AcpPreparedSession = class {
28256
28353
  capabilities;
28257
28354
  commands;
28258
28355
  configOptions;
28356
+ prompted;
28259
28357
  restored;
28260
- constructor(agentName, agentVersion, capabilities, commands, configOptions, restored) {
28358
+ constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored) {
28261
28359
  this.agentName = agentName;
28262
28360
  this.agentVersion = agentVersion;
28263
28361
  this.capabilities = capabilities;
28264
28362
  this.commands = commands;
28265
28363
  this.configOptions = configOptions;
28364
+ this.prompted = prompted;
28266
28365
  this.restored = restored;
28267
28366
  }
28268
28367
  };
@@ -34432,6 +34531,10 @@ var acpSessionStoreMigrations = [
34432
34531
  {
34433
34532
  "name": "0000_slimy_captain_midlands.sql",
34434
34533
  "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"
34534
+ },
34535
+ {
34536
+ "name": "0001_regular_ultimates.sql",
34537
+ "sql": "ALTER TABLE `acp_session` ADD `last_terminal_command_id` text;"
34435
34538
  }
34436
34539
  ];
34437
34540
 
@@ -34449,6 +34552,7 @@ var acpSession = sqliteTable("acp_session", {
34449
34552
  nativeSessionId: text("native_session_id").notNull(),
34450
34553
  cwd: text("cwd").notNull(),
34451
34554
  additionalDirectories: text("additional_directories").notNull(),
34555
+ lastTerminalCommandId: text("last_terminal_command_id"),
34452
34556
  prompted: integer2("prompted", { mode: "boolean" }).notNull(),
34453
34557
  title: text("title").notNull(),
34454
34558
  updatedAt: text("updated_at").notNull()
@@ -34464,15 +34568,17 @@ var AcpStoredSession = class {
34464
34568
  additionalDirectories;
34465
34569
  agentId;
34466
34570
  cwd;
34571
+ lastTerminalCommandId;
34467
34572
  nativeSessionId;
34468
34573
  prompted;
34469
34574
  sessionId;
34470
34575
  title;
34471
34576
  updatedAt;
34472
- constructor(sessionId, agentId, nativeSessionId, cwd, additionalDirectories, prompted, title2, updatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
34577
+ constructor(sessionId, agentId, nativeSessionId, cwd, additionalDirectories, prompted, title2, updatedAt = (/* @__PURE__ */ new Date()).toISOString(), lastTerminalCommandId = null) {
34473
34578
  this.additionalDirectories = [...additionalDirectories];
34474
34579
  this.agentId = agentId;
34475
34580
  this.cwd = cwd;
34581
+ this.lastTerminalCommandId = lastTerminalCommandId;
34476
34582
  this.nativeSessionId = nativeSessionId;
34477
34583
  this.prompted = prompted;
34478
34584
  this.sessionId = sessionId;
@@ -34533,6 +34639,7 @@ var storedSessionRowSchema = external_exports.object({
34533
34639
  additionalDirectories: external_exports.string(),
34534
34640
  agentId: external_exports.string(),
34535
34641
  cwd: external_exports.string(),
34642
+ lastTerminalCommandId: external_exports.string().nullable(),
34536
34643
  nativeSessionId: external_exports.string(),
34537
34644
  prompted: external_exports.boolean(),
34538
34645
  sessionId: external_exports.string(),
@@ -34552,7 +34659,8 @@ function decodeStoredSession(value) {
34552
34659
  additionalDirectories,
34553
34660
  row.prompted,
34554
34661
  row.title,
34555
- row.updatedAt
34662
+ row.updatedAt,
34663
+ row.lastTerminalCommandId
34556
34664
  );
34557
34665
  }
34558
34666
  var SqliteAcpSessionStore = class {
@@ -34619,6 +34727,7 @@ var SqliteAcpSessionStore = class {
34619
34727
  additionalDirectories: JSON.stringify(session.additionalDirectories),
34620
34728
  agentId: session.agentId,
34621
34729
  cwd: session.cwd,
34730
+ lastTerminalCommandId: session.lastTerminalCommandId,
34622
34731
  nativeSessionId: session.nativeSessionId,
34623
34732
  prompted: session.prompted,
34624
34733
  sessionId: session.sessionId,
@@ -34671,6 +34780,7 @@ var AgentSession = class _AgentSession {
34671
34780
  #commands = [];
34672
34781
  #configRevision = 0;
34673
34782
  #configOptions = [];
34783
+ #lastTerminalCommandId = null;
34674
34784
  #prompted = false;
34675
34785
  #nativePrompt = null;
34676
34786
  #sessionLoad = { kind: "not-requested" };
@@ -34774,6 +34884,7 @@ var AgentSession = class _AgentSession {
34774
34884
  this.#capabilities,
34775
34885
  this.#commands,
34776
34886
  this.#configOptions,
34887
+ this.#prompted,
34777
34888
  this.#restored
34778
34889
  );
34779
34890
  loadHistory = async (signal) => {
@@ -35007,6 +35118,7 @@ var AgentSession = class _AgentSession {
35007
35118
  } else if (stored instanceof FoundAcpStoredSession && stored.session.agentId === this.#agentId && stored.session.cwd === this.#cwd && arraysEqual(stored.session.additionalDirectories, this.#additionalDirectories)) {
35008
35119
  this.selectNativeSession(stored.session.nativeSessionId);
35009
35120
  this.#prompted = stored.session.prompted;
35121
+ this.#lastTerminalCommandId = stored.session.lastTerminalCommandId;
35010
35122
  this.#sessionTitle = stored.session.title;
35011
35123
  this.#restored = true;
35012
35124
  if (this.#capabilities.resumeSession) {
@@ -35080,7 +35192,9 @@ var AgentSession = class _AgentSession {
35080
35192
  this.#cwd,
35081
35193
  this.#additionalDirectories,
35082
35194
  this.#prompted,
35083
- this.#sessionTitle ?? ""
35195
+ this.#sessionTitle ?? "",
35196
+ (/* @__PURE__ */ new Date()).toISOString(),
35197
+ this.#lastTerminalCommandId
35084
35198
  )
35085
35199
  );
35086
35200
  };
@@ -35280,7 +35394,9 @@ var AgentSession = class _AgentSession {
35280
35394
  this.#cwd,
35281
35395
  this.#additionalDirectories,
35282
35396
  true,
35283
- this.#sessionTitle
35397
+ this.#sessionTitle,
35398
+ (/* @__PURE__ */ new Date()).toISOString(),
35399
+ this.#lastTerminalCommandId
35284
35400
  )
35285
35401
  );
35286
35402
  this.#prompted = true;
@@ -35332,6 +35448,20 @@ var AgentSession = class _AgentSession {
35332
35448
  usage: result.usage ?? null
35333
35449
  });
35334
35450
  }
35451
+ this.#lastTerminalCommandId = options.commandId;
35452
+ this.#store.write(
35453
+ new AcpStoredSession(
35454
+ this.#mobiusSessionId,
35455
+ this.#agentId,
35456
+ this.#updates.sessionId(),
35457
+ this.#cwd,
35458
+ this.#additionalDirectories,
35459
+ true,
35460
+ this.#sessionTitle ?? "",
35461
+ (/* @__PURE__ */ new Date()).toISOString(),
35462
+ this.#lastTerminalCommandId
35463
+ )
35464
+ );
35335
35465
  return result;
35336
35466
  } catch (error62) {
35337
35467
  await running.activities.finish();
@@ -35524,7 +35654,9 @@ var AgentSession = class _AgentSession {
35524
35654
  this.#cwd,
35525
35655
  this.#additionalDirectories,
35526
35656
  this.#prompted,
35527
- this.#sessionTitle ?? ""
35657
+ this.#sessionTitle ?? "",
35658
+ (/* @__PURE__ */ new Date()).toISOString(),
35659
+ this.#lastTerminalCommandId
35528
35660
  )
35529
35661
  );
35530
35662
  }
@@ -36570,9 +36702,17 @@ var AgentRuntimeManager = class {
36570
36702
  const openedKey = `${options2.agent.id}:${runtime.nativeSessionId()}`;
36571
36703
  this.#nativeOperations.add(openedKey);
36572
36704
  try {
36705
+ const prepared = runtime.prepared();
36706
+ if (origin.kind === "fork" && options2.agent.id === "claude") {
36707
+ await this.#retireSessionProcess(
36708
+ options2.sessionId,
36709
+ runtime,
36710
+ async () => await runtime.closeProcess()
36711
+ );
36712
+ }
36573
36713
  return {
36574
36714
  sessionId: options2.sessionId,
36575
- prepared: runtime.prepared()
36715
+ prepared
36576
36716
  };
36577
36717
  } finally {
36578
36718
  this.#nativeOperations.delete(openedKey);
@@ -38983,6 +39123,7 @@ var readCodexFullHistory = async (agent, cwd, nativeSessionId, signal, nativeTur
38983
39123
 
38984
39124
  // src/native-sessions/service.ts
38985
39125
  import { randomUUID as randomUUID2 } from "node:crypto";
39126
+ import { basename as basename4 } from "node:path";
38986
39127
 
38987
39128
  // src/native-sessions/acp-history.ts
38988
39129
  async function readAcpHistory(sessions, directories, bindingId, options, nativeSessionId) {
@@ -39123,6 +39264,64 @@ var NativeSessionService = class {
39123
39264
  sessionId: command.sessionId
39124
39265
  };
39125
39266
  }
39267
+ if (command.action === "fork-recorded") {
39268
+ const source = this.#sessions.findSessions([command.sessionId])[0];
39269
+ if (source === void 0 || !source.prompted) {
39270
+ throw new AcpExecutionError(
39271
+ "The source conversation has no resumable native Agent session",
39272
+ {},
39273
+ "native_session_not_found"
39274
+ );
39275
+ }
39276
+ if (source.agentId !== command.agentId || source.cwd !== cwd || source.additionalDirectories.length !== additionalDirectories.length || source.additionalDirectories.some((path2, index) => path2 !== additionalDirectories[index])) {
39277
+ throw new AcpExecutionError(
39278
+ "The source conversation execution target does not match the fork request",
39279
+ {},
39280
+ "native_session_target_changed"
39281
+ );
39282
+ }
39283
+ if (source.lastTerminalCommandId !== command.throughCommandId) {
39284
+ throw new AcpExecutionError(
39285
+ "The selected Turn is no longer the source conversation head",
39286
+ {},
39287
+ "fork_point_stale"
39288
+ );
39289
+ }
39290
+ const opened2 = await this.#sessions.openNativeSession(
39291
+ { ...options, sessionId: command.targetSessionId },
39292
+ { kind: "fork", nativeSessionId: source.nativeSessionId }
39293
+ );
39294
+ signal.throwIfAborted();
39295
+ const directory = await this.#directories.list(this.#config.bindingId, command.directoryId, signal);
39296
+ const ready2 = {
39297
+ additionalDirectories: additionalDirectories.map((path2, index) => ({
39298
+ directoryId: command.additionalDirectoryIds[index] ?? command.directoryId,
39299
+ name: basename4(path2) || path2
39300
+ })),
39301
+ agentId: command.agentId,
39302
+ agentName: opened2.prepared.agentName,
39303
+ agentVersion: opened2.prepared.agentVersion,
39304
+ capabilities: opened2.prepared.capabilities,
39305
+ commands: opened2.prepared.commands,
39306
+ configOptions: opened2.prepared.configOptions,
39307
+ directoryId: command.directoryId,
39308
+ directoryLabel: directory.directoryLabel,
39309
+ kind: "session.ready",
39310
+ requestId: command.requestId,
39311
+ restored: true,
39312
+ sessionId: opened2.sessionId
39313
+ };
39314
+ this.record(command, "native_recorded_session_forked");
39315
+ return {
39316
+ action: "fork-recorded",
39317
+ kind: "native.session.result",
39318
+ ready: ready2,
39319
+ requestId: command.requestId,
39320
+ sessionId: command.sessionId,
39321
+ targetSessionId: command.targetSessionId,
39322
+ title: source.title || "Untitled session"
39323
+ };
39324
+ }
39126
39325
  const prepared = await this.#sessions.prepareNativeSessionBrowser(options);
39127
39326
  if (!prepared.capabilities.listSessions) {
39128
39327
  throw new AcpExecutionError(
@@ -40444,7 +40643,7 @@ function createAgentSettingsDiscoveryHandler(dependencies) {
40444
40643
  // src/artifacts/local-file-streamer.ts
40445
40644
  import { createReadStream as createReadStream2 } from "node:fs";
40446
40645
  import { realpath as realpath8, stat as stat7 } from "node:fs/promises";
40447
- import { basename as basename4, isAbsolute as isAbsolute10, normalize as normalize4, resolve as resolve5 } from "node:path";
40646
+ import { basename as basename5, isAbsolute as isAbsolute10, normalize as normalize4, resolve as resolve5 } from "node:path";
40448
40647
  import { fileURLToPath as fileURLToPath2 } from "node:url";
40449
40648
  var CHUNK_BYTES = 192 * 1024;
40450
40649
  function requestedPath(value) {
@@ -40495,7 +40694,7 @@ async function streamLocalFile(command, send2, signal, workingDirectory) {
40495
40694
  kind: FILE_BEGIN_KIND,
40496
40695
  mediaType: raster?.mimeType ?? "application/octet-stream",
40497
40696
  modifiedAt: before.mtime.toISOString(),
40498
- name: basename4(target) || "file",
40697
+ name: basename5(target) || "file",
40499
40698
  requestId: command.requestId,
40500
40699
  sessionId: command.sessionId
40501
40700
  });
@@ -40525,7 +40724,7 @@ async function streamLocalFile(command, send2, signal, workingDirectory) {
40525
40724
  }
40526
40725
 
40527
40726
  // src/authorization/resolve-session-directories.ts
40528
- import { basename as basename5 } from "node:path";
40727
+ import { basename as basename6 } from "node:path";
40529
40728
  var ResolvedSessionDirectories = class {
40530
40729
  additionalDirectories;
40531
40730
  additionalDirectoryEntries;
@@ -40550,7 +40749,7 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
40550
40749
  additionalDirectories.push(directory);
40551
40750
  additionalDirectoryEntries.push({
40552
40751
  directoryId: additionalDirectoryId,
40553
- name: basename5(directory)
40752
+ name: basename6(directory)
40554
40753
  });
40555
40754
  }
40556
40755
  return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
@@ -41287,14 +41486,15 @@ async function executePrompt({
41287
41486
  const appliedSettings = command.sessionConfigurations === void 0 ? void 0 : agentForSessionConfiguration(agent, command.sessionConfigurations);
41288
41487
  const executionAgent = appliedSettings?.agent ?? agent;
41289
41488
  const sessionConfigurations = appliedSettings?.sessionConfigurations;
41489
+ const preparation = {
41490
+ additionalDirectories: resolved.additionalDirectories,
41491
+ agent: executionAgent,
41492
+ cwd: resolved.cwd,
41493
+ sessionId: command.sessionId,
41494
+ signal
41495
+ };
41496
+ let prepared = await sessions.prepareExecutionSession(preparation);
41290
41497
  if (command.sessionConfigurations !== void 0) {
41291
- const prepared = await sessions.prepareExecutionSession({
41292
- additionalDirectories: resolved.additionalDirectories,
41293
- agent: executionAgent,
41294
- cwd: resolved.cwd,
41295
- sessionId: command.sessionId,
41296
- signal
41297
- });
41298
41498
  if (availability.disposition === SessionPromptDisposition.Steer) {
41299
41499
  assertAppliedSessionConfiguration(prepared.configOptions, sessionConfigurations ?? []);
41300
41500
  } else {
@@ -41310,6 +41510,7 @@ async function executePrompt({
41310
41510
  command.sessionId,
41311
41511
  signal
41312
41512
  );
41513
+ prepared = await sessions.prepareExecutionSession(preparation);
41313
41514
  }
41314
41515
  }
41315
41516
  audit.record({
@@ -41328,6 +41529,24 @@ async function executePrompt({
41328
41529
  }
41329
41530
  }
41330
41531
  const stream = activateAttempt();
41532
+ if (!prepared.prompted) {
41533
+ const listing = await directories.list(config2.bindingId, command.directoryId, signal);
41534
+ publisher.publish(command.sessionId, {
41535
+ additionalDirectories: resolved.additionalDirectoryEntries,
41536
+ agentId: command.agentId,
41537
+ agentName: prepared.agentName,
41538
+ agentVersion: prepared.agentVersion,
41539
+ capabilities: prepared.capabilities,
41540
+ commands: prepared.commands,
41541
+ configOptions: prepared.configOptions,
41542
+ directoryId: command.directoryId,
41543
+ directoryLabel: listing.directoryLabel,
41544
+ kind: SESSION_READY_KIND,
41545
+ requestId: command.commandId,
41546
+ restored: prepared.restored,
41547
+ sessionId: command.sessionId
41548
+ });
41549
+ }
41331
41550
  publisher.publish(command.sessionId, {
41332
41551
  commandId: command.commandId,
41333
41552
  epoch: stream.epoch,
@@ -41581,7 +41800,7 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
41581
41800
  // src/native-sessions/handle-native-requests.ts
41582
41801
  import { WebSocket as WebSocket5 } from "undici";
41583
41802
  function createNativeSessionRequestHandler(dependencies) {
41584
- const { audit, diagnostics, nativeSessions, signal, socket, timer } = dependencies;
41803
+ const { audit, diagnostics, nativeSessions, publisher, signal, socket, timer } = dependencies;
41585
41804
  const pending = /* @__PURE__ */ new Map();
41586
41805
  const cancel = (command) => {
41587
41806
  const request = pending.get(command.requestId);
@@ -41633,6 +41852,17 @@ function createNativeSessionRequestHandler(dependencies) {
41633
41852
  }
41634
41853
  operationSignal.throwIfAborted();
41635
41854
  const result = await nativeSessions.execute(command, operationSignal);
41855
+ if (command.action === "fork-recorded" && result.action === "fork-recorded") {
41856
+ publisher.publishBatch(result.ready.sessionId, [
41857
+ result.ready,
41858
+ {
41859
+ kind: "session.forked",
41860
+ sessionId: result.ready.sessionId,
41861
+ sourceSessionId: command.sessionId,
41862
+ throughCommandId: command.throughCommandId
41863
+ }
41864
+ ]);
41865
+ }
41636
41866
  if (socket.readyState === WebSocket5.OPEN) {
41637
41867
  send(socket, result);
41638
41868
  }
@@ -41718,6 +41948,7 @@ function createDaemonCommandHandler({
41718
41948
  audit,
41719
41949
  diagnostics,
41720
41950
  nativeSessions,
41951
+ publisher,
41721
41952
  signal,
41722
41953
  socket,
41723
41954
  timer
@@ -42240,6 +42471,7 @@ async function runRelayConnection({
42240
42471
  }) {
42241
42472
  const relayUrl = new URL(config2.relayUrl);
42242
42473
  relayUrl.searchParams.set("liveness", "1");
42474
+ relayUrl.searchParams.set("sessionEventDelivery", "reliable-v1");
42243
42475
  const socket = await connectRelay(relayUrl, signal);
42244
42476
  diagnostics.connectionOpened(connectionEpoch);
42245
42477
  const connectionController = new AbortController();
@@ -42359,6 +42591,10 @@ async function runRelayConnection({
42359
42591
  const message = decodeServiceToDaemonMessage(encoded);
42360
42592
  const kind = message.kind;
42361
42593
  if (kind === RUNTIME_LIVENESS_READY_KIND) {
42594
+ publisher.connect(
42595
+ socket,
42596
+ message.sessionEventDelivery === "reliable-v1" ? "reliable" : "ephemeral"
42597
+ );
42362
42598
  connectionLiveness.start();
42363
42599
  return;
42364
42600
  }
@@ -42366,6 +42602,18 @@ async function runRelayConnection({
42366
42602
  connectionLiveness.acknowledge(message);
42367
42603
  return;
42368
42604
  }
42605
+ if (kind === SESSION_EVENT_ACK_KIND) {
42606
+ publisher.acknowledge(message);
42607
+ return;
42608
+ }
42609
+ if (kind === SESSION_EVENT_DEFERRED_KIND) {
42610
+ publisher.defer(message);
42611
+ return;
42612
+ }
42613
+ if (kind === SESSION_EVENT_REJECTED_KIND) {
42614
+ publisher.quarantine(message);
42615
+ return;
42616
+ }
42369
42617
  commands.handle(message);
42370
42618
  } catch (error62) {
42371
42619
  console.error("Mobius daemon rejected a message", error62);
@@ -42380,7 +42628,6 @@ async function runRelayConnection({
42380
42628
  socket.addEventListener("close", handleClose);
42381
42629
  socket.addEventListener("error", handleError);
42382
42630
  socket.addEventListener("message", handleMessage);
42383
- publisher.connect(socket);
42384
42631
  if (signal.aborted) {
42385
42632
  stop();
42386
42633
  }
@@ -42391,54 +42638,405 @@ async function runRelayConnection({
42391
42638
  }
42392
42639
  }
42393
42640
 
42641
+ // src/relay-connection/session-event-outbox.ts
42642
+ import { randomUUID as randomUUID6 } from "node:crypto";
42643
+ import { mkdirSync as mkdirSync3 } from "node:fs";
42644
+ import { dirname as dirname5 } from "node:path";
42645
+ import { DatabaseSync } from "node:sqlite";
42646
+ var DEFAULT_STANDARD_EVENT_BYTES = 96 * 1024 * 1024;
42647
+ var DEFAULT_CRITICAL_EVENT_BYTES = 128 * 1024 * 1024;
42648
+ var MAX_DEAD_LETTERS = 1024;
42649
+ var DEFAULT_LIMITS = {
42650
+ criticalEventBytes: DEFAULT_CRITICAL_EVENT_BYTES,
42651
+ standardEventBytes: DEFAULT_STANDARD_EVENT_BYTES
42652
+ };
42653
+ var SessionEventOutboxCapacityError = class extends Error {
42654
+ code = "session_event_outbox_full";
42655
+ constructor() {
42656
+ super("The persistent Session event outbox is full");
42657
+ this.name = "SessionEventOutboxCapacityError";
42658
+ }
42659
+ };
42660
+ function rowNumber(row, field) {
42661
+ const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
42662
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
42663
+ throw new Error(`SQLite returned an invalid ${field} value`);
42664
+ }
42665
+ return value;
42666
+ }
42667
+ function rowString(row, field) {
42668
+ const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
42669
+ if (typeof value !== "string" || value.length === 0) {
42670
+ throw new Error(`SQLite returned an invalid ${field} value`);
42671
+ }
42672
+ return value;
42673
+ }
42674
+ function validateLimits(limits) {
42675
+ if (!Number.isSafeInteger(limits.standardEventBytes) || limits.standardEventBytes < 1 || !Number.isSafeInteger(limits.criticalEventBytes) || limits.criticalEventBytes < limits.standardEventBytes) {
42676
+ throw new Error("Session event outbox limits are invalid");
42677
+ }
42678
+ }
42679
+ var SessionEventOutbox = class {
42680
+ #database;
42681
+ #limits;
42682
+ constructor(path2, limits = DEFAULT_LIMITS) {
42683
+ validateLimits(limits);
42684
+ mkdirSync3(dirname5(path2), { mode: 448, recursive: true });
42685
+ this.#limits = limits;
42686
+ this.#database = new DatabaseSync(path2);
42687
+ this.#database.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL; PRAGMA busy_timeout = 5000;");
42688
+ this.#database.exec(`
42689
+ CREATE TABLE IF NOT EXISTS relay_session_event_outbox (
42690
+ ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
42691
+ event_id TEXT NOT NULL UNIQUE,
42692
+ session_id TEXT NOT NULL,
42693
+ source_seq INTEGER NOT NULL UNIQUE,
42694
+ byte_length INTEGER NOT NULL,
42695
+ envelope_json TEXT NOT NULL
42696
+ ) STRICT;
42697
+ CREATE INDEX IF NOT EXISTS relay_session_event_outbox_order
42698
+ ON relay_session_event_outbox(source_seq);
42699
+ CREATE TABLE IF NOT EXISTS relay_session_event_outbox_state (
42700
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
42701
+ stream_id TEXT NOT NULL,
42702
+ next_source_seq INTEGER NOT NULL,
42703
+ total_bytes INTEGER NOT NULL
42704
+ ) STRICT;
42705
+ CREATE TABLE IF NOT EXISTS relay_session_event_dead_letters (
42706
+ ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
42707
+ event_id TEXT NOT NULL UNIQUE,
42708
+ session_id TEXT NOT NULL,
42709
+ rejection_code TEXT NOT NULL,
42710
+ envelope_json TEXT NOT NULL
42711
+ ) STRICT;
42712
+ `);
42713
+ this.#database.prepare(
42714
+ `INSERT OR IGNORE INTO relay_session_event_outbox_state (
42715
+ singleton, stream_id, next_source_seq, total_bytes
42716
+ ) VALUES (1, ?, 1, 0)`
42717
+ ).run(randomUUID6());
42718
+ }
42719
+ acknowledge = (acknowledgement) => this.#remove(acknowledgement.eventId, acknowledgement.sessionId);
42720
+ close = () => {
42721
+ this.#database.close();
42722
+ };
42723
+ enqueue = (sessionId, payload, critical) => {
42724
+ const envelope2 = this.enqueueBatch(sessionId, [payload], critical)[0];
42725
+ if (envelope2 === void 0) {
42726
+ throw new Error("The Session event outbox did not enqueue the event");
42727
+ }
42728
+ return envelope2;
42729
+ };
42730
+ enqueueBatch = (sessionId, payloads, critical) => {
42731
+ if (payloads.length < 1 || payloads.length > 32) {
42732
+ throw new Error("A Session event batch must contain between 1 and 32 events");
42733
+ }
42734
+ this.#database.exec("BEGIN IMMEDIATE");
42735
+ try {
42736
+ const state = this.#database.prepare(
42737
+ `SELECT stream_id, next_source_seq, total_bytes
42738
+ FROM relay_session_event_outbox_state
42739
+ WHERE singleton = 1`
42740
+ ).get();
42741
+ const sourceSeq = rowNumber(state, "next_source_seq");
42742
+ if (sourceSeq < 1 || !Number.isSafeInteger(sourceSeq + payloads.length)) {
42743
+ throw new Error("The Session event source sequence is exhausted");
42744
+ }
42745
+ const streamId = rowString(state, "stream_id");
42746
+ const stored = payloads.map((payload, index) => {
42747
+ const envelope2 = {
42748
+ delivery: {
42749
+ eventId: randomUUID6(),
42750
+ kind: "reliable",
42751
+ sourceSeq: (sourceSeq + index).toString(),
42752
+ streamId
42753
+ },
42754
+ kind: SESSION_EVENT_KIND,
42755
+ payload,
42756
+ sessionId
42757
+ };
42758
+ const encoded = encodeSessionEventEnvelope(envelope2);
42759
+ return { byteLength: Buffer.byteLength(encoded, "utf8"), encoded, envelope: envelope2 };
42760
+ });
42761
+ const byteLength = stored.reduce((total, entry) => total + entry.byteLength, 0);
42762
+ const maximumBytes = critical ? this.#limits.criticalEventBytes : this.#limits.standardEventBytes;
42763
+ if (rowNumber(state, "total_bytes") + byteLength > maximumBytes) {
42764
+ throw new SessionEventOutboxCapacityError();
42765
+ }
42766
+ const insert = this.#database.prepare(
42767
+ `INSERT INTO relay_session_event_outbox (
42768
+ event_id, session_id, source_seq, byte_length, envelope_json
42769
+ ) VALUES (?, ?, ?, ?, ?)`
42770
+ );
42771
+ for (const entry of stored) {
42772
+ insert.run(
42773
+ entry.envelope.delivery.eventId,
42774
+ sessionId,
42775
+ Number(entry.envelope.delivery.sourceSeq),
42776
+ entry.byteLength,
42777
+ entry.encoded
42778
+ );
42779
+ }
42780
+ this.#database.prepare(
42781
+ `UPDATE relay_session_event_outbox_state
42782
+ SET next_source_seq = next_source_seq + ?,
42783
+ total_bytes = total_bytes + ?
42784
+ WHERE singleton = 1`
42785
+ ).run(stored.length, byteLength);
42786
+ this.#database.exec("COMMIT");
42787
+ return stored.map((entry) => entry.envelope);
42788
+ } catch (error62) {
42789
+ this.#database.exec("ROLLBACK");
42790
+ throw error62;
42791
+ }
42792
+ };
42793
+ pending = (limit, excludedEventIds) => {
42794
+ if (!Number.isSafeInteger(limit) || limit < 1) {
42795
+ throw new Error("Session event replay limit must be a positive integer");
42796
+ }
42797
+ const exclusion = excludedEventIds.length === 0 ? "" : `WHERE event_id NOT IN (${excludedEventIds.map(() => "?").join(", ")})`;
42798
+ return this.#database.prepare(
42799
+ `SELECT envelope_json
42800
+ FROM relay_session_event_outbox
42801
+ ${exclusion}
42802
+ ORDER BY source_seq
42803
+ LIMIT ?`
42804
+ ).all(...excludedEventIds, limit).map((row) => {
42805
+ const envelope2 = decodeSessionEventEnvelope(rowString(row, "envelope_json"));
42806
+ if (!("delivery" in envelope2)) {
42807
+ throw new Error("SQLite returned an invalid reliable Session event envelope");
42808
+ }
42809
+ return envelope2;
42810
+ });
42811
+ };
42812
+ quarantine = (rejection) => {
42813
+ this.#database.exec("BEGIN IMMEDIATE");
42814
+ try {
42815
+ const stored = this.#database.prepare(
42816
+ `SELECT byte_length, envelope_json
42817
+ FROM relay_session_event_outbox
42818
+ WHERE event_id = ? AND session_id = ?`
42819
+ ).get(rejection.eventId, rejection.sessionId);
42820
+ if (stored === void 0) {
42821
+ this.#database.exec("COMMIT");
42822
+ return false;
42823
+ }
42824
+ this.#database.prepare(
42825
+ `INSERT INTO relay_session_event_dead_letters (
42826
+ event_id, session_id, rejection_code, envelope_json
42827
+ ) VALUES (?, ?, ?, ?)`
42828
+ ).run(rejection.eventId, rejection.sessionId, rejection.code, rowString(stored, "envelope_json"));
42829
+ this.#deleteStored(rejection.eventId, rejection.sessionId, rowNumber(stored, "byte_length"));
42830
+ this.#database.prepare(
42831
+ `DELETE FROM relay_session_event_dead_letters
42832
+ WHERE ordinal NOT IN (
42833
+ SELECT ordinal FROM relay_session_event_dead_letters
42834
+ ORDER BY ordinal DESC LIMIT ?
42835
+ )`
42836
+ ).run(MAX_DEAD_LETTERS);
42837
+ this.#database.exec("COMMIT");
42838
+ return true;
42839
+ } catch (error62) {
42840
+ this.#database.exec("ROLLBACK");
42841
+ throw error62;
42842
+ }
42843
+ };
42844
+ #deleteStored = (eventId, sessionId, byteLength) => {
42845
+ const deletion = this.#database.prepare(
42846
+ `DELETE FROM relay_session_event_outbox
42847
+ WHERE event_id = ? AND session_id = ?`
42848
+ ).run(eventId, sessionId);
42849
+ if (deletion.changes !== 1) {
42850
+ throw new Error("The Session event could not be removed from the outbox");
42851
+ }
42852
+ const state = this.#database.prepare(
42853
+ `UPDATE relay_session_event_outbox_state
42854
+ SET total_bytes = total_bytes - ?
42855
+ WHERE singleton = 1 AND total_bytes >= ?`
42856
+ ).run(byteLength, byteLength);
42857
+ if (state.changes !== 1) {
42858
+ throw new Error("The Session event outbox capacity state is inconsistent");
42859
+ }
42860
+ };
42861
+ #remove = (eventId, sessionId) => {
42862
+ this.#database.exec("BEGIN IMMEDIATE");
42863
+ try {
42864
+ const stored = this.#database.prepare(
42865
+ `SELECT byte_length
42866
+ FROM relay_session_event_outbox
42867
+ WHERE event_id = ? AND session_id = ?`
42868
+ ).get(eventId, sessionId);
42869
+ if (stored === void 0) {
42870
+ this.#database.exec("COMMIT");
42871
+ return false;
42872
+ }
42873
+ this.#deleteStored(eventId, sessionId, rowNumber(stored, "byte_length"));
42874
+ this.#database.exec("COMMIT");
42875
+ return true;
42876
+ } catch (error62) {
42877
+ this.#database.exec("ROLLBACK");
42878
+ throw error62;
42879
+ }
42880
+ };
42881
+ };
42882
+
42394
42883
  // src/relay-connection/session-event-publisher.ts
42395
42884
  import { WebSocket as WebSocket9 } from "undici";
42396
- var MAX_LIVE_EVENT_BUFFERED_BYTES = 16 * 1024 * 1024;
42885
+ var SESSION_EVENT_REPLAY_WINDOW = 16;
42886
+ var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
42887
+ function reservesSessionEventCapacity(event) {
42888
+ if (event.kind === SESSION_COMPLETED_KIND || event.kind === SESSION_FAILED_KIND || event.kind === SESSION_FORKED_KIND || event.kind === SESSION_READY_KIND) {
42889
+ return true;
42890
+ }
42891
+ return event.kind === AGENT_ACTIVITY_KIND && (event.activityType === "progress" || event.activityType === "reasoning") && (event.status === "completed" || event.status === "failed");
42892
+ }
42397
42893
  var DaemonSessionEventPublisher = class {
42398
- #socket = null;
42399
- #reportedDrop = false;
42400
- connect = (socket) => {
42401
- this.#socket = socket;
42402
- this.#reportedDrop = false;
42894
+ #inFlight = /* @__PURE__ */ new Map();
42895
+ #outbox;
42896
+ #reportedCapacityKinds = /* @__PURE__ */ new Set();
42897
+ #timer;
42898
+ #connection = { kind: "disconnected" };
42899
+ #deferredReplay = null;
42900
+ #inFlightBytes = 0;
42901
+ constructor(outbox, timer) {
42902
+ this.#outbox = outbox;
42903
+ this.#timer = timer;
42904
+ }
42905
+ acknowledge = (acknowledgement) => {
42906
+ if (!this.#outbox.acknowledge(acknowledgement)) {
42907
+ return;
42908
+ }
42909
+ this.#removeInFlight(acknowledgement.eventId);
42910
+ this.#publishNextBatch();
42911
+ };
42912
+ close = () => {
42913
+ this.#deferredReplay?.cancel();
42914
+ this.#deferredReplay = null;
42915
+ this.#connection = { kind: "disconnected" };
42916
+ this.#clearInFlight();
42917
+ this.#outbox.close();
42918
+ };
42919
+ connect = (socket, delivery) => {
42920
+ this.#connection = { delivery, kind: "connected", socket };
42921
+ this.#clearInFlight();
42922
+ this.#publishNextBatch();
42923
+ };
42924
+ defer = (deferred) => {
42925
+ if (!this.#inFlight.has(deferred.eventId)) {
42926
+ return;
42927
+ }
42928
+ this.#clearInFlight();
42929
+ if (this.#deferredReplay !== null) {
42930
+ return;
42931
+ }
42932
+ this.#deferredReplay = this.#timer.schedule(() => {
42933
+ this.#deferredReplay = null;
42934
+ this.#publishNextBatch();
42935
+ }, deferred.retryAfterMilliseconds);
42403
42936
  };
42404
42937
  disconnect = (socket) => {
42405
- if (this.#socket === socket) {
42406
- this.#socket = null;
42938
+ if (this.#connection.kind !== "connected" || this.#connection.socket !== socket) {
42939
+ return;
42407
42940
  }
42941
+ this.#connection = { kind: "disconnected" };
42942
+ this.#clearInFlight();
42408
42943
  };
42409
42944
  publish = (sessionId, event) => {
42410
- const socket = this.#socket;
42411
- if (socket === null || socket.readyState !== WebSocket9.OPEN) {
42412
- this.#reportDrop();
42945
+ try {
42946
+ this.#outbox.enqueue(sessionId, encodeRecordedSessionEvent(event), reservesSessionEventCapacity(event));
42947
+ } catch (error62) {
42948
+ if (!(error62 instanceof SessionEventOutboxCapacityError)) {
42949
+ throw error62;
42950
+ }
42951
+ if (!this.#reportedCapacityKinds.has(event.kind)) {
42952
+ this.#reportedCapacityKinds.add(event.kind);
42953
+ console.warn(
42954
+ `Mobius daemon could not persist "${event.kind}" because the Session event outbox is full`
42955
+ );
42956
+ }
42413
42957
  return;
42414
42958
  }
42415
- if (socket.bufferedAmount >= MAX_LIVE_EVENT_BUFFERED_BYTES) {
42416
- this.#socket = null;
42417
- socket.close(1013, "Live event backpressure");
42418
- this.#reportDrop();
42959
+ this.#publishNextBatch();
42960
+ };
42961
+ publishBatch = (sessionId, events) => {
42962
+ const critical = events.some(reservesSessionEventCapacity);
42963
+ this.#outbox.enqueueBatch(sessionId, events.map(encodeRecordedSessionEvent), critical);
42964
+ this.#publishNextBatch();
42965
+ };
42966
+ quarantine = (rejection) => {
42967
+ if (!this.#outbox.quarantine(rejection)) {
42419
42968
  return;
42420
42969
  }
42421
- try {
42422
- socket.send(
42423
- encodeDaemonToServiceMessage({
42424
- kind: SESSION_EVENT_KIND,
42425
- payload: encodeRecordedSessionEvent(event),
42426
- sessionId
42427
- })
42428
- );
42429
- } catch {
42430
- this.#reportDrop();
42431
- }
42970
+ this.#removeInFlight(rejection.eventId);
42971
+ console.error(`Mobius Relay rejected Session event ${rejection.eventId} with code ${rejection.code}`);
42972
+ this.#publishNextBatch();
42432
42973
  };
42433
- close = () => {
42434
- this.#socket = null;
42974
+ #clearInFlight = () => {
42975
+ this.#inFlight.clear();
42976
+ this.#inFlightBytes = 0;
42977
+ };
42978
+ #publishNextBatch = () => {
42979
+ if (this.#deferredReplay !== null || this.#connection.kind !== "connected") {
42980
+ return;
42981
+ }
42982
+ while (this.#inFlight.size < SESSION_EVENT_REPLAY_WINDOW) {
42983
+ const batch = this.#outbox.pending(SESSION_EVENT_REPLAY_WINDOW - this.#inFlight.size, [
42984
+ ...this.#inFlight.keys()
42985
+ ]);
42986
+ let sent = 0;
42987
+ for (const envelope2 of batch) {
42988
+ const encoded = encodeDaemonToServiceMessage(
42989
+ this.#connection.delivery === "reliable" ? envelope2 : {
42990
+ kind: envelope2.kind,
42991
+ payload: envelope2.payload,
42992
+ sessionId: envelope2.sessionId
42993
+ }
42994
+ );
42995
+ const byteLength = Buffer.byteLength(encoded, "utf8");
42996
+ if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
42997
+ return;
42998
+ }
42999
+ if (!this.#send(encoded)) {
43000
+ this.#clearInFlight();
43001
+ return;
43002
+ }
43003
+ if (this.#connection.delivery === "ephemeral") {
43004
+ this.#outbox.acknowledge({
43005
+ eventId: envelope2.delivery.eventId,
43006
+ kind: "session.event.ack",
43007
+ outcome: { kind: "relayed" },
43008
+ sessionId: envelope2.sessionId
43009
+ });
43010
+ sent += 1;
43011
+ continue;
43012
+ }
43013
+ this.#inFlight.set(envelope2.delivery.eventId, { byteLength });
43014
+ this.#inFlightBytes += byteLength;
43015
+ sent += 1;
43016
+ }
43017
+ if (sent === 0) {
43018
+ return;
43019
+ }
43020
+ }
42435
43021
  };
42436
- #reportDrop = () => {
42437
- if (this.#reportedDrop) {
43022
+ #removeInFlight = (eventId) => {
43023
+ const inFlight = this.#inFlight.get(eventId);
43024
+ if (inFlight === void 0) {
42438
43025
  return;
42439
43026
  }
42440
- this.#reportedDrop = true;
42441
- console.warn("Mobius daemon dropped live Session events while the Relay was unavailable");
43027
+ this.#inFlight.delete(eventId);
43028
+ this.#inFlightBytes -= inFlight.byteLength;
43029
+ };
43030
+ #send = (encoded) => {
43031
+ if (this.#connection.kind !== "connected" || this.#connection.socket.readyState !== WebSocket9.OPEN) {
43032
+ return false;
43033
+ }
43034
+ try {
43035
+ this.#connection.socket.send(encoded);
43036
+ return true;
43037
+ } catch {
43038
+ return false;
43039
+ }
42442
43040
  };
42443
43041
  };
42444
43042
 
@@ -42499,7 +43097,7 @@ async function installDetectedAgentIntegrations(config2, installations, signal)
42499
43097
  {
42500
43098
  agentId: agent.id,
42501
43099
  kind: AGENT_INSTALL_REQUEST_KIND,
42502
- requestId: `automatic-integration-${randomUUID6()}`
43100
+ requestId: `automatic-integration-${randomUUID7()}`
42503
43101
  },
42504
43102
  signal
42505
43103
  );
@@ -42521,7 +43119,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
42521
43119
  join17(stateDirectory, "sessions", `${createHash5("sha256").update(config2.channelId).digest("hex")}.sqlite`)
42522
43120
  );
42523
43121
  const operations = new DaemonOperationState();
42524
- const publisher = new DaemonSessionEventPublisher();
43122
+ const channelHash = createHash5("sha256").update(config2.channelId).digest("hex");
43123
+ const publisher = new DaemonSessionEventPublisher(
43124
+ new SessionEventOutbox(join17(stateDirectory, "events", `${channelHash}.sqlite`)),
43125
+ timer
43126
+ );
42525
43127
  const activeTurns = new DaemonTurnRegistry(operations);
42526
43128
  const recordSessionEvent = (sessionId, event) => {
42527
43129
  publisher.publish(sessionId, activeTurns.recordEvent(event));
@@ -42709,13 +43311,13 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
42709
43311
  // src/pairing/pair-local-machine.ts
42710
43312
  import { mkdir as mkdir5, realpath as realpath9, stat as stat8 } from "node:fs/promises";
42711
43313
  import { hostname as hostname3 } from "node:os";
42712
- import { basename as basename6, resolve as resolve7 } from "node:path";
43314
+ import { basename as basename7, resolve as resolve7 } from "node:path";
42713
43315
  import { fetch as fetch4 } from "undici";
42714
43316
 
42715
43317
  // src/storage/runtime-installation-store.ts
42716
- import { createHash as createHash6, randomUUID as randomUUID7 } from "node:crypto";
43318
+ import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
42717
43319
  import { mkdir as mkdir4, open as open5, readdir as readdir3, rename as rename3, unlink, writeFile as writeFile3 } from "node:fs/promises";
42718
- import { dirname as dirname5, join as join18, resolve as resolve6 } from "node:path";
43320
+ import { dirname as dirname6, join as join18, resolve as resolve6 } from "node:path";
42719
43321
  var APPLICATION_ID_PATTERN = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+){1,31}$/;
42720
43322
  var CONNECTION_ID_PATTERN = /^mcn_[a-f0-9]{32}$/;
42721
43323
  var INSTALLATION_ID_PATTERN = /^min_[a-f0-9]{32}$/;
@@ -42843,10 +43445,10 @@ function parseRelayOrigin(value) {
42843
43445
  return origin;
42844
43446
  }
42845
43447
  function connectionId() {
42846
- return `mcn_${randomUUID7().replaceAll("-", "")}`;
43448
+ return `mcn_${randomUUID8().replaceAll("-", "")}`;
42847
43449
  }
42848
43450
  function installationId() {
42849
- return `min_${randomUUID7().replaceAll("-", "")}`;
43451
+ return `min_${randomUUID8().replaceAll("-", "")}`;
42850
43452
  }
42851
43453
  function channelIndexFileName(channelId) {
42852
43454
  return `mci_${createHash6("sha256").update(channelId).digest("hex")}.json`;
@@ -42957,8 +43559,8 @@ async function readJsonFile(path2, signal, missingCode) {
42957
43559
  }
42958
43560
  async function writeJsonAtomically(path2, value, signal) {
42959
43561
  signal.throwIfAborted();
42960
- const directory = dirname5(path2);
42961
- const temporaryPath = join18(directory, `.${randomUUID7()}.tmp`);
43562
+ const directory = dirname6(path2);
43563
+ const temporaryPath = join18(directory, `.${randomUUID8()}.tmp`);
42962
43564
  try {
42963
43565
  await mkdir4(directory, { mode: 448, recursive: true });
42964
43566
  signal.throwIfAborted();
@@ -43351,7 +43953,7 @@ async function claimPairing(options, defaultWorkspace, localAgents, installableA
43351
43953
  installableAgents,
43352
43954
  machineName: hostname3(),
43353
43955
  pairingCode: options.code,
43354
- workspaceName: basename6(defaultWorkspace) || "Local workspace"
43956
+ workspaceName: basename7(defaultWorkspace) || "Local workspace"
43355
43957
  }),
43356
43958
  dispatcher: environmentProxyDispatcher,
43357
43959
  headers: { "content-type": "application/json" },