@moxt-ai/mobius 0.0.152 → 0.0.154

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 (35) hide show
  1. package/dist/cli.js +126 -284
  2. package/dist/cli.js.map +4 -4
  3. package/dist/daemon-active-turn.d.ts +1 -0
  4. package/dist/daemon-active-turn.d.ts.map +1 -1
  5. package/dist/daemon-active-turn.js +1 -0
  6. package/dist/daemon-active-turn.js.map +1 -1
  7. package/dist/daemon-turn-registry.d.ts +2 -1
  8. package/dist/daemon-turn-registry.d.ts.map +1 -1
  9. package/dist/daemon-turn-registry.js +32 -34
  10. package/dist/daemon-turn-registry.js.map +1 -1
  11. package/dist/index.js +116 -281
  12. package/dist/index.js.map +4 -4
  13. package/dist/native-sessions/live-history-page.d.ts.map +1 -1
  14. package/dist/native-sessions/live-history-page.js +2 -0
  15. package/dist/native-sessions/live-history-page.js.map +1 -1
  16. package/dist/relay-connection/run-relay-connection.js +1 -1
  17. package/dist/relay-connection/run-relay-connection.js.map +1 -1
  18. package/dist/relay-connection/session-event-publisher.d.ts +3 -4
  19. package/dist/relay-connection/session-event-publisher.d.ts.map +1 -1
  20. package/dist/relay-connection/session-event-publisher.js +14 -22
  21. package/dist/relay-connection/session-event-publisher.js.map +1 -1
  22. package/dist/relay-connection/session-event-queue.d.ts +14 -0
  23. package/dist/relay-connection/session-event-queue.d.ts.map +1 -0
  24. package/dist/relay-connection/session-event-queue.js +75 -0
  25. package/dist/relay-connection/session-event-queue.js.map +1 -0
  26. package/dist/run-daemon.d.ts.map +1 -1
  27. package/dist/run-daemon.js +1 -3
  28. package/dist/run-daemon.js.map +1 -1
  29. package/dist/runtime.js +118 -283
  30. package/dist/runtime.js.map +4 -4
  31. package/package.json +10 -3
  32. package/dist/relay-connection/session-event-outbox.d.ts +0 -20
  33. package/dist/relay-connection/session-event-outbox.d.ts.map +0 -1
  34. package/dist/relay-connection/session-event-outbox.js +0 -243
  35. package/dist/relay-connection/session-event-outbox.js.map +0 -1
package/dist/runtime.js CHANGED
@@ -20964,6 +20964,11 @@ var assistantHistoryMessageSchema = external_exports.strictObject({
20964
20964
  }).readonly();
20965
20965
  var historyMessage = external_exports.discriminatedUnion("role", [userHistoryMessage, assistantHistoryMessageSchema]);
