@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/index.js
CHANGED
|
@@ -20990,6 +20990,11 @@ var assistantHistoryMessageSchema = external_exports.strictObject({
|
|
|
20990
20990
|
}).readonly();
|
|
20991
20991
|
var historyMessage = external_exports.discriminatedUnion("role", [userHistoryMessage, assistantHistoryMessageSchema]);
|
|
20992
20992
|
var availableSessionHistoryObjectSchema = external_exports.strictObject({
|
|
20993
|
+
streamPosition: external_exports.strictObject({
|
|
20994
|
+
commandId: identifierStringSchema,
|
|
20995
|
+
epoch: identifierStringSchema,
|
|
20996
|
+
sequence: external_exports.number().int().nonnegative()
|
|
20997
|
+
}).readonly().optional(),
|
|
20993
20998
|
status: external_exports.literal("available"),
|
|
20994
20999
|
messages: external_exports.array(historyMessage).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
|
|
20995
21000
|
truncated: external_exports.boolean(),
|
|
@@ -39111,6 +39116,7 @@ var ActiveDaemonTurn = class {
|
|
|
39111
39116
|
commandId: decodeIdentifier(this.command.commandId),
|
|
39112
39117
|
epoch: this.#epoch,
|
|
39113
39118
|
messages: this.timelineMessages(),
|
|
39119
|
+
sequence: this.#sequence,
|
|
39114
39120
|
state: "running",
|
|
39115
39121
|
truncated: false
|
|
39116
39122
|
};
|
|
@@ -39138,7 +39144,7 @@ var ActiveDaemonTurn = class {
|
|
|
39138
39144
|
};
|
|
39139
39145
|
|
|
39140
39146
|
// src/daemon-turn-registry.ts
|
|
39141
|
-
var
|
|
39147
|
+
var MAX_SESSIONS_WITH_COMPLETED_RECOVERY = 32;
|
|
39142
39148
|
var waitForTurnCompletion = (turn, waitSignal) => {
|
|
39143
39149
|
waitSignal.throwIfAborted();
|
|
39144
39150
|
return new Promise((resolve9, reject) => {
|
|
@@ -39164,46 +39170,46 @@ var waitForTurnCompletion = (turn, waitSignal) => {
|
|
|
39164
39170
|
});
|
|
39165
39171
|
};
|
|
39166
39172
|
var DaemonTurnRegistry = class {
|
|
39167
|
-
#
|
|
39168
|
-
#
|
|
39173
|
+
#inFlightByCommandId = /* @__PURE__ */ new Map();
|
|
39174
|
+
#latestCompletedBySessionId = /* @__PURE__ */ new Map();
|
|
39169
39175
|
#operations;
|
|
39170
39176
|
constructor(operations) {
|
|
39171
39177
|
this.#operations = operations;
|
|
39172
39178
|
}
|
|
39173
39179
|
start = (command, attemptId) => {
|
|
39174
39180
|
const turn = new ActiveDaemonTurn(command, attemptId);
|
|
39175
|
-
this.#
|
|
39181
|
+
this.#inFlightByCommandId.set(command.commandId, turn);
|
|
39176
39182
|
return turn;
|
|
39177
39183
|
};
|
|
39178
|
-
get = (commandId) => this.#
|
|
39184
|
+
get = (commandId) => this.#inFlightByCommandId.get(commandId);
|
|
39179
39185
|
finish = (turn) => {
|
|
39180
39186
|
const recovery = turn.recovery();
|
|
39181
|
-
this.#
|
|
39187
|
+
this.#inFlightByCommandId.delete(turn.command.commandId);
|
|
39182
39188
|
if (recovery !== null) {
|
|
39183
|
-
this
|
|
39184
|
-
this.#completed.set(turn.command.sessionId, {
|
|
39185
|
-
command: turn.command,
|
|
39186
|
-
recovery: { ...recovery, state: "completed" }
|
|
39187
|
-
});
|
|
39188
|
-
while (this.#completed.size > COMPLETED_TURN_RECOVERY_LIMIT) {
|
|
39189
|
-
const oldestSessionId = this.#completed.keys().next().value;
|
|
39190
|
-
if (oldestSessionId === void 0) {
|
|
39191
|
-
break;
|
|
39192
|
-
}
|
|
39193
|
-
this.#completed.delete(oldestSessionId);
|
|
39194
|
-
}
|
|
39189
|
+
this.retainLatestCompletedRecovery(turn.command, { ...recovery, state: "completed" });
|
|
39195
39190
|
}
|
|
39196
39191
|
turn.finish();
|
|
39197
39192
|
};
|
|
39193
|
+
retainLatestCompletedRecovery = (command, recovery) => {
|
|
39194
|
+
this.#latestCompletedBySessionId.delete(command.sessionId);
|
|
39195
|
+
this.#latestCompletedBySessionId.set(command.sessionId, { command, recovery });
|
|
39196
|
+
while (this.#latestCompletedBySessionId.size > MAX_SESSIONS_WITH_COMPLETED_RECOVERY) {
|
|
39197
|
+
const oldestSessionId = this.#latestCompletedBySessionId.keys().next().value;
|
|
39198
|
+
if (oldestSessionId === void 0) {
|
|
39199
|
+
break;
|
|
39200
|
+
}
|
|
39201
|
+
this.#latestCompletedBySessionId.delete(oldestSessionId);
|
|
39202
|
+
}
|
|
39203
|
+
};
|
|
39198
39204
|
recordEvent = (event) => {
|
|
39199
39205
|
if ("commandId" in event) {
|
|
39200
|
-
const turn = this.#
|
|
39206
|
+
const turn = this.#inFlightByCommandId.get(event.commandId);
|
|
39201
39207
|
if (turn !== void 0 && turn.command.sessionId === event.sessionId) {
|
|
39202
39208
|
return turn.recordEvent(event);
|
|
39203
39209
|
}
|
|
39204
39210
|
}
|
|
39205
39211
|
if (event.kind === SESSION_PERMISSION_RESOLVED_KIND || event.kind === SESSION_ELICITATION_RESOLVED_KIND) {
|
|
39206
|
-
for (const turn of this.#
|
|
39212
|
+
for (const turn of this.#inFlightByCommandId.values()) {
|
|
39207
39213
|
if (turn.matchesInteraction(event.interactionId, event.sessionId)) {
|
|
39208
39214
|
return turn.recordEvent(event);
|
|
39209
39215
|
}
|
|
@@ -39212,17 +39218,17 @@ var DaemonTurnRegistry = class {
|
|
|
39212
39218
|
return event;
|
|
39213
39219
|
};
|
|
39214
39220
|
recordSteeringPrompt = (command) => {
|
|
39215
|
-
const target = this.#
|
|
39221
|
+
const target = this.#inFlightByCommandId.get(command.targetCommandId);
|
|
39216
39222
|
return target?.recordSteeringPrompt(command) ?? false;
|
|
39217
39223
|
};
|
|
39218
39224
|
executionSnapshot = async (matches) => {
|
|
39219
39225
|
const matching = [];
|
|
39220
|
-
for (const turn of this.#
|
|
39226
|
+
for (const turn of this.#inFlightByCommandId.values()) {
|
|
39221
39227
|
if (await matches(turn.command)) {
|
|
39222
39228
|
matching.push(turn);
|
|
39223
39229
|
}
|
|
39224
39230
|
}
|
|
39225
|
-
const current = matching.filter((turn) => this.#
|
|
39231
|
+
const current = matching.filter((turn) => this.#inFlightByCommandId.get(turn.command.commandId) === turn);
|
|
39226
39232
|
return {
|
|
39227
39233
|
turn: current.map((turn) => turn.snapshot()).find((snapshot) => snapshot !== null) ?? null,
|
|
39228
39234
|
queued: current.filter((turn) => turn.isQueued()).map(({ command }) => ({
|
|
@@ -39233,7 +39239,7 @@ var DaemonTurnRegistry = class {
|
|
|
39233
39239
|
};
|
|
39234
39240
|
};
|
|
39235
39241
|
snapshot = (matches) => {
|
|
39236
|
-
for (const turn of this.#
|
|
39242
|
+
for (const turn of this.#inFlightByCommandId.values()) {
|
|
39237
39243
|
if (!matches(turn.command)) {
|
|
39238
39244
|
continue;
|
|
39239
39245
|
}
|
|
@@ -39245,7 +39251,7 @@ var DaemonTurnRegistry = class {
|
|
|
39245
39251
|
return null;
|
|
39246
39252
|
};
|
|
39247
39253
|
recovery = (matches) => {
|
|
39248
|
-
for (const turn of this.#
|
|
39254
|
+
for (const turn of this.#inFlightByCommandId.values()) {
|
|
39249
39255
|
if (!matches(turn.command)) {
|
|
39250
39256
|
continue;
|
|
39251
39257
|
}
|
|
@@ -39254,7 +39260,7 @@ var DaemonTurnRegistry = class {
|
|
|
39254
39260
|
return recovery;
|
|
39255
39261
|
}
|
|
39256
39262
|
}
|
|
39257
|
-
for (const completed of this.#
|
|
39263
|
+
for (const completed of this.#latestCompletedBySessionId.values()) {
|
|
39258
39264
|
if (matches(completed.command)) {
|
|
39259
39265
|
return completed.recovery;
|
|
39260
39266
|
}
|
|
@@ -39262,17 +39268,17 @@ var DaemonTurnRegistry = class {
|
|
|
39262
39268
|
return null;
|
|
39263
39269
|
};
|
|
39264
39270
|
close = async () => {
|
|
39265
|
-
const turns = [...this.#
|
|
39271
|
+
const turns = [...this.#inFlightByCommandId.values()];
|
|
39266
39272
|
for (const turn of turns) {
|
|
39267
39273
|
turn.abortDaemon();
|
|
39268
39274
|
}
|
|
39269
39275
|
await Promise.all(turns.map((turn) => turn.completion));
|
|
39270
|
-
this.#
|
|
39276
|
+
this.#latestCompletedBySessionId.clear();
|
|
39271
39277
|
};
|
|
39272
39278
|
admitAndWait = async (turn, waitSignal, onQueued) => {
|
|
39273
39279
|
const command = turn.command;
|
|
39274
39280
|
waitSignal.throwIfAborted();
|
|
39275
|
-
const head = () => [...this.#
|
|
39281
|
+
const head = () => [...this.#inFlightByCommandId.values()].find(
|
|
39276
39282
|
(turn2) => turn2.command.sessionId === command.sessionId && turn2.command.disposition !== SessionPromptDisposition.Steer
|
|
39277
39283
|
);
|
|
39278
39284
|
if (command.disposition === SessionPromptDisposition.Queue) {
|
|
@@ -41842,6 +41848,7 @@ function paginateActiveTurn(history, activeTurn, sessionId, endIndex, limit) {
|
|
|
41842
41848
|
historicalCharacterLimit
|
|
41843
41849
|
) : null;
|
|
41844
41850
|
return {
|
|
41851
|
+
streamPosition: { commandId: activeTurn.commandId, epoch: activeTurn.epoch, sequence: activeTurn.sequence },
|
|
41845
41852
|
coveredCommandIds: historical?.coveredCommandIds ?? [],
|
|
41846
41853
|
coveredMessageIds: [
|
|
41847
41854
|
...historical?.coveredMessageIds ?? [],
|
|
@@ -41874,6 +41881,7 @@ function mergeLiveSessionHistory(history, activeTurn, sessionId) {
|
|
|
41874
41881
|
}
|
|
41875
41882
|
return {
|
|
41876
41883
|
coveredCommandIds: page.coveredCommandIds,
|
|
41884
|
+
...page.streamPosition === void 0 ? {} : { streamPosition: page.streamPosition },
|
|
41877
41885
|
coveredMessageIds: page.coveredMessageIds,
|
|
41878
41886
|
messages: page.messages,
|
|
41879
41887
|
status: "available",
|
|
@@ -43469,7 +43477,7 @@ async function runRelayConnection({
|
|
|
43469
43477
|
return;
|
|
43470
43478
|
}
|
|
43471
43479
|
if (kind === SESSION_EVENT_REJECTED_KIND) {
|
|
43472
|
-
publisher.
|
|
43480
|
+
publisher.reject(message);
|
|
43473
43481
|
return;
|
|
43474
43482
|
}
|
|
43475
43483
|
commands.handle(message);
|
|
@@ -43496,250 +43504,84 @@ async function runRelayConnection({
|
|
|
43496
43504
|
}
|
|
43497
43505
|
}
|
|
43498
43506
|
|
|
43499
|
-
// src/relay-connection/session-event-
|
|
43507
|
+
// src/relay-connection/session-event-publisher.ts
|
|
43508
|
+
import { WebSocket as WebSocket9 } from "undici";
|
|
43509
|
+
|
|
43510
|
+
// src/relay-connection/session-event-queue.ts
|
|
43500
43511
|
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
43501
|
-
|
|
43502
|
-
|
|
43503
|
-
|
|
43504
|
-
|
|
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";
|
|
43512
|
+
var STANDARD_EVENT_BYTES = 96 * 1024 * 1024;
|
|
43513
|
+
var CRITICAL_EVENT_BYTES = 128 * 1024 * 1024;
|
|
43514
|
+
var SessionEventQueueCapacityError = class extends Error {
|
|
43515
|
+
code = "session_event_queue_full";
|
|
43513
43516
|
constructor() {
|
|
43514
|
-
super("The
|
|
43515
|
-
this.name = "
|
|
43517
|
+
super("The in-memory Session event queue is full");
|
|
43518
|
+
this.name = "SessionEventQueueCapacityError";
|
|
43516
43519
|
}
|
|
43517
43520
|
};
|
|
43518
|
-
|
|
43519
|
-
|
|
43520
|
-
|
|
43521
|
-
|
|
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
|
-
};
|
|
43521
|
+
var SessionEventQueue = class {
|
|
43522
|
+
#streamId = randomUUID7();
|
|
43523
|
+
#events = /* @__PURE__ */ new Map();
|
|
43524
|
+
#nextSourceSeq = 1;
|
|
43525
|
+
#totalBytes = 0;
|
|
43588
43526
|
enqueueBatch = (sessionId, payloads, critical) => {
|
|
43589
43527
|
if (payloads.length < 1 || payloads.length > 32) {
|
|
43590
43528
|
throw new Error("A Session event batch must contain between 1 and 32 events");
|
|
43591
43529
|
}
|
|
43592
|
-
this.#
|
|
43593
|
-
|
|
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;
|
|
43530
|
+
if (!Number.isSafeInteger(this.#nextSourceSeq + payloads.length)) {
|
|
43531
|
+
throw new Error("The Session event source sequence is exhausted");
|
|
43649
43532
|
}
|
|
43650
|
-
|
|
43651
|
-
|
|
43652
|
-
|
|
43653
|
-
|
|
43654
|
-
|
|
43655
|
-
|
|
43656
|
-
|
|
43657
|
-
|
|
43658
|
-
|
|
43659
|
-
|
|
43660
|
-
|
|
43661
|
-
|
|
43662
|
-
|
|
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;
|
|
43533
|
+
const entries = payloads.map((payload, index) => {
|
|
43534
|
+
const envelope2 = {
|
|
43535
|
+
delivery: {
|
|
43536
|
+
eventId: randomUUID7(),
|
|
43537
|
+
kind: "reliable",
|
|
43538
|
+
sourceSeq: (this.#nextSourceSeq + index).toString(),
|
|
43539
|
+
streamId: this.#streamId
|
|
43540
|
+
},
|
|
43541
|
+
kind: SESSION_EVENT_KIND,
|
|
43542
|
+
payload,
|
|
43543
|
+
sessionId
|
|
43544
|
+
};
|
|
43545
|
+
return { envelope: envelope2, byteLength: Buffer.byteLength(encodeSessionEventEnvelope(envelope2), "utf8") };
|
|
43668
43546
|
});
|
|
43547
|
+
const byteLength = entries.reduce((total, entry) => total + entry.byteLength, 0);
|
|
43548
|
+
if (this.#totalBytes + byteLength > (critical ? CRITICAL_EVENT_BYTES : STANDARD_EVENT_BYTES)) {
|
|
43549
|
+
throw new SessionEventQueueCapacityError();
|
|
43550
|
+
}
|
|
43551
|
+
for (const entry of entries) {
|
|
43552
|
+
this.#events.set(entry.envelope.delivery.eventId, entry);
|
|
43553
|
+
}
|
|
43554
|
+
this.#nextSourceSeq += entries.length;
|
|
43555
|
+
this.#totalBytes += byteLength;
|
|
43669
43556
|
};
|
|
43670
|
-
|
|
43671
|
-
|
|
43672
|
-
|
|
43673
|
-
|
|
43674
|
-
|
|
43675
|
-
|
|
43676
|
-
|
|
43677
|
-
|
|
43678
|
-
if (stored === void 0) {
|
|
43679
|
-
this.#database.exec("COMMIT");
|
|
43680
|
-
return false;
|
|
43557
|
+
pending = (limit, excludedEventIds) => {
|
|
43558
|
+
const result = [];
|
|
43559
|
+
for (const [eventId, entry] of this.#events) {
|
|
43560
|
+
if (!excludedEventIds.has(eventId)) {
|
|
43561
|
+
result.push(entry.envelope);
|
|
43562
|
+
if (result.length === limit) {
|
|
43563
|
+
break;
|
|
43564
|
+
}
|
|
43681
43565
|
}
|
|
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
43566
|
}
|
|
43567
|
+
return result;
|
|
43701
43568
|
};
|
|
43702
|
-
|
|
43703
|
-
const
|
|
43704
|
-
|
|
43705
|
-
|
|
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");
|
|
43569
|
+
remove = (eventId, sessionId) => {
|
|
43570
|
+
const entry = this.#events.get(eventId);
|
|
43571
|
+
if (entry === void 0 || entry.envelope.sessionId !== sessionId) {
|
|
43572
|
+
return false;
|
|
43717
43573
|
}
|
|
43574
|
+
this.#events.delete(eventId);
|
|
43575
|
+
this.#totalBytes -= entry.byteLength;
|
|
43576
|
+
return true;
|
|
43718
43577
|
};
|
|
43719
|
-
|
|
43720
|
-
this.#
|
|
43721
|
-
|
|
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
|
-
}
|
|
43578
|
+
clear = () => {
|
|
43579
|
+
this.#events.clear();
|
|
43580
|
+
this.#totalBytes = 0;
|
|
43738
43581
|
};
|
|
43739
43582
|
};
|
|
43740
43583
|
|
|
43741
43584
|
// src/relay-connection/session-event-publisher.ts
|
|
43742
|
-
import { WebSocket as WebSocket9 } from "undici";
|
|
43743
43585
|
var SESSION_EVENT_REPLAY_WINDOW = 16;
|
|
43744
43586
|
var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
|
|
43745
43587
|
function reservesSessionEventCapacity(event) {
|
|
@@ -43750,18 +43592,17 @@ function reservesSessionEventCapacity(event) {
|
|
|
43750
43592
|
}
|
|
43751
43593
|
var DaemonSessionEventPublisher = class {
|
|
43752
43594
|
#inFlight = /* @__PURE__ */ new Map();
|
|
43753
|
-
#
|
|
43595
|
+
#queue = new SessionEventQueue();
|
|
43754
43596
|
#reportedCapacityKinds = /* @__PURE__ */ new Set();
|
|
43755
43597
|
#timer;
|
|
43756
43598
|
#connection = { kind: "disconnected" };
|
|
43757
43599
|
#deferredReplay = null;
|
|
43758
43600
|
#inFlightBytes = 0;
|
|
43759
|
-
constructor(
|
|
43760
|
-
this.#outbox = outbox;
|
|
43601
|
+
constructor(timer) {
|
|
43761
43602
|
this.#timer = timer;
|
|
43762
43603
|
}
|
|
43763
43604
|
acknowledge = (acknowledgement) => {
|
|
43764
|
-
if (!this.#
|
|
43605
|
+
if (!this.#queue.remove(acknowledgement.eventId, acknowledgement.sessionId)) {
|
|
43765
43606
|
return;
|
|
43766
43607
|
}
|
|
43767
43608
|
this.#removeInFlight(acknowledgement.eventId);
|
|
@@ -43772,7 +43613,7 @@ var DaemonSessionEventPublisher = class {
|
|
|
43772
43613
|
this.#deferredReplay = null;
|
|
43773
43614
|
this.#connection = { kind: "disconnected" };
|
|
43774
43615
|
this.#clearInFlight();
|
|
43775
|
-
this.#
|
|
43616
|
+
this.#queue.clear();
|
|
43776
43617
|
};
|
|
43777
43618
|
connect = (socket, delivery) => {
|
|
43778
43619
|
this.#connection = { delivery, kind: "connected", socket };
|
|
@@ -43801,16 +43642,18 @@ var DaemonSessionEventPublisher = class {
|
|
|
43801
43642
|
};
|
|
43802
43643
|
publish = (sessionId, event) => {
|
|
43803
43644
|
try {
|
|
43804
|
-
this.#
|
|
43645
|
+
this.#queue.enqueueBatch(
|
|
43646
|
+
sessionId,
|
|
43647
|
+
[encodeRecordedSessionEvent(event)],
|
|
43648
|
+
reservesSessionEventCapacity(event)
|
|
43649
|
+
);
|
|
43805
43650
|
} catch (error62) {
|
|
43806
|
-
if (!(error62 instanceof
|
|
43651
|
+
if (!(error62 instanceof SessionEventQueueCapacityError)) {
|
|
43807
43652
|
throw error62;
|
|
43808
43653
|
}
|
|
43809
43654
|
if (!this.#reportedCapacityKinds.has(event.kind)) {
|
|
43810
43655
|
this.#reportedCapacityKinds.add(event.kind);
|
|
43811
|
-
console.warn(
|
|
43812
|
-
`Mobius daemon could not persist "${event.kind}" because the Session event outbox is full`
|
|
43813
|
-
);
|
|
43656
|
+
console.warn(`Mobius daemon could not buffer "${event.kind}" because the Session event queue is full`);
|
|
43814
43657
|
}
|
|
43815
43658
|
return;
|
|
43816
43659
|
}
|
|
@@ -43818,11 +43661,11 @@ var DaemonSessionEventPublisher = class {
|
|
|
43818
43661
|
};
|
|
43819
43662
|
publishBatch = (sessionId, events) => {
|
|
43820
43663
|
const critical = events.some(reservesSessionEventCapacity);
|
|
43821
|
-
this.#
|
|
43664
|
+
this.#queue.enqueueBatch(sessionId, events.map(encodeRecordedSessionEvent), critical);
|
|
43822
43665
|
this.#publishNextBatch();
|
|
43823
43666
|
};
|
|
43824
|
-
|
|
43825
|
-
if (!this.#
|
|
43667
|
+
reject = (rejection) => {
|
|
43668
|
+
if (!this.#queue.remove(rejection.eventId, rejection.sessionId)) {
|
|
43826
43669
|
return;
|
|
43827
43670
|
}
|
|
43828
43671
|
this.#removeInFlight(rejection.eventId);
|
|
@@ -43838,9 +43681,10 @@ var DaemonSessionEventPublisher = class {
|
|
|
43838
43681
|
return;
|
|
43839
43682
|
}
|
|
43840
43683
|
while (this.#inFlight.size < SESSION_EVENT_REPLAY_WINDOW) {
|
|
43841
|
-
const batch = this.#
|
|
43842
|
-
|
|
43843
|
-
|
|
43684
|
+
const batch = this.#queue.pending(
|
|
43685
|
+
SESSION_EVENT_REPLAY_WINDOW - this.#inFlight.size,
|
|
43686
|
+
new Set(this.#inFlight.keys())
|
|
43687
|
+
);
|
|
43844
43688
|
let sent = 0;
|
|
43845
43689
|
for (const envelope2 of batch) {
|
|
43846
43690
|
const encoded = encodeDaemonToServiceMessage(
|
|
@@ -43859,12 +43703,7 @@ var DaemonSessionEventPublisher = class {
|
|
|
43859
43703
|
return;
|
|
43860
43704
|
}
|
|
43861
43705
|
if (this.#connection.delivery === "ephemeral") {
|
|
43862
|
-
this.#
|
|
43863
|
-
eventId: envelope2.delivery.eventId,
|
|
43864
|
-
kind: "session.event.ack",
|
|
43865
|
-
outcome: { kind: "relayed" },
|
|
43866
|
-
sessionId: envelope2.sessionId
|
|
43867
|
-
});
|
|
43706
|
+
this.#queue.remove(envelope2.delivery.eventId, envelope2.sessionId);
|
|
43868
43707
|
sent += 1;
|
|
43869
43708
|
continue;
|
|
43870
43709
|
}
|
|
@@ -43977,11 +43816,7 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
|
|
|
43977
43816
|
join18(stateDirectory, "sessions", `${createHash6("sha256").update(config2.channelId).digest("hex")}.sqlite`)
|
|
43978
43817
|
);
|
|
43979
43818
|
const operations = new DaemonOperationState();
|
|
43980
|
-
const
|
|
43981
|
-
const publisher = new DaemonSessionEventPublisher(
|
|
43982
|
-
new SessionEventOutbox(join18(stateDirectory, "events", `${channelHash}.sqlite`)),
|
|
43983
|
-
timer
|
|
43984
|
-
);
|
|
43819
|
+
const publisher = new DaemonSessionEventPublisher(timer);
|
|
43985
43820
|
const activeTurns = new DaemonTurnRegistry(operations);
|
|
43986
43821
|
const recordSessionEvent = (sessionId, event) => {
|
|
43987
43822
|
publisher.publish(sessionId, activeTurns.recordEvent(event));
|