@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.
- package/dist/cli.js +126 -284
- package/dist/cli.js.map +4 -4
- package/dist/daemon-active-turn.d.ts +1 -0
- package/dist/daemon-active-turn.d.ts.map +1 -1
- package/dist/daemon-active-turn.js +1 -0
- package/dist/daemon-active-turn.js.map +1 -1
- package/dist/daemon-turn-registry.d.ts +2 -1
- package/dist/daemon-turn-registry.d.ts.map +1 -1
- package/dist/daemon-turn-registry.js +32 -34
- package/dist/daemon-turn-registry.js.map +1 -1
- package/dist/index.js +116 -281
- package/dist/index.js.map +4 -4
- package/dist/native-sessions/live-history-page.d.ts.map +1 -1
- package/dist/native-sessions/live-history-page.js +2 -0
- package/dist/native-sessions/live-history-page.js.map +1 -1
- package/dist/relay-connection/run-relay-connection.js +1 -1
- package/dist/relay-connection/run-relay-connection.js.map +1 -1
- package/dist/relay-connection/session-event-publisher.d.ts +3 -4
- package/dist/relay-connection/session-event-publisher.d.ts.map +1 -1
- package/dist/relay-connection/session-event-publisher.js +14 -22
- package/dist/relay-connection/session-event-publisher.js.map +1 -1
- package/dist/relay-connection/session-event-queue.d.ts +14 -0
- package/dist/relay-connection/session-event-queue.d.ts.map +1 -0
- package/dist/relay-connection/session-event-queue.js +75 -0
- package/dist/relay-connection/session-event-queue.js.map +1 -0
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +1 -3
- package/dist/run-daemon.js.map +1 -1
- package/dist/runtime.js +118 -283
- package/dist/runtime.js.map +4 -4
- package/package.json +10 -3
- package/dist/relay-connection/session-event-outbox.d.ts +0 -20
- package/dist/relay-connection/session-event-outbox.d.ts.map +0 -1
- package/dist/relay-connection/session-event-outbox.js +0 -243
- 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
|
|
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
|
-
#
|
|
38290
|
-
#
|
|
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.#
|
|
38303
|
+
this.#inFlightByCommandId.set(command.commandId, turn);
|
|
38298
38304
|
return turn;
|
|
38299
38305
|
};
|
|
38300
|
-
get = (commandId) => this.#
|
|
38306
|
+
get = (commandId) => this.#inFlightByCommandId.get(commandId);
|
|
38301
38307
|
finish = (turn) => {
|
|
38302
38308
|
const recovery = turn.recovery();
|
|
38303
|
-
this.#
|
|
38309
|
+
this.#inFlightByCommandId.delete(turn.command.commandId);
|
|
38304
38310
|
if (recovery !== null) {
|
|
38305
|
-
this
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.#
|
|
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.
|
|
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-
|
|
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
|
-
|
|
42644
|
-
|
|
42645
|
-
|
|
42646
|
-
|
|
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
|
|
42657
|
-
this.name = "
|
|
42659
|
+
super("The in-memory Session event queue is full");
|
|
42660
|
+
this.name = "SessionEventQueueCapacityError";
|
|
42658
42661
|
}
|
|
42659
42662
|
};
|
|
42660
|
-
|
|
42661
|
-
|
|
42662
|
-
|
|
42663
|
-
|
|
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.#
|
|
42735
|
-
|
|
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
|
-
|
|
42794
|
-
|
|
42795
|
-
|
|
42796
|
-
|
|
42797
|
-
|
|
42798
|
-
|
|
42799
|
-
|
|
42800
|
-
|
|
42801
|
-
|
|
42802
|
-
|
|
42803
|
-
|
|
42804
|
-
|
|
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
|
-
|
|
42813
|
-
|
|
42814
|
-
|
|
42815
|
-
|
|
42816
|
-
|
|
42817
|
-
|
|
42818
|
-
|
|
42819
|
-
|
|
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
|
-
|
|
42845
|
-
const
|
|
42846
|
-
|
|
42847
|
-
|
|
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
|
-
|
|
42862
|
-
this.#
|
|
42863
|
-
|
|
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
|
-
#
|
|
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(
|
|
42902
|
-
this.#outbox = outbox;
|
|
42743
|
+
constructor(timer) {
|
|
42903
42744
|
this.#timer = timer;
|
|
42904
42745
|
}
|
|
42905
42746
|
acknowledge = (acknowledgement) => {
|
|
42906
|
-
if (!this.#
|
|
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.#
|
|
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.#
|
|
42787
|
+
this.#queue.enqueueBatch(
|
|
42788
|
+
sessionId,
|
|
42789
|
+
[encodeRecordedSessionEvent(event)],
|
|
42790
|
+
reservesSessionEventCapacity(event)
|
|
42791
|
+
);
|
|
42947
42792
|
} catch (error62) {
|
|
42948
|
-
if (!(error62 instanceof
|
|
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.#
|
|
42806
|
+
this.#queue.enqueueBatch(sessionId, events.map(encodeRecordedSessionEvent), critical);
|
|
42964
42807
|
this.#publishNextBatch();
|
|
42965
42808
|
};
|
|
42966
|
-
|
|
42967
|
-
if (!this.#
|
|
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.#
|
|
42984
|
-
|
|
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.#
|
|
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
|
|
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
|
|
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 =
|
|
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 });
|