20966
20966
  var availableSessionHistoryObjectSchema = external_exports.strictObject({
20967
+ streamPosition: external_exports.strictObject({
20968
+ commandId: identifierStringSchema,
20969
+ epoch: identifierStringSchema,
20970
+ sequence: external_exports.number().int().nonnegative()
20971
+ }).readonly().optional(),
20967
20972
  status: external_exports.literal("available"),
20968
20973
  messages: external_exports.array(historyMessage).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
20969
20974
  truncated: external_exports.boolean(),
@@ -38233,6 +38238,7 @@ var ActiveDaemonTurn = class {
38233
38238
  commandId: decodeIdentifier(this.command.commandId),
38234
38239
  epoch: this.#epoch,
38235
38240
  messages: this.timelineMessages(),
38241
+ sequence: this.#sequence,
38236
38242
  state: "running",
38237
38243
  truncated: false
38238
38244
  };
@@ -38260,7 +38266,7 @@ var ActiveDaemonTurn = class {
38260
38266
  };
38261
38267
 
38262
38268
  // src/daemon-turn-registry.ts
38263
- var COMPLETED_TURN_RECOVERY_LIMIT = 32;
38269
+ var MAX_SESSIONS_WITH_COMPLETED_RECOVERY = 32;
38264
38270
  var waitForTurnCompletion = (turn, waitSignal) => {
38265
38271
  waitSignal.throwIfAborted();
38266
38272
  return new Promise((resolve9, reject) => {
@@ -38286,46 +38292,46 @@ var waitForTurnCompletion = (turn, waitSignal) => {
38286
38292
  });
38287
38293
  };
38288
38294
  var DaemonTurnRegistry = class {
38289
- #turns = /* @__PURE__ */ new Map();
38290
- #completed = /* @__PURE__ */ new Map();
38295
+ #inFlightByCommandId = /* @__PURE__ */ new Map();
38296
+ #latestCompletedBySessionId = /* @__PURE__ */ new Map();
38291
38297
  #operations;
38292
38298
  constructor(operations) {
38293
38299
  this.#operations = operations;
38294
38300
  }
38295
38301
  start = (command, attemptId) => {
38296
38302
  const turn = new ActiveDaemonTurn(command, attemptId);
38297
- this.#turns.set(command.commandId, turn);
38303
+ this.#inFlightByCommandId.set(command.commandId, turn);
38298
38304
  return turn;
38299
38305
  };
38300
- get = (commandId) => this.#turns.get(commandId);
38306
+ get = (commandId) => this.#inFlightByCommandId.get(commandId);
38301
38307
  finish = (turn) => {
38302
38308
  const recovery = turn.recovery();
38303
- this.#turns.delete(turn.command.commandId);
38309
+ this.#inFlightByCommandId.delete(turn.command.commandId);
38304
38310
  if (recovery !== null) {
38305
- this.#completed.delete(turn.command.sessionId);
38306
- this.#completed.set(turn.command.sessionId, {
38307
- command: turn.command,
38308
- recovery: { ...recovery, state: "completed" }
38309
- });
38310
- while (this.#completed.size > COMPLETED_TURN_RECOVERY_LIMIT) {
38311
- const oldestSessionId = this.#completed.keys().next().value;
38312
- if (oldestSessionId === void 0) {
38313
- break;
38314
- }
38315
- this.#completed.delete(oldestSessionId);
38316
- }
38311
+ this.retainLatestCompletedRecovery(turn.command, { ...recovery, state: "completed" });
38317
38312
  }
38318
38313
  turn.finish();
38319
38314
  };
38315
+ retainLatestCompletedRecovery = (command, recovery) => {
38316
+ this.#latestCompletedBySessionId.delete(command.sessionId);
38317
+ this.#latestCompletedBySessionId.set(command.sessionId, { command, recovery });
38318
+ while (this.#latestCompletedBySessionId.size > MAX_SESSIONS_WITH_COMPLETED_RECOVERY) {
38319
+ const oldestSessionId = this.#latestCompletedBySessionId.keys().next().value;
38320
+ if (oldestSessionId === void 0) {
38321
+ break;
38322
+ }
38323
+ this.#latestCompletedBySessionId.delete(oldestSessionId);
38324
+ }
38325
+ };
38320
38326
  recordEvent = (event) => {
38321
38327
  if ("commandId" in event) {
38322
- const turn = this.#turns.get(event.commandId);
38328
+ const turn = this.#inFlightByCommandId.get(event.commandId);
38323
38329
  if (turn !== void 0 && turn.command.sessionId === event.sessionId) {
38324
38330
  return turn.recordEvent(event);
38325
38331
  }
38326
38332
  }
38327
38333
  if (event.kind === SESSION_PERMISSION_RESOLVED_KIND || event.kind === SESSION_ELICITATION_RESOLVED_KIND) {
38328
- for (const turn of this.#turns.values()) {
38334
+ for (const turn of this.#inFlightByCommandId.values()) {
38329
38335
  if (turn.matchesInteraction(event.interactionId, event.sessionId)) {
38330
38336
  return turn.recordEvent(event);
38331
38337
  }
@@ -38334,17 +38340,17 @@ var DaemonTurnRegistry = class {
38334
38340
  return event;
38335
38341
  };
38336
38342
  recordSteeringPrompt = (command) => {
38337
- const target = this.#turns.get(command.targetCommandId);
38343
+ const target = this.#inFlightByCommandId.get(command.targetCommandId);
38338
38344
  return target?.recordSteeringPrompt(command) ?? false;
38339
38345
  };
38340
38346
  executionSnapshot = async (matches) => {
38341
38347
  const matching = [];
38342
- for (const turn of this.#turns.values()) {
38348
+ for (const turn of this.#inFlightByCommandId.values()) {
38343
38349
  if (await matches(turn.command)) {
38344
38350
  matching.push(turn);
38345
38351
  }
38346
38352
  }
38347
- const current = matching.filter((turn) => this.#turns.get(turn.command.commandId) === turn);
38353
+ const current = matching.filter((turn) => this.#inFlightByCommandId.get(turn.command.commandId) === turn);
38348
38354
  return {
38349
38355
  turn: current.map((turn) => turn.snapshot()).find((snapshot) => snapshot !== null) ?? null,
38350
38356
  queued: current.filter((turn) => turn.isQueued()).map(({ command }) => ({
@@ -38355,7 +38361,7 @@ var DaemonTurnRegistry = class {
38355
38361
  };
38356
38362
  };
38357
38363
  snapshot = (matches) => {
38358
- for (const turn of this.#turns.values()) {
38364
+ for (const turn of this.#inFlightByCommandId.values()) {
38359
38365
  if (!matches(turn.command)) {
38360
38366
  continue;
38361
38367
  }
@@ -38367,7 +38373,7 @@ var DaemonTurnRegistry = class {
38367
38373
  return null;
38368
38374
  };
38369
38375
  recovery = (matches) => {
38370
- for (const turn of this.#turns.values()) {
38376
+ for (const turn of this.#inFlightByCommandId.values()) {
38371
38377
  if (!matches(turn.command)) {
38372
38378
  continue;
38373
38379
  }
@@ -38376,7 +38382,7 @@ var DaemonTurnRegistry = class {
38376
38382
  return recovery;
38377
38383
  }
38378
38384
  }
38379
- for (const completed of this.#completed.values()) {
38385
+ for (const completed of this.#latestCompletedBySessionId.values()) {
38380
38386
  if (matches(completed.command)) {
38381
38387
  return completed.recovery;
38382
38388
  }
@@ -38384,17 +38390,17 @@ var DaemonTurnRegistry = class {
38384
38390
  return null;
38385
38391
  };
38386
38392
  close = async () => {
38387
- const turns = [...this.#turns.values()];
38393
+ const turns = [...this.#inFlightByCommandId.values()];
38388
38394
  for (const turn of turns) {
38389
38395
  turn.abortDaemon();
38390
38396
  }
38391
38397
  await Promise.all(turns.map((turn) => turn.completion));
38392
- this.#completed.clear();
38398
+ this.#latestCompletedBySessionId.clear();
38393
38399
  };
38394
38400
  admitAndWait = async (turn, waitSignal, onQueued) => {
38395
38401
  const command = turn.command;
38396
38402
  waitSignal.throwIfAborted();
38397
- const head = () => [...this.#turns.values()].find(
38403
+ const head = () => [...this.#inFlightByCommandId.values()].find(
38398
38404
  (turn2) => turn2.command.sessionId === command.sessionId && turn2.command.disposition !== SessionPromptDisposition.Steer
38399
38405
  );
38400
38406
  if (command.disposition === SessionPromptDisposition.Queue) {
@@ -40964,6 +40970,7 @@ function paginateActiveTurn(history, activeTurn, sessionId, endIndex, limit) {
40964
40970
  historicalCharacterLimit
40965
40971
  ) : null;
40966
40972
  return {
40973
+ streamPosition: { commandId: activeTurn.commandId, epoch: activeTurn.epoch, sequence: activeTurn.sequence },
40967
40974
  coveredCommandIds: historical?.coveredCommandIds ?? [],
40968
40975
  coveredMessageIds: [
40969
40976
  ...historical?.coveredMessageIds ?? [],
@@ -40996,6 +41003,7 @@ function mergeLiveSessionHistory(history, activeTurn, sessionId) {
40996
41003
  }
40997
41004
  return {
40998
41005
  coveredCommandIds: page.coveredCommandIds,
41006
+ ...page.streamPosition === void 0 ? {} : { streamPosition: page.streamPosition },
40999
41007
  coveredMessageIds: page.coveredMessageIds,
41000
41008
  messages: page.messages,
41001
41009
  status: "available",
@@ -42611,7 +42619,7 @@ async function runRelayConnection({
42611
42619
  return;
42612
42620
  }
42613
42621
  if (kind === SESSION_EVENT_REJECTED_KIND) {
42614
- publisher.quarantine(message);
42622
+ publisher.reject(message);
42615
42623
  return;
42616
42624
  }
42617
42625
  commands.handle(message);
@@ -42638,250 +42646,84 @@ async function runRelayConnection({
42638
42646
  }
42639
42647
  }
42640
42648
 
42641
- // src/relay-connection/session-event-outbox.ts
42649
+ // src/relay-connection/session-event-publisher.ts
42650
+ import { WebSocket as WebSocket9 } from "undici";
42651
+
42652
+ // src/relay-connection/session-event-queue.ts
42642
42653
  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";
42654
+ var STANDARD_EVENT_BYTES = 96 * 1024 * 1024;
42655
+ var CRITICAL_EVENT_BYTES = 128 * 1024 * 1024;
42656
+ var SessionEventQueueCapacityError = class extends Error {
42657
+ code = "session_event_queue_full";
42655
42658
  constructor() {
42656
- super("The persistent Session event outbox is full");
42657
- this.name = "SessionEventOutboxCapacityError";
42659
+ super("The in-memory Session event queue is full");
42660
+ this.name = "SessionEventQueueCapacityError";
42658
42661
  }
42659
42662
  };
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
- };
42663
+ var SessionEventQueue = class {
42664
+ #streamId = randomUUID6();
42665
+ #events = /* @__PURE__ */ new Map();
42666
+ #nextSourceSeq = 1;
42667
+ #totalBytes = 0;
42730
42668
  enqueueBatch = (sessionId, payloads, critical) => {
42731
42669
  if (payloads.length < 1 || payloads.length > 32) {
42732
42670
  throw new Error("A Session event batch must contain between 1 and 32 events");
42733
42671
  }
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;
42672
+ if (!Number.isSafeInteger(this.#nextSourceSeq + payloads.length)) {
42673
+ throw new Error("The Session event source sequence is exhausted");
42791
42674
  }
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;
42675
+ const entries = payloads.map((payload, index) => {
42676
+ const envelope2 = {
42677
+ delivery: {
42678
+ eventId: randomUUID6(),
42679
+ kind: "reliable",
42680
+ sourceSeq: (this.#nextSourceSeq + index).toString(),
42681
+ streamId: this.#streamId
42682
+ },
42683
+ kind: SESSION_EVENT_KIND,
42684
+ payload,
42685
+ sessionId
42686
+ };
42687
+ return { envelope: envelope2, byteLength: Buffer.byteLength(encodeSessionEventEnvelope(envelope2), "utf8") };
42810
42688
  });
42689
+ const byteLength = entries.reduce((total, entry) => total + entry.byteLength, 0);
42690
+ if (this.#totalBytes + byteLength > (critical ? CRITICAL_EVENT_BYTES : STANDARD_EVENT_BYTES)) {
42691
+ throw new SessionEventQueueCapacityError();
42692
+ }
42693
+ for (const entry of entries) {
42694
+ this.#events.set(entry.envelope.delivery.eventId, entry);
42695
+ }
42696
+ this.#nextSourceSeq += entries.length;
42697
+ this.#totalBytes += byteLength;
42811
42698
  };
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;
42699
+ pending = (limit, excludedEventIds) => {
42700
+ const result = [];
42701
+ for (const [eventId, entry] of this.#events) {
42702
+ if (!excludedEventIds.has(eventId)) {
42703
+ result.push(entry.envelope);
42704
+ if (result.length === limit) {
42705
+ break;
42706
+ }
42823
42707
  }
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
42708
  }
42709
+ return result;
42843
42710
  };
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");
42711
+ remove = (eventId, sessionId) => {
42712
+ const entry = this.#events.get(eventId);
42713
+ if (entry === void 0 || entry.envelope.sessionId !== sessionId) {
42714
+ return false;
42859
42715
  }
42716
+ this.#events.delete(eventId);
42717
+ this.#totalBytes -= entry.byteLength;
42718
+ return true;
42860
42719
  };
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
- }
42720
+ clear = () => {
42721
+ this.#events.clear();
42722
+ this.#totalBytes = 0;
42880
42723
  };
42881
42724
  };
42882
42725
 
42883
42726
  // src/relay-connection/session-event-publisher.ts
42884
- import { WebSocket as WebSocket9 } from "undici";
42885
42727
  var SESSION_EVENT_REPLAY_WINDOW = 16;
42886
42728
  var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
42887
42729
  function reservesSessionEventCapacity(event) {
@@ -42892,18 +42734,17 @@ function reservesSessionEventCapacity(event) {
42892
42734
  }
42893
42735
  var DaemonSessionEventPublisher = class {
42894
42736
  #inFlight = /* @__PURE__ */ new Map();
42895
- #outbox;
42737
+ #queue = new SessionEventQueue();
42896
42738
  #reportedCapacityKinds = /* @__PURE__ */ new Set();
42897
42739
  #timer;
42898
42740
  #connection = { kind: "disconnected" };
42899
42741
  #deferredReplay = null;
42900
42742
  #inFlightBytes = 0;
42901
- constructor(outbox, timer) {
42902
- this.#outbox = outbox;
42743
+ constructor(timer) {
42903
42744
  this.#timer = timer;
42904
42745
  }
42905
42746
  acknowledge = (acknowledgement) => {
42906
- if (!this.#outbox.acknowledge(acknowledgement)) {
42747
+ if (!this.#queue.remove(acknowledgement.eventId, acknowledgement.sessionId)) {
42907
42748
  return;
42908
42749
  }
42909
42750
  this.#removeInFlight(acknowledgement.eventId);
@@ -42914,7 +42755,7 @@ var DaemonSessionEventPublisher = class {
42914
42755
  this.#deferredReplay = null;
42915
42756
  this.#connection = { kind: "disconnected" };
42916
42757
  this.#clearInFlight();
42917
- this.#outbox.close();
42758
+ this.#queue.clear();
42918
42759
  };
42919
42760
  connect = (socket, delivery) => {
42920
42761
  this.#connection = { delivery, kind: "connected", socket };
@@ -42943,16 +42784,18 @@ var DaemonSessionEventPublisher = class {
42943
42784
  };
42944
42785
  publish = (sessionId, event) => {
42945
42786
  try {
42946
- this.#outbox.enqueue(sessionId, encodeRecordedSessionEvent(event), reservesSessionEventCapacity(event));
42787
+ this.#queue.enqueueBatch(
42788
+ sessionId,
42789
+ [encodeRecordedSessionEvent(event)],
42790
+ reservesSessionEventCapacity(event)
42791
+ );
42947
42792
  } catch (error62) {
42948
- if (!(error62 instanceof SessionEventOutboxCapacityError)) {
42793
+ if (!(error62 instanceof SessionEventQueueCapacityError)) {
42949
42794
  throw error62;
42950
42795
  }
42951
42796
  if (!this.#reportedCapacityKinds.has(event.kind)) {
42952
42797
  this.#reportedCapacityKinds.add(event.kind);
42953
- console.warn(
42954
- `Mobius daemon could not persist "${event.kind}" because the Session event outbox is full`
42955
- );
42798
+ console.warn(`Mobius daemon could not buffer "${event.kind}" because the Session event queue is full`);
42956
42799
  }
42957
42800
  return;
42958
42801
  }
@@ -42960,11 +42803,11 @@ var DaemonSessionEventPublisher = class {
42960
42803
  };
42961
42804
  publishBatch = (sessionId, events) => {
42962
42805
  const critical = events.some(reservesSessionEventCapacity);
42963
- this.#outbox.enqueueBatch(sessionId, events.map(encodeRecordedSessionEvent), critical);
42806
+ this.#queue.enqueueBatch(sessionId, events.map(encodeRecordedSessionEvent), critical);
42964
42807
  this.#publishNextBatch();
42965
42808
  };
42966
- quarantine = (rejection) => {
42967
- if (!this.#outbox.quarantine(rejection)) {
42809
+ reject = (rejection) => {
42810
+ if (!this.#queue.remove(rejection.eventId, rejection.sessionId)) {
42968
42811
  return;
42969
42812
  }
42970
42813
  this.#removeInFlight(rejection.eventId);
@@ -42980,9 +42823,10 @@ var DaemonSessionEventPublisher = class {
42980
42823
  return;
42981
42824
  }
42982
42825
  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
- ]);
42826
+ const batch = this.#queue.pending(
42827
+ SESSION_EVENT_REPLAY_WINDOW - this.#inFlight.size,
42828
+ new Set(this.#inFlight.keys())
42829
+ );
42986
42830
  let sent = 0;
42987
42831
  for (const envelope2 of batch) {
42988
42832
  const encoded = encodeDaemonToServiceMessage(
@@ -43001,12 +42845,7 @@ var DaemonSessionEventPublisher = class {
43001
42845
  return;
43002
42846
  }
43003
42847
  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
- });
42848
+ this.#queue.remove(envelope2.delivery.eventId, envelope2.sessionId);
43010
42849
  sent += 1;
43011
42850
  continue;
43012
42851
  }
@@ -43119,11 +42958,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
43119
42958
  join17(stateDirectory, "sessions", `${createHash5("sha256").update(config2.channelId).digest("hex")}.sqlite`)
43120
42959
  );
43121
42960
  const operations = new DaemonOperationState();
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
- );
42961
+ const publisher = new DaemonSessionEventPublisher(timer);
43127
42962
  const activeTurns = new DaemonTurnRegistry(operations);
43128
42963
  const recordSessionEvent = (sessionId, event) => {
43129
42964
  publisher.publish(sessionId, activeTurns.recordEvent(event));
@@ -43317,7 +43152,7 @@ import { fetch as fetch4 } from "undici";
43317
43152
  // src/storage/runtime-installation-store.ts
43318
43153
  import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
43319
43154
  import { mkdir as mkdir4, open as open5, readdir as readdir3, rename as rename3, unlink, writeFile as writeFile3 } from "node:fs/promises";
43320
- import { dirname as dirname6, join as join18, resolve as resolve6 } from "node:path";
43155
+ import { dirname as dirname5, join as join18, resolve as resolve6 } from "node:path";
43321
43156
  var APPLICATION_ID_PATTERN = /^[a-z][a-z0-9]*(?:[.-][a-z0-9]+){1,31}$/;
43322
43157
  var CONNECTION_ID_PATTERN = /^mcn_[a-f0-9]{32}$/;
43323
43158
  var INSTALLATION_ID_PATTERN = /^min_[a-f0-9]{32}$/;
@@ -43559,7 +43394,7 @@ async function readJsonFile(path2, signal, missingCode) {
43559
43394
  }
43560
43395
  async function writeJsonAtomically(path2, value, signal) {
43561
43396
  signal.throwIfAborted();
43562
- const directory = dirname6(path2);
43397
+ const directory = dirname5(path2);
43563
43398
  const temporaryPath = join18(directory, `.${randomUUID8()}.tmp`);
43564
43399
  try {
43565
43400
  await mkdir4(directory, { mode: 448, recursive: true });