@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/index.js CHANGED
@@ -20766,9 +20766,17 @@ function decodeSessionQueuedEvent(encoded) {
20766
20766
  }
20767
20767
  function decodeRuntimeLivenessReadyEvent(encoded) {
20768
20768
  const value = parseJson2(encoded);
20769
- assertExactKeys(value, ["kind"]);
20770
20769
  assertKind(value, RUNTIME_LIVENESS_READY_KIND);
20771
- return { kind: RUNTIME_LIVENESS_READY_KIND };
20770
+ if (value["sessionEventDelivery"] === void 0) {
20771
+ assertExactKeys(value, ["kind"]);
20772
+ return { kind: RUNTIME_LIVENESS_READY_KIND };
20773
+ }
20774
+ assertExactKeys(value, ["kind", "sessionEventDelivery"]);
20775
+ const sessionEventDelivery = readString(value, "sessionEventDelivery", 32);
20776
+ if (sessionEventDelivery !== "reliable-v1") {
20777
+ throw new ProtocolMessageError("Unsupported Session event delivery mode");
20778
+ }
20779
+ return { kind: RUNTIME_LIVENESS_READY_KIND, sessionEventDelivery };
20772
20780
  }
20773
20781
  function decodeRuntimeHeartbeatEvent(encoded) {
20774
20782
  const value = parseJson2(encoded);
@@ -21100,6 +21108,12 @@ var nativeSessionRequestSchema = external_exports.discriminatedUnion("action", [
21100
21108
  entryId: identifierStringSchema,
21101
21109
  targetSessionId: identifierStringSchema
21102
21110
  }).readonly(),
21111
+ external_exports.strictObject({
21112
+ ...identity,
21113
+ action: external_exports.literal("fork-recorded"),
21114
+ targetSessionId: identifierStringSchema,
21115
+ throughCommandId: identifierStringSchema
21116
+ }).readonly(),
21103
21117
  external_exports.strictObject({
21104
21118
  ...identity,
21105
21119
  action: external_exports.literal("delete"),
@@ -21123,6 +21137,12 @@ var nativeSessionCommandSchema = external_exports.discriminatedUnion("action", [
21123
21137
  entryId: identifierStringSchema,
21124
21138
  targetSessionId: identifierStringSchema
21125
21139
  }).readonly(),
21140
+ external_exports.strictObject({
21141
+ ...identity,
21142
+ action: external_exports.literal("fork-recorded"),
21143
+ targetSessionId: identifierStringSchema,
21144
+ throughCommandId: identifierStringSchema
21145
+ }).readonly(),
21126
21146
  external_exports.strictObject({
21127
21147
  ...identity,
21128
21148
  action: external_exports.literal("delete"),
@@ -21172,7 +21192,7 @@ var nativeSessionResultSchema = external_exports.discriminatedUnion("action", [
21172
21192
  }).readonly(),
21173
21193
  external_exports.strictObject({
21174
21194
  ...resultEnvelope,
21175
- action: external_exports.enum(["import", "fork"]),
21195
+ action: external_exports.enum(["import", "fork", "fork-recorded"]),
21176
21196
  targetSessionId: identifierStringSchema,
21177
21197
  ready: sessionReadyEventSchema,
21178
21198
  title: external_exports.string().max(500)
@@ -21218,14 +21238,68 @@ var encodeNativeSessionCancel = (value) => JSON.stringify(nativeSessionCancelSch
21218
21238
 
21219
21239
  // ../protocol/src/session-events.ts
21220
21240
  var SESSION_EVENT_KIND = "session.event";
21241
+ var SESSION_EVENT_ACK_KIND = "session.event.ack";
21242
+ var SESSION_EVENT_DEFERRED_KIND = "session.event.deferred";
21243
+ var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
21244
+ var SESSION_FORKED_KIND = "session.forked";
21245
+ var SessionEventRejectionCode = {
21246
+ IdempotencyConflict: "idempotency_conflict",
21247
+ InvalidEvent: "invalid_event",
21248
+ RecordingRejected: "recording_rejected"
21249
+ };
21221
21250
  var MAX_RECORDED_EVENT_LENGTH = 8 * 1024 * 1024;
21222
- var eventEnvelopeSchema = external_exports.strictObject({
21251
+ var ephemeralEventEnvelopeSchema = external_exports.strictObject({
21223
21252
  kind: external_exports.literal(SESSION_EVENT_KIND),
21224
21253
  payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
21225
21254
  sessionId: identifierStringSchema
21226
21255
  }).readonly();
21256
+ var reliableDeliverySchema = external_exports.strictObject({
21257
+ eventId: identifierStringSchema,
21258
+ kind: external_exports.literal("reliable"),
21259
+ sourceSeq: external_exports.string().regex(/^(?:0|[1-9][0-9]*)$/u).max(40),
21260
+ streamId: identifierStringSchema
21261
+ }).readonly();
21262
+ var reliableSessionEventEnvelopeSchema = external_exports.strictObject({
21263
+ delivery: reliableDeliverySchema,
21264
+ kind: external_exports.literal(SESSION_EVENT_KIND),
21265
+ payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
21266
+ sessionId: identifierStringSchema
21267
+ }).readonly();
21268
+ var eventEnvelopeSchema = external_exports.union([ephemeralEventEnvelopeSchema, reliableSessionEventEnvelopeSchema]).readonly();
21269
+ var acknowledgementOutcomeSchema = external_exports.discriminatedUnion("kind", [
21270
+ external_exports.strictObject({ kind: external_exports.literal("relayed") }).readonly(),
21271
+ external_exports.strictObject({
21272
+ kind: external_exports.literal("recorded"),
21273
+ providerId: identifierStringSchema,
21274
+ recordingEpoch: identifierStringSchema
21275
+ }).readonly()
21276
+ ]);
21277
+ var eventAcknowledgementSchema = external_exports.strictObject({
21278
+ eventId: identifierStringSchema,
21279
+ kind: external_exports.literal(SESSION_EVENT_ACK_KIND),
21280
+ outcome: acknowledgementOutcomeSchema,
21281
+ sessionId: identifierStringSchema
21282
+ }).readonly();
21283
+ var eventDeferredSchema = external_exports.strictObject({
21284
+ eventId: identifierStringSchema,
21285
+ kind: external_exports.literal(SESSION_EVENT_DEFERRED_KIND),
21286
+ retryAfterMilliseconds: external_exports.number().int().min(100).max(6e4),
21287
+ sessionId: identifierStringSchema
21288
+ }).readonly();
21289
+ var eventRejectedSchema = external_exports.strictObject({
21290
+ code: external_exports.enum(SessionEventRejectionCode),
21291
+ eventId: identifierStringSchema,
21292
+ kind: external_exports.literal(SESSION_EVENT_REJECTED_KIND),
21293
+ sessionId: identifierStringSchema
21294
+ }).readonly();
21227
21295
  var SESSION_QUEUE_CHANGED_KIND = "session.queue.changed";
21228
21296
  var sessionQueueChangedSchema = external_exports.strictObject({ kind: external_exports.literal(SESSION_QUEUE_CHANGED_KIND), sessionId: identifierStringSchema }).readonly();
21297
+ var sessionForkedSchema = external_exports.strictObject({
21298
+ kind: external_exports.literal(SESSION_FORKED_KIND),
21299
+ sessionId: identifierStringSchema,
21300
+ sourceSessionId: identifierStringSchema,
21301
+ throughCommandId: identifierStringSchema
21302
+ }).readonly();
21229
21303
  function decode5(encoded, schema) {
21230
21304
  let value;
21231
21305
  try {
@@ -21252,11 +21326,19 @@ var encode4 = (message, schema) => {
21252
21326
  };
21253
21327
  var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
21254
21328
  var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
21329
+ var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
21330
+ var encodeSessionEventAcknowledgement = (message) => encode4(message, eventAcknowledgementSchema);
21331
+ var decodeSessionEventDeferred = (encoded) => decode5(encoded, eventDeferredSchema);
21332
+ var encodeSessionEventDeferred = (message) => encode4(message, eventDeferredSchema);
21333
+ var decodeSessionEventRejected = (encoded) => decode5(encoded, eventRejectedSchema);
21334
+ var encodeSessionEventRejected = (message) => encode4(message, eventRejectedSchema);
21255
21335
  function decodeRecordedSessionEvent(encoded) {
21256
21336
  const kind = decodeMessageKind(encoded);
21257
21337
  switch (kind) {
21258
21338
  case SESSION_QUEUE_CHANGED_KIND:
21259
21339
  return decode5(encoded, sessionQueueChangedSchema);
21340
+ case SESSION_FORKED_KIND:
21341
+ return decode5(encoded, sessionForkedSchema);
21260
21342
  case AGENT_ACTIVITY_KIND:
21261
21343
  return decodeAgentActivityEvent(encoded);
21262
21344
  case AGENT_ACTIVITY_NARRATION_KIND:
@@ -21423,6 +21505,21 @@ var serviceToDaemonSignatures = {
21423
21505
  encodeSessionElicitationRespondCommand,
21424
21506
  decodeSessionElicitationRespondCommand
21425
21507
  ),
21508
+ [SESSION_EVENT_ACK_KIND]: defineMessageSignature(
21509
+ SESSION_EVENT_ACK_KIND,
21510
+ encodeSessionEventAcknowledgement,
21511
+ decodeSessionEventAcknowledgement
21512
+ ),
21513
+ [SESSION_EVENT_DEFERRED_KIND]: defineMessageSignature(
21514
+ SESSION_EVENT_DEFERRED_KIND,
21515
+ encodeSessionEventDeferred,
21516
+ decodeSessionEventDeferred
21517
+ ),
21518
+ [SESSION_EVENT_REJECTED_KIND]: defineMessageSignature(
21519
+ SESSION_EVENT_REJECTED_KIND,
21520
+ encodeSessionEventRejected,
21521
+ decodeSessionEventRejected
21522
+ ),
21426
21523
  [SESSION_LIVE_HISTORY_LOAD_KIND]: defineMessageSignature(
21427
21524
  SESSION_LIVE_HISTORY_LOAD_KIND,
21428
21525
  encodeSessionLiveHistoryLoad,
@@ -24021,13 +24118,15 @@ var AcpPreparedSession = class {
24021
24118
  capabilities;
24022
24119
  commands;
24023
24120
  configOptions;
24121
+ prompted;
24024
24122
  restored;
24025
- constructor(agentName, agentVersion, capabilities, commands, configOptions, restored) {
24123
+ constructor(agentName, agentVersion, capabilities, commands, configOptions, prompted, restored) {
24026
24124
  this.agentName = agentName;
24027
24125
  this.agentVersion = agentVersion;
24028
24126
  this.capabilities = capabilities;
24029
24127
  this.commands = commands;
24030
24128
  this.configOptions = configOptions;
24129
+ this.prompted = prompted;
24031
24130
  this.restored = restored;
24032
24131
  }
24033
24132
  };
@@ -30254,6 +30353,10 @@ var acpSessionStoreMigrations = [
30254
30353
  {
30255
30354
  "name": "0000_slimy_captain_midlands.sql",
30256
30355
  "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"
30356
+ },
30357
+ {
30358
+ "name": "0001_regular_ultimates.sql",
30359
+ "sql": "ALTER TABLE `acp_session` ADD `last_terminal_command_id` text;"
30257
30360
  }
30258
30361
  ];
30259
30362
 
@@ -30271,6 +30374,7 @@ var acpSession = sqliteTable("acp_session", {
30271
30374
  nativeSessionId: text("native_session_id").notNull(),
30272
30375
  cwd: text("cwd").notNull(),
30273
30376
  additionalDirectories: text("additional_directories").notNull(),
30377
+ lastTerminalCommandId: text("last_terminal_command_id"),
30274
30378
  prompted: integer2("prompted", { mode: "boolean" }).notNull(),
30275
30379
  title: text("title").notNull(),
30276
30380
  updatedAt: text("updated_at").notNull()
@@ -30286,15 +30390,17 @@ var AcpStoredSession = class {
30286
30390
  additionalDirectories;
30287
30391
  agentId;
30288
30392
  cwd;
30393
+ lastTerminalCommandId;
30289
30394
  nativeSessionId;
30290
30395
  prompted;
30291
30396
  sessionId;
30292
30397
  title;
30293
30398
  updatedAt;
30294
- constructor(sessionId, agentId, nativeSessionId, cwd, additionalDirectories, prompted, title2, updatedAt = (/* @__PURE__ */ new Date()).toISOString()) {
30399
+ constructor(sessionId, agentId, nativeSessionId, cwd, additionalDirectories, prompted, title2, updatedAt = (/* @__PURE__ */ new Date()).toISOString(), lastTerminalCommandId = null) {
30295
30400
  this.additionalDirectories = [...additionalDirectories];
30296
30401
  this.agentId = agentId;
30297
30402
  this.cwd = cwd;
30403
+ this.lastTerminalCommandId = lastTerminalCommandId;
30298
30404
  this.nativeSessionId = nativeSessionId;
30299
30405
  this.prompted = prompted;
30300
30406
  this.sessionId = sessionId;
@@ -30355,6 +30461,7 @@ var storedSessionRowSchema = external_exports.object({
30355
30461
  additionalDirectories: external_exports.string(),
30356
30462
  agentId: external_exports.string(),
30357
30463
  cwd: external_exports.string(),
30464
+ lastTerminalCommandId: external_exports.string().nullable(),
30358
30465
  nativeSessionId: external_exports.string(),
30359
30466
  prompted: external_exports.boolean(),
30360
30467
  sessionId: external_exports.string(),
@@ -30374,7 +30481,8 @@ function decodeStoredSession(value) {
30374
30481
  additionalDirectories,
30375
30482
  row.prompted,
30376
30483
  row.title,
30377
- row.updatedAt
30484
+ row.updatedAt,
30485
+ row.lastTerminalCommandId
30378
30486
  );
30379
30487
  }
30380
30488
  var SqliteAcpSessionStore = class {
@@ -30441,6 +30549,7 @@ var SqliteAcpSessionStore = class {
30441
30549
  additionalDirectories: JSON.stringify(session.additionalDirectories),
30442
30550
  agentId: session.agentId,
30443
30551
  cwd: session.cwd,
30552
+ lastTerminalCommandId: session.lastTerminalCommandId,
30444
30553
  nativeSessionId: session.nativeSessionId,
30445
30554
  prompted: session.prompted,
30446
30555
  sessionId: session.sessionId,
@@ -30493,6 +30602,7 @@ var AgentSession = class _AgentSession {
30493
30602
  #commands = [];
30494
30603
  #configRevision = 0;
30495
30604
  #configOptions = [];
30605
+ #lastTerminalCommandId = null;
30496
30606
  #prompted = false;
30497
30607
  #nativePrompt = null;
30498
30608
  #sessionLoad = { kind: "not-requested" };
@@ -30596,6 +30706,7 @@ var AgentSession = class _AgentSession {
30596
30706
  this.#capabilities,
30597
30707
  this.#commands,
30598
30708
  this.#configOptions,
30709
+ this.#prompted,
30599
30710
  this.#restored
30600
30711
  );
30601
30712
  loadHistory = async (signal) => {
@@ -30829,6 +30940,7 @@ var AgentSession = class _AgentSession {
30829
30940
  } else if (stored instanceof FoundAcpStoredSession && stored.session.agentId === this.#agentId && stored.session.cwd === this.#cwd && arraysEqual(stored.session.additionalDirectories, this.#additionalDirectories)) {
30830
30941
  this.selectNativeSession(stored.session.nativeSessionId);
30831
30942
  this.#prompted = stored.session.prompted;
30943
+ this.#lastTerminalCommandId = stored.session.lastTerminalCommandId;
30832
30944
  this.#sessionTitle = stored.session.title;
30833
30945
  this.#restored = true;
30834
30946
  if (this.#capabilities.resumeSession) {
@@ -30902,7 +31014,9 @@ var AgentSession = class _AgentSession {
30902
31014
  this.#cwd,
30903
31015
  this.#additionalDirectories,
30904
31016
  this.#prompted,
30905
- this.#sessionTitle ?? ""
31017
+ this.#sessionTitle ?? "",
31018
+ (/* @__PURE__ */ new Date()).toISOString(),
31019
+ this.#lastTerminalCommandId
30906
31020
  )
30907
31021
  );
30908
31022
  };
@@ -31102,7 +31216,9 @@ var AgentSession = class _AgentSession {
31102
31216
  this.#cwd,
31103
31217
  this.#additionalDirectories,
31104
31218
  true,
31105
- this.#sessionTitle
31219
+ this.#sessionTitle,
31220
+ (/* @__PURE__ */ new Date()).toISOString(),
31221
+ this.#lastTerminalCommandId
31106
31222
  )
31107
31223
  );
31108
31224
  this.#prompted = true;
@@ -31154,6 +31270,20 @@ var AgentSession = class _AgentSession {
31154
31270
  usage: result.usage ?? null
31155
31271
  });
31156
31272
  }
31273
+ this.#lastTerminalCommandId = options.commandId;
31274
+ this.#store.write(
31275
+ new AcpStoredSession(
31276
+ this.#mobiusSessionId,
31277
+ this.#agentId,
31278
+ this.#updates.sessionId(),
31279
+ this.#cwd,
31280
+ this.#additionalDirectories,
31281
+ true,
31282
+ this.#sessionTitle ?? "",
31283
+ (/* @__PURE__ */ new Date()).toISOString(),
31284
+ this.#lastTerminalCommandId
31285
+ )
31286
+ );
31157
31287
  return result;
31158
31288
  } catch (error62) {
31159
31289
  await running.activities.finish();
@@ -31346,7 +31476,9 @@ var AgentSession = class _AgentSession {
31346
31476
  this.#cwd,
31347
31477
  this.#additionalDirectories,
31348
31478
  this.#prompted,
31349
- this.#sessionTitle ?? ""
31479
+ this.#sessionTitle ?? "",
31480
+ (/* @__PURE__ */ new Date()).toISOString(),
31481
+ this.#lastTerminalCommandId
31350
31482
  )
31351
31483
  );
31352
31484
  }
@@ -32392,9 +32524,17 @@ var AgentRuntimeManager = class {
32392
32524
  const openedKey = `${options2.agent.id}:${runtime.nativeSessionId()}`;
32393
32525
  this.#nativeOperations.add(openedKey);
32394
32526
  try {
32527
+ const prepared = runtime.prepared();
32528
+ if (origin.kind === "fork" && options2.agent.id === "claude") {
32529
+ await this.#retireSessionProcess(
32530
+ options2.sessionId,
32531
+ runtime,
32532
+ async () => await runtime.closeProcess()
32533
+ );
32534
+ }
32395
32535
  return {
32396
32536
  sessionId: options2.sessionId,
32397
- prepared: runtime.prepared()
32537
+ prepared
32398
32538
  };
32399
32539
  } finally {
32400
32540
  this.#nativeOperations.delete(openedKey);
@@ -37515,7 +37655,7 @@ function isRuntimeRelayRevocation(closeCode, closeReason) {
37515
37655
  }
37516
37656
 
37517
37657
  // src/run-daemon.ts
37518
- import { createHash as createHash6, randomUUID as randomUUID7 } from "node:crypto";
37658
+ import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
37519
37659
  import { join as join18 } from "node:path";
37520
37660
  import { styleText } from "node:util";
37521
37661
 
@@ -39861,6 +40001,7 @@ var readCodexFullHistory = async (agent, cwd, nativeSessionId, signal, nativeTur
39861
40001
 
39862
40002
  // src/native-sessions/service.ts
39863
40003
  import { randomUUID as randomUUID3 } from "node:crypto";
40004
+ import { basename as basename5 } from "node:path";
39864
40005
 
39865
40006
  // src/native-sessions/acp-history.ts
39866
40007
  async function readAcpHistory(sessions, directories, bindingId, options, nativeSessionId) {
@@ -40001,6 +40142,64 @@ var NativeSessionService = class {
40001
40142
  sessionId: command.sessionId
40002
40143
  };
40003
40144
  }
40145
+ if (command.action === "fork-recorded") {
40146
+ const source = this.#sessions.findSessions([command.sessionId])[0];
40147
+ if (source === void 0 || !source.prompted) {
40148
+ throw new AcpExecutionError(
40149
+ "The source conversation has no resumable native Agent session",
40150
+ {},
40151
+ "native_session_not_found"
40152
+ );
40153
+ }
40154
+ if (source.agentId !== command.agentId || source.cwd !== cwd || source.additionalDirectories.length !== additionalDirectories.length || source.additionalDirectories.some((path2, index) => path2 !== additionalDirectories[index])) {
40155
+ throw new AcpExecutionError(
40156
+ "The source conversation execution target does not match the fork request",
40157
+ {},
40158
+ "native_session_target_changed"
40159
+ );
40160
+ }
40161
+ if (source.lastTerminalCommandId !== command.throughCommandId) {
40162
+ throw new AcpExecutionError(
40163
+ "The selected Turn is no longer the source conversation head",
40164
+ {},
40165
+ "fork_point_stale"
40166
+ );
40167
+ }
40168
+ const opened2 = await this.#sessions.openNativeSession(
40169
+ { ...options, sessionId: command.targetSessionId },
40170
+ { kind: "fork", nativeSessionId: source.nativeSessionId }
40171
+ );
40172
+ signal.throwIfAborted();
40173
+ const directory = await this.#directories.list(this.#config.bindingId, command.directoryId, signal);
40174
+ const ready2 = {
40175
+ additionalDirectories: additionalDirectories.map((path2, index) => ({
40176
+ directoryId: command.additionalDirectoryIds[index] ?? command.directoryId,
40177
+ name: basename5(path2) || path2
40178
+ })),
40179
+ agentId: command.agentId,
40180
+ agentName: opened2.prepared.agentName,
40181
+ agentVersion: opened2.prepared.agentVersion,
40182
+ capabilities: opened2.prepared.capabilities,
40183
+ commands: opened2.prepared.commands,
40184
+ configOptions: opened2.prepared.configOptions,
40185
+ directoryId: command.directoryId,
40186
+ directoryLabel: directory.directoryLabel,
40187
+ kind: "session.ready",
40188
+ requestId: command.requestId,
40189
+ restored: true,
40190
+ sessionId: opened2.sessionId
40191
+ };
40192
+ this.record(command, "native_recorded_session_forked");
40193
+ return {
40194
+ action: "fork-recorded",
40195
+ kind: "native.session.result",
40196
+ ready: ready2,
40197
+ requestId: command.requestId,
40198
+ sessionId: command.sessionId,
40199
+ targetSessionId: command.targetSessionId,
40200
+ title: source.title || "Untitled session"
40201
+ };
40202
+ }
40004
40203
  const prepared = await this.#sessions.prepareNativeSessionBrowser(options);
40005
40204
  if (!prepared.capabilities.listSessions) {
40006
40205
  throw new AcpExecutionError(
@@ -41322,7 +41521,7 @@ function createAgentSettingsDiscoveryHandler(dependencies) {
41322
41521
  // src/artifacts/local-file-streamer.ts
41323
41522
  import { createReadStream as createReadStream2 } from "node:fs";
41324
41523
  import { realpath as realpath9, stat as stat8 } from "node:fs/promises";
41325
- import { basename as basename5, isAbsolute as isAbsolute10, normalize as normalize4, resolve as resolve7 } from "node:path";
41524
+ import { basename as basename6, isAbsolute as isAbsolute10, normalize as normalize4, resolve as resolve7 } from "node:path";
41326
41525
  import { fileURLToPath as fileURLToPath2 } from "node:url";
41327
41526
  var CHUNK_BYTES = 192 * 1024;
41328
41527
  function requestedPath(value) {
@@ -41373,7 +41572,7 @@ async function streamLocalFile(command, send2, signal, workingDirectory) {
41373
41572
  kind: FILE_BEGIN_KIND,
41374
41573
  mediaType: raster?.mimeType ?? "application/octet-stream",
41375
41574
  modifiedAt: before.mtime.toISOString(),
41376
- name: basename5(target) || "file",
41575
+ name: basename6(target) || "file",
41377
41576
  requestId: command.requestId,
41378
41577
  sessionId: command.sessionId
41379
41578
  });
@@ -41403,7 +41602,7 @@ async function streamLocalFile(command, send2, signal, workingDirectory) {
41403
41602
  }
41404
41603
 
41405
41604
  // src/authorization/resolve-session-directories.ts
41406
- import { basename as basename6 } from "node:path";
41605
+ import { basename as basename7 } from "node:path";
41407
41606
  var ResolvedSessionDirectories = class {
41408
41607
  additionalDirectories;
41409
41608
  additionalDirectoryEntries;
@@ -41428,7 +41627,7 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
41428
41627
  additionalDirectories.push(directory);
41429
41628
  additionalDirectoryEntries.push({
41430
41629
  directoryId: additionalDirectoryId,
41431
- name: basename6(directory)
41630
+ name: basename7(directory)
41432
41631
  });
41433
41632
  }
41434
41633
  return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
@@ -42165,14 +42364,15 @@ async function executePrompt({
42165
42364
  const appliedSettings = command.sessionConfigurations === void 0 ? void 0 : agentForSessionConfiguration(agent, command.sessionConfigurations);
42166
42365
  const executionAgent = appliedSettings?.agent ?? agent;
42167
42366
  const sessionConfigurations = appliedSettings?.sessionConfigurations;
42367
+ const preparation = {
42368
+ additionalDirectories: resolved.additionalDirectories,
42369
+ agent: executionAgent,
42370
+ cwd: resolved.cwd,
42371
+ sessionId: command.sessionId,
42372
+ signal
42373
+ };
42374
+ let prepared = await sessions.prepareExecutionSession(preparation);
42168
42375
  if (command.sessionConfigurations !== void 0) {
42169
- const prepared = await sessions.prepareExecutionSession({
42170
- additionalDirectories: resolved.additionalDirectories,
42171
- agent: executionAgent,
42172
- cwd: resolved.cwd,
42173
- sessionId: command.sessionId,
42174
- signal
42175
- });
42176
42376
  if (availability.disposition === SessionPromptDisposition.Steer) {
42177
42377
  assertAppliedSessionConfiguration(prepared.configOptions, sessionConfigurations ?? []);
42178
42378
  } else {
@@ -42188,6 +42388,7 @@ async function executePrompt({
42188
42388
  command.sessionId,
42189
42389
  signal
42190
42390
  );
42391
+ prepared = await sessions.prepareExecutionSession(preparation);
42191
42392
  }
42192
42393
  }
42193
42394
  audit.record({
@@ -42206,6 +42407,24 @@ async function executePrompt({
42206
42407
  }
42207
42408
  }
42208
42409
  const stream = activateAttempt();
42410
+ if (!prepared.prompted) {
42411
+ const listing = await directories.list(config2.bindingId, command.directoryId, signal);
42412
+ publisher.publish(command.sessionId, {
42413
+ additionalDirectories: resolved.additionalDirectoryEntries,
42414
+ agentId: command.agentId,
42415
+ agentName: prepared.agentName,
42416
+ agentVersion: prepared.agentVersion,
42417
+ capabilities: prepared.capabilities,
42418
+ commands: prepared.commands,
42419
+ configOptions: prepared.configOptions,
42420
+ directoryId: command.directoryId,
42421
+ directoryLabel: listing.directoryLabel,
42422
+ kind: SESSION_READY_KIND,
42423
+ requestId: command.commandId,
42424
+ restored: prepared.restored,
42425
+ sessionId: command.sessionId
42426
+ });
42427
+ }
42209
42428
  publisher.publish(command.sessionId, {
42210
42429
  commandId: command.commandId,
42211
42430
  epoch: stream.epoch,
@@ -42459,7 +42678,7 @@ async function executeSessionConfigure(publisher, socket, command, config2, dire
42459
42678
  // src/native-sessions/handle-native-requests.ts
42460
42679
  import { WebSocket as WebSocket5 } from "undici";
42461
42680
  function createNativeSessionRequestHandler(dependencies) {
42462
- const { audit, diagnostics, nativeSessions, signal, socket, timer } = dependencies;
42681
+ const { audit, diagnostics, nativeSessions, publisher, signal, socket, timer } = dependencies;
42463
42682
  const pending = /* @__PURE__ */ new Map();
42464
42683
  const cancel = (command) => {
42465
42684
  const request = pending.get(command.requestId);
@@ -42511,6 +42730,17 @@ function createNativeSessionRequestHandler(dependencies) {
42511
42730
  }
42512
42731
  operationSignal.throwIfAborted();
42513
42732
  const result = await nativeSessions.execute(command, operationSignal);
42733
+ if (command.action === "fork-recorded" && result.action === "fork-recorded") {
42734
+ publisher.publishBatch(result.ready.sessionId, [
42735
+ result.ready,
42736
+ {
42737
+ kind: "session.forked",
42738
+ sessionId: result.ready.sessionId,
42739
+ sourceSessionId: command.sessionId,
42740
+ throughCommandId: command.throughCommandId
42741
+ }
42742
+ ]);
42743
+ }
42514
42744
  if (socket.readyState === WebSocket5.OPEN) {
42515
42745
  send(socket, result);
42516
42746
  }
@@ -42596,6 +42826,7 @@ function createDaemonCommandHandler({
42596
42826
  audit,
42597
42827
  diagnostics,
42598
42828
  nativeSessions,
42829
+ publisher,
42599
42830
  signal,
42600
42831
  socket,
42601
42832
  timer
@@ -43098,6 +43329,7 @@ async function runRelayConnection({
43098
43329
  }) {
43099
43330
  const relayUrl = new URL(config2.relayUrl);
43100
43331
  relayUrl.searchParams.set("liveness", "1");
43332
+ relayUrl.searchParams.set("sessionEventDelivery", "reliable-v1");
43101
43333
  const socket = await connectRelay(relayUrl, signal);
43102
43334
  diagnostics.connectionOpened(connectionEpoch);
43103
43335
  const connectionController = new AbortController();
@@ -43217,6 +43449,10 @@ async function runRelayConnection({
43217
43449
  const message = decodeServiceToDaemonMessage(encoded);
43218
43450
  const kind = message.kind;
43219
43451
  if (kind === RUNTIME_LIVENESS_READY_KIND) {
43452
+ publisher.connect(
43453
+ socket,
43454
+ message.sessionEventDelivery === "reliable-v1" ? "reliable" : "ephemeral"
43455
+ );
43220
43456
  connectionLiveness.start();
43221
43457
  return;
43222
43458
  }
@@ -43224,6 +43460,18 @@ async function runRelayConnection({
43224
43460
  connectionLiveness.acknowledge(message);
43225
43461
  return;
43226
43462
  }
43463
+ if (kind === SESSION_EVENT_ACK_KIND) {
43464
+ publisher.acknowledge(message);
43465
+ return;
43466
+ }
43467
+ if (kind === SESSION_EVENT_DEFERRED_KIND) {
43468
+ publisher.defer(message);
43469
+ return;
43470
+ }
43471
+ if (kind === SESSION_EVENT_REJECTED_KIND) {
43472
+ publisher.quarantine(message);
43473
+ return;
43474
+ }
43227
43475
  commands.handle(message);
43228
43476
  } catch (error62) {
43229
43477
  console.error("Mobius daemon rejected a message", error62);
@@ -43238,7 +43486,6 @@ async function runRelayConnection({
43238
43486
  socket.addEventListener("close", handleClose);
43239
43487
  socket.addEventListener("error", handleError);
43240
43488
  socket.addEventListener("message", handleMessage);
43241
- publisher.connect(socket);
43242
43489
  if (signal.aborted) {
43243
43490
  stop();
43244
43491
  }
@@ -43249,54 +43496,405 @@ async function runRelayConnection({
43249
43496
  }
43250
43497
  }
43251
43498
 
43499
+ // src/relay-connection/session-event-outbox.ts
43500
+ import { randomUUID as randomUUID7 } from "node:crypto";
43501
+ import { mkdirSync as mkdirSync3 } from "node:fs";
43502
+ import { dirname as dirname6 } from "node:path";
43503
+ import { DatabaseSync } from "node:sqlite";
43504
+ var DEFAULT_STANDARD_EVENT_BYTES = 96 * 1024 * 1024;
43505
+ var DEFAULT_CRITICAL_EVENT_BYTES = 128 * 1024 * 1024;
43506
+ var MAX_DEAD_LETTERS = 1024;
43507
+ var DEFAULT_LIMITS = {
43508
+ criticalEventBytes: DEFAULT_CRITICAL_EVENT_BYTES,
43509
+ standardEventBytes: DEFAULT_STANDARD_EVENT_BYTES
43510
+ };
43511
+ var SessionEventOutboxCapacityError = class extends Error {
43512
+ code = "session_event_outbox_full";
43513
+ constructor() {
43514
+ super("The persistent Session event outbox is full");
43515
+ this.name = "SessionEventOutboxCapacityError";
43516
+ }
43517
+ };
43518
+ function rowNumber(row, field) {
43519
+ const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
43520
+ if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
43521
+ throw new Error(`SQLite returned an invalid ${field} value`);
43522
+ }
43523
+ return value;
43524
+ }
43525
+ function rowString(row, field) {
43526
+ const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
43527
+ if (typeof value !== "string" || value.length === 0) {
43528
+ throw new Error(`SQLite returned an invalid ${field} value`);
43529
+ }
43530
+ return value;
43531
+ }
43532
+ function validateLimits(limits) {
43533
+ if (!Number.isSafeInteger(limits.standardEventBytes) || limits.standardEventBytes < 1 || !Number.isSafeInteger(limits.criticalEventBytes) || limits.criticalEventBytes < limits.standardEventBytes) {
43534
+ throw new Error("Session event outbox limits are invalid");
43535
+ }
43536
+ }
43537
+ var SessionEventOutbox = class {
43538
+ #database;
43539
+ #limits;
43540
+ constructor(path2, limits = DEFAULT_LIMITS) {
43541
+ validateLimits(limits);
43542
+ mkdirSync3(dirname6(path2), { mode: 448, recursive: true });
43543
+ this.#limits = limits;
43544
+ this.#database = new DatabaseSync(path2);
43545
+ this.#database.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL; PRAGMA busy_timeout = 5000;");
43546
+ this.#database.exec(`
43547
+ CREATE TABLE IF NOT EXISTS relay_session_event_outbox (
43548
+ ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
43549
+ event_id TEXT NOT NULL UNIQUE,
43550
+ session_id TEXT NOT NULL,
43551
+ source_seq INTEGER NOT NULL UNIQUE,
43552
+ byte_length INTEGER NOT NULL,
43553
+ envelope_json TEXT NOT NULL
43554
+ ) STRICT;
43555
+ CREATE INDEX IF NOT EXISTS relay_session_event_outbox_order
43556
+ ON relay_session_event_outbox(source_seq);
43557
+ CREATE TABLE IF NOT EXISTS relay_session_event_outbox_state (
43558
+ singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
43559
+ stream_id TEXT NOT NULL,
43560
+ next_source_seq INTEGER NOT NULL,
43561
+ total_bytes INTEGER NOT NULL
43562
+ ) STRICT;
43563
+ CREATE TABLE IF NOT EXISTS relay_session_event_dead_letters (
43564
+ ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
43565
+ event_id TEXT NOT NULL UNIQUE,
43566
+ session_id TEXT NOT NULL,
43567
+ rejection_code TEXT NOT NULL,
43568
+ envelope_json TEXT NOT NULL
43569
+ ) STRICT;
43570
+ `);
43571
+ this.#database.prepare(
43572
+ `INSERT OR IGNORE INTO relay_session_event_outbox_state (
43573
+ singleton, stream_id, next_source_seq, total_bytes
43574
+ ) VALUES (1, ?, 1, 0)`
43575
+ ).run(randomUUID7());
43576
+ }
43577
+ acknowledge = (acknowledgement) => this.#remove(acknowledgement.eventId, acknowledgement.sessionId);
43578
+ close = () => {
43579
+ this.#database.close();
43580
+ };
43581
+ enqueue = (sessionId, payload, critical) => {
43582
+ const envelope2 = this.enqueueBatch(sessionId, [payload], critical)[0];
43583
+ if (envelope2 === void 0) {
43584
+ throw new Error("The Session event outbox did not enqueue the event");
43585
+ }
43586
+ return envelope2;
43587
+ };
43588
+ enqueueBatch = (sessionId, payloads, critical) => {
43589
+ if (payloads.length < 1 || payloads.length > 32) {
43590
+ throw new Error("A Session event batch must contain between 1 and 32 events");
43591
+ }
43592
+ this.#database.exec("BEGIN IMMEDIATE");
43593
+ try {
43594
+ const state = this.#database.prepare(
43595
+ `SELECT stream_id, next_source_seq, total_bytes
43596
+ FROM relay_session_event_outbox_state
43597
+ WHERE singleton = 1`
43598
+ ).get();
43599
+ const sourceSeq = rowNumber(state, "next_source_seq");
43600
+ if (sourceSeq < 1 || !Number.isSafeInteger(sourceSeq + payloads.length)) {
43601
+ throw new Error("The Session event source sequence is exhausted");
43602
+ }
43603
+ const streamId = rowString(state, "stream_id");
43604
+ const stored = payloads.map((payload, index) => {
43605
+ const envelope2 = {
43606
+ delivery: {
43607
+ eventId: randomUUID7(),
43608
+ kind: "reliable",
43609
+ sourceSeq: (sourceSeq + index).toString(),
43610
+ streamId
43611
+ },
43612
+ kind: SESSION_EVENT_KIND,
43613
+ payload,
43614
+ sessionId
43615
+ };
43616
+ const encoded = encodeSessionEventEnvelope(envelope2);
43617
+ return { byteLength: Buffer.byteLength(encoded, "utf8"), encoded, envelope: envelope2 };
43618
+ });
43619
+ const byteLength = stored.reduce((total, entry) => total + entry.byteLength, 0);
43620
+ const maximumBytes = critical ? this.#limits.criticalEventBytes : this.#limits.standardEventBytes;
43621
+ if (rowNumber(state, "total_bytes") + byteLength > maximumBytes) {
43622
+ throw new SessionEventOutboxCapacityError();
43623
+ }
43624
+ const insert = this.#database.prepare(
43625
+ `INSERT INTO relay_session_event_outbox (
43626
+ event_id, session_id, source_seq, byte_length, envelope_json
43627
+ ) VALUES (?, ?, ?, ?, ?)`
43628
+ );
43629
+ for (const entry of stored) {
43630
+ insert.run(
43631
+ entry.envelope.delivery.eventId,
43632
+ sessionId,
43633
+ Number(entry.envelope.delivery.sourceSeq),
43634
+ entry.byteLength,
43635
+ entry.encoded
43636
+ );
43637
+ }
43638
+ this.#database.prepare(
43639
+ `UPDATE relay_session_event_outbox_state
43640
+ SET next_source_seq = next_source_seq + ?,
43641
+ total_bytes = total_bytes + ?
43642
+ WHERE singleton = 1`
43643
+ ).run(stored.length, byteLength);
43644
+ this.#database.exec("COMMIT");
43645
+ return stored.map((entry) => entry.envelope);
43646
+ } catch (error62) {
43647
+ this.#database.exec("ROLLBACK");
43648
+ throw error62;
43649
+ }
43650
+ };
43651
+ pending = (limit, excludedEventIds) => {
43652
+ if (!Number.isSafeInteger(limit) || limit < 1) {
43653
+ throw new Error("Session event replay limit must be a positive integer");
43654
+ }
43655
+ const exclusion = excludedEventIds.length === 0 ? "" : `WHERE event_id NOT IN (${excludedEventIds.map(() => "?").join(", ")})`;
43656
+ return this.#database.prepare(
43657
+ `SELECT envelope_json
43658
+ FROM relay_session_event_outbox
43659
+ ${exclusion}
43660
+ ORDER BY source_seq
43661
+ LIMIT ?`
43662
+ ).all(...excludedEventIds, limit).map((row) => {
43663
+ const envelope2 = decodeSessionEventEnvelope(rowString(row, "envelope_json"));
43664
+ if (!("delivery" in envelope2)) {
43665
+ throw new Error("SQLite returned an invalid reliable Session event envelope");
43666
+ }
43667
+ return envelope2;
43668
+ });
43669
+ };
43670
+ quarantine = (rejection) => {
43671
+ this.#database.exec("BEGIN IMMEDIATE");
43672
+ try {
43673
+ const stored = this.#database.prepare(
43674
+ `SELECT byte_length, envelope_json
43675
+ FROM relay_session_event_outbox
43676
+ WHERE event_id = ? AND session_id = ?`
43677
+ ).get(rejection.eventId, rejection.sessionId);
43678
+ if (stored === void 0) {
43679
+ this.#database.exec("COMMIT");
43680
+ return false;
43681
+ }
43682
+ this.#database.prepare(
43683
+ `INSERT INTO relay_session_event_dead_letters (
43684
+ event_id, session_id, rejection_code, envelope_json
43685
+ ) VALUES (?, ?, ?, ?)`
43686
+ ).run(rejection.eventId, rejection.sessionId, rejection.code, rowString(stored, "envelope_json"));
43687
+ this.#deleteStored(rejection.eventId, rejection.sessionId, rowNumber(stored, "byte_length"));
43688
+ this.#database.prepare(
43689
+ `DELETE FROM relay_session_event_dead_letters
43690
+ WHERE ordinal NOT IN (
43691
+ SELECT ordinal FROM relay_session_event_dead_letters
43692
+ ORDER BY ordinal DESC LIMIT ?
43693
+ )`
43694
+ ).run(MAX_DEAD_LETTERS);
43695
+ this.#database.exec("COMMIT");
43696
+ return true;
43697
+ } catch (error62) {
43698
+ this.#database.exec("ROLLBACK");
43699
+ throw error62;
43700
+ }
43701
+ };
43702
+ #deleteStored = (eventId, sessionId, byteLength) => {
43703
+ const deletion = this.#database.prepare(
43704
+ `DELETE FROM relay_session_event_outbox
43705
+ WHERE event_id = ? AND session_id = ?`
43706
+ ).run(eventId, sessionId);
43707
+ if (deletion.changes !== 1) {
43708
+ throw new Error("The Session event could not be removed from the outbox");
43709
+ }
43710
+ const state = this.#database.prepare(
43711
+ `UPDATE relay_session_event_outbox_state
43712
+ SET total_bytes = total_bytes - ?
43713
+ WHERE singleton = 1 AND total_bytes >= ?`
43714
+ ).run(byteLength, byteLength);
43715
+ if (state.changes !== 1) {
43716
+ throw new Error("The Session event outbox capacity state is inconsistent");
43717
+ }
43718
+ };
43719
+ #remove = (eventId, sessionId) => {
43720
+ this.#database.exec("BEGIN IMMEDIATE");
43721
+ try {
43722
+ const stored = this.#database.prepare(
43723
+ `SELECT byte_length
43724
+ FROM relay_session_event_outbox
43725
+ WHERE event_id = ? AND session_id = ?`
43726
+ ).get(eventId, sessionId);
43727
+ if (stored === void 0) {
43728
+ this.#database.exec("COMMIT");
43729
+ return false;
43730
+ }
43731
+ this.#deleteStored(eventId, sessionId, rowNumber(stored, "byte_length"));
43732
+ this.#database.exec("COMMIT");
43733
+ return true;
43734
+ } catch (error62) {
43735
+ this.#database.exec("ROLLBACK");
43736
+ throw error62;
43737
+ }
43738
+ };
43739
+ };
43740
+
43252
43741
  // src/relay-connection/session-event-publisher.ts
43253
43742
  import { WebSocket as WebSocket9 } from "undici";
43254
- var MAX_LIVE_EVENT_BUFFERED_BYTES = 16 * 1024 * 1024;
43743
+ var SESSION_EVENT_REPLAY_WINDOW = 16;
43744
+ var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
43745
+ function reservesSessionEventCapacity(event) {
43746
+ if (event.kind === SESSION_COMPLETED_KIND || event.kind === SESSION_FAILED_KIND || event.kind === SESSION_FORKED_KIND || event.kind === SESSION_READY_KIND) {
43747
+ return true;
43748
+ }
43749
+ return event.kind === AGENT_ACTIVITY_KIND && (event.activityType === "progress" || event.activityType === "reasoning") && (event.status === "completed" || event.status === "failed");
43750
+ }
43255
43751
  var DaemonSessionEventPublisher = class {
43256
- #socket = null;
43257
- #reportedDrop = false;
43258
- connect = (socket) => {
43259
- this.#socket = socket;
43260
- this.#reportedDrop = false;
43752
+ #inFlight = /* @__PURE__ */ new Map();
43753
+ #outbox;
43754
+ #reportedCapacityKinds = /* @__PURE__ */ new Set();
43755
+ #timer;
43756
+ #connection = { kind: "disconnected" };
43757
+ #deferredReplay = null;
43758
+ #inFlightBytes = 0;
43759
+ constructor(outbox, timer) {
43760
+ this.#outbox = outbox;
43761
+ this.#timer = timer;
43762
+ }
43763
+ acknowledge = (acknowledgement) => {
43764
+ if (!this.#outbox.acknowledge(acknowledgement)) {
43765
+ return;
43766
+ }
43767
+ this.#removeInFlight(acknowledgement.eventId);
43768
+ this.#publishNextBatch();
43769
+ };
43770
+ close = () => {
43771
+ this.#deferredReplay?.cancel();
43772
+ this.#deferredReplay = null;
43773
+ this.#connection = { kind: "disconnected" };
43774
+ this.#clearInFlight();
43775
+ this.#outbox.close();
43776
+ };
43777
+ connect = (socket, delivery) => {
43778
+ this.#connection = { delivery, kind: "connected", socket };
43779
+ this.#clearInFlight();
43780
+ this.#publishNextBatch();
43781
+ };
43782
+ defer = (deferred) => {
43783
+ if (!this.#inFlight.has(deferred.eventId)) {
43784
+ return;
43785
+ }
43786
+ this.#clearInFlight();
43787
+ if (this.#deferredReplay !== null) {
43788
+ return;
43789
+ }
43790
+ this.#deferredReplay = this.#timer.schedule(() => {
43791
+ this.#deferredReplay = null;
43792
+ this.#publishNextBatch();
43793
+ }, deferred.retryAfterMilliseconds);
43261
43794
  };
43262
43795
  disconnect = (socket) => {
43263
- if (this.#socket === socket) {
43264
- this.#socket = null;
43796
+ if (this.#connection.kind !== "connected" || this.#connection.socket !== socket) {
43797
+ return;
43265
43798
  }
43799
+ this.#connection = { kind: "disconnected" };
43800
+ this.#clearInFlight();
43266
43801
  };
43267
43802
  publish = (sessionId, event) => {
43268
- const socket = this.#socket;
43269
- if (socket === null || socket.readyState !== WebSocket9.OPEN) {
43270
- this.#reportDrop();
43803
+ try {
43804
+ this.#outbox.enqueue(sessionId, encodeRecordedSessionEvent(event), reservesSessionEventCapacity(event));
43805
+ } catch (error62) {
43806
+ if (!(error62 instanceof SessionEventOutboxCapacityError)) {
43807
+ throw error62;
43808
+ }
43809
+ if (!this.#reportedCapacityKinds.has(event.kind)) {
43810
+ this.#reportedCapacityKinds.add(event.kind);
43811
+ console.warn(
43812
+ `Mobius daemon could not persist "${event.kind}" because the Session event outbox is full`
43813
+ );
43814
+ }
43271
43815
  return;
43272
43816
  }
43273
- if (socket.bufferedAmount >= MAX_LIVE_EVENT_BUFFERED_BYTES) {
43274
- this.#socket = null;
43275
- socket.close(1013, "Live event backpressure");
43276
- this.#reportDrop();
43817
+ this.#publishNextBatch();
43818
+ };
43819
+ publishBatch = (sessionId, events) => {
43820
+ const critical = events.some(reservesSessionEventCapacity);
43821
+ this.#outbox.enqueueBatch(sessionId, events.map(encodeRecordedSessionEvent), critical);
43822
+ this.#publishNextBatch();
43823
+ };
43824
+ quarantine = (rejection) => {
43825
+ if (!this.#outbox.quarantine(rejection)) {
43277
43826
  return;
43278
43827
  }
43279
- try {
43280
- socket.send(
43281
- encodeDaemonToServiceMessage({
43282
- kind: SESSION_EVENT_KIND,
43283
- payload: encodeRecordedSessionEvent(event),
43284
- sessionId
43285
- })
43286
- );
43287
- } catch {
43288
- this.#reportDrop();
43289
- }
43828
+ this.#removeInFlight(rejection.eventId);
43829
+ console.error(`Mobius Relay rejected Session event ${rejection.eventId} with code ${rejection.code}`);
43830
+ this.#publishNextBatch();
43290
43831
  };
43291
- close = () => {
43292
- this.#socket = null;
43832
+ #clearInFlight = () => {
43833
+ this.#inFlight.clear();
43834
+ this.#inFlightBytes = 0;
43835
+ };
43836
+ #publishNextBatch = () => {
43837
+ if (this.#deferredReplay !== null || this.#connection.kind !== "connected") {
43838
+ return;
43839
+ }
43840
+ while (this.#inFlight.size < SESSION_EVENT_REPLAY_WINDOW) {
43841
+ const batch = this.#outbox.pending(SESSION_EVENT_REPLAY_WINDOW - this.#inFlight.size, [
43842
+ ...this.#inFlight.keys()
43843
+ ]);
43844
+ let sent = 0;
43845
+ for (const envelope2 of batch) {
43846
+ const encoded = encodeDaemonToServiceMessage(
43847
+ this.#connection.delivery === "reliable" ? envelope2 : {
43848
+ kind: envelope2.kind,
43849
+ payload: envelope2.payload,
43850
+ sessionId: envelope2.sessionId
43851
+ }
43852
+ );
43853
+ const byteLength = Buffer.byteLength(encoded, "utf8");
43854
+ if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
43855
+ return;
43856
+ }
43857
+ if (!this.#send(encoded)) {
43858
+ this.#clearInFlight();
43859
+ return;
43860
+ }
43861
+ if (this.#connection.delivery === "ephemeral") {
43862
+ this.#outbox.acknowledge({
43863
+ eventId: envelope2.delivery.eventId,
43864
+ kind: "session.event.ack",
43865
+ outcome: { kind: "relayed" },
43866
+ sessionId: envelope2.sessionId
43867
+ });
43868
+ sent += 1;
43869
+ continue;
43870
+ }
43871
+ this.#inFlight.set(envelope2.delivery.eventId, { byteLength });
43872
+ this.#inFlightBytes += byteLength;
43873
+ sent += 1;
43874
+ }
43875
+ if (sent === 0) {
43876
+ return;
43877
+ }
43878
+ }
43293
43879
  };
43294
- #reportDrop = () => {
43295
- if (this.#reportedDrop) {
43880
+ #removeInFlight = (eventId) => {
43881
+ const inFlight = this.#inFlight.get(eventId);
43882
+ if (inFlight === void 0) {
43296
43883
  return;
43297
43884
  }
43298
- this.#reportedDrop = true;
43299
- console.warn("Mobius daemon dropped live Session events while the Relay was unavailable");
43885
+ this.#inFlight.delete(eventId);
43886
+ this.#inFlightBytes -= inFlight.byteLength;
43887
+ };
43888
+ #send = (encoded) => {
43889
+ if (this.#connection.kind !== "connected" || this.#connection.socket.readyState !== WebSocket9.OPEN) {
43890
+ return false;
43891
+ }
43892
+ try {
43893
+ this.#connection.socket.send(encoded);
43894
+ return true;
43895
+ } catch {
43896
+ return false;
43897
+ }
43300
43898
  };
43301
43899
  };
43302
43900
 
@@ -43357,7 +43955,7 @@ async function installDetectedAgentIntegrations(config2, installations, signal)
43357
43955
  {
43358
43956
  agentId: agent.id,
43359
43957
  kind: AGENT_INSTALL_REQUEST_KIND,
43360
- requestId: `automatic-integration-${randomUUID7()}`
43958
+ requestId: `automatic-integration-${randomUUID8()}`
43361
43959
  },
43362
43960
  signal
43363
43961
  );
@@ -43379,7 +43977,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
43379
43977
  join18(stateDirectory, "sessions", `${createHash6("sha256").update(config2.channelId).digest("hex")}.sqlite`)
43380
43978
  );
43381
43979
  const operations = new DaemonOperationState();
43382
- const publisher = new DaemonSessionEventPublisher();
43980
+ const channelHash = createHash6("sha256").update(config2.channelId).digest("hex");
43981
+ const publisher = new DaemonSessionEventPublisher(
43982
+ new SessionEventOutbox(join18(stateDirectory, "events", `${channelHash}.sqlite`)),
43983
+ timer
43984
+ );
43383
43985
  const activeTurns = new DaemonTurnRegistry(operations);
43384
43986
  const recordSessionEvent = (sessionId, event) => {
43385
43987
  publisher.publish(sessionId, activeTurns.recordEvent(event));