@moxt-ai/mobius 0.0.149 → 0.0.150
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 +459 -40
- package/dist/cli.js.map +4 -4
- package/dist/daemon-turn-registry.d.ts +13 -13
- package/dist/index.js +458 -39
- package/dist/index.js.map +4 -4
- package/dist/relay-connection/run-relay-connection.d.ts.map +1 -1
- package/dist/relay-connection/run-relay-connection.js +15 -2
- package/dist/relay-connection/run-relay-connection.js.map +1 -1
- package/dist/relay-connection/session-event-outbox.d.ts +19 -0
- package/dist/relay-connection/session-event-outbox.d.ts.map +1 -0
- package/dist/relay-connection/session-event-outbox.js +228 -0
- package/dist/relay-connection/session-event-outbox.js.map +1 -0
- package/dist/relay-connection/session-event-publisher.d.ts +10 -4
- package/dist/relay-connection/session-event-publisher.d.ts.map +1 -1
- package/dist/relay-connection/session-event-publisher.js +140 -32
- package/dist/relay-connection/session-event-publisher.js.map +1 -1
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +3 -1
- package/dist/run-daemon.js.map +1 -1
- package/dist/runtime.js +464 -45
- package/dist/runtime.js.map +4 -4
- package/package.json +1 -1
|
@@ -19,19 +19,6 @@ export declare class DaemonTurnRegistry {
|
|
|
19
19
|
epoch: string;
|
|
20
20
|
hasOlder: boolean;
|
|
21
21
|
interaction: Readonly<{
|
|
22
|
-
commandId: string;
|
|
23
|
-
interactionId: string;
|
|
24
|
-
kind: "session.permission.requested";
|
|
25
|
-
options: readonly Readonly<{
|
|
26
|
-
kind: "allow_always" | "allow_once" | "reject_always" | "reject_once";
|
|
27
|
-
name: string;
|
|
28
|
-
optionId: string;
|
|
29
|
-
}>[];
|
|
30
|
-
sessionId: string;
|
|
31
|
-
title: string;
|
|
32
|
-
toolCallId: string;
|
|
33
|
-
toolKind: string;
|
|
34
|
-
}> | Readonly<{
|
|
35
22
|
accepted: boolean;
|
|
36
23
|
commandId: string;
|
|
37
24
|
elicitationId: string;
|
|
@@ -63,6 +50,19 @@ export declare class DaemonTurnRegistry {
|
|
|
63
50
|
mode: "form" | "url";
|
|
64
51
|
sessionId: string;
|
|
65
52
|
url: string;
|
|
53
|
+
}> | Readonly<{
|
|
54
|
+
commandId: string;
|
|
55
|
+
interactionId: string;
|
|
56
|
+
kind: "session.permission.requested";
|
|
57
|
+
options: readonly Readonly<{
|
|
58
|
+
kind: "allow_always" | "allow_once" | "reject_always" | "reject_once";
|
|
59
|
+
name: string;
|
|
60
|
+
optionId: string;
|
|
61
|
+
}>[];
|
|
62
|
+
sessionId: string;
|
|
63
|
+
title: string;
|
|
64
|
+
toolCallId: string;
|
|
65
|
+
toolKind: string;
|
|
66
66
|
}> | null;
|
|
67
67
|
messages: readonly (Readonly<{
|
|
68
68
|
id: string;
|
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
|
-
|
|
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);
|
|
@@ -21218,12 +21226,59 @@ var encodeNativeSessionCancel = (value) => JSON.stringify(nativeSessionCancelSch
|
|
|
21218
21226
|
|
|
21219
21227
|
// ../protocol/src/session-events.ts
|
|
21220
21228
|
var SESSION_EVENT_KIND = "session.event";
|
|
21229
|
+
var SESSION_EVENT_ACK_KIND = "session.event.ack";
|
|
21230
|
+
var SESSION_EVENT_DEFERRED_KIND = "session.event.deferred";
|
|
21231
|
+
var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
|
|
21232
|
+
var SessionEventRejectionCode = {
|
|
21233
|
+
IdempotencyConflict: "idempotency_conflict",
|
|
21234
|
+
InvalidEvent: "invalid_event",
|
|
21235
|
+
RecordingRejected: "recording_rejected"
|
|
21236
|
+
};
|
|
21221
21237
|
var MAX_RECORDED_EVENT_LENGTH = 8 * 1024 * 1024;
|
|
21222
|
-
var
|
|
21238
|
+
var ephemeralEventEnvelopeSchema = external_exports.strictObject({
|
|
21223
21239
|
kind: external_exports.literal(SESSION_EVENT_KIND),
|
|
21224
21240
|
payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
|
|
21225
21241
|
sessionId: identifierStringSchema
|
|
21226
21242
|
}).readonly();
|
|
21243
|
+
var reliableDeliverySchema = external_exports.strictObject({
|
|
21244
|
+
eventId: identifierStringSchema,
|
|
21245
|
+
kind: external_exports.literal("reliable"),
|
|
21246
|
+
sourceSeq: external_exports.string().regex(/^(?:0|[1-9][0-9]*)$/u).max(40),
|
|
21247
|
+
streamId: identifierStringSchema
|
|
21248
|
+
}).readonly();
|
|
21249
|
+
var reliableEventEnvelopeSchema = external_exports.strictObject({
|
|
21250
|
+
delivery: reliableDeliverySchema,
|
|
21251
|
+
kind: external_exports.literal(SESSION_EVENT_KIND),
|
|
21252
|
+
payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
|
|
21253
|
+
sessionId: identifierStringSchema
|
|
21254
|
+
}).readonly();
|
|
21255
|
+
var eventEnvelopeSchema = external_exports.union([ephemeralEventEnvelopeSchema, reliableEventEnvelopeSchema]).readonly();
|
|
21256
|
+
var acknowledgementOutcomeSchema = external_exports.discriminatedUnion("kind", [
|
|
21257
|
+
external_exports.strictObject({ kind: external_exports.literal("relayed") }).readonly(),
|
|
21258
|
+
external_exports.strictObject({
|
|
21259
|
+
kind: external_exports.literal("recorded"),
|
|
21260
|
+
providerId: identifierStringSchema,
|
|
21261
|
+
recordingEpoch: identifierStringSchema
|
|
21262
|
+
}).readonly()
|
|
21263
|
+
]);
|
|
21264
|
+
var eventAcknowledgementSchema = external_exports.strictObject({
|
|
21265
|
+
eventId: identifierStringSchema,
|
|
21266
|
+
kind: external_exports.literal(SESSION_EVENT_ACK_KIND),
|
|
21267
|
+
outcome: acknowledgementOutcomeSchema,
|
|
21268
|
+
sessionId: identifierStringSchema
|
|
21269
|
+
}).readonly();
|
|
21270
|
+
var eventDeferredSchema = external_exports.strictObject({
|
|
21271
|
+
eventId: identifierStringSchema,
|
|
21272
|
+
kind: external_exports.literal(SESSION_EVENT_DEFERRED_KIND),
|
|
21273
|
+
retryAfterMilliseconds: external_exports.number().int().min(100).max(6e4),
|
|
21274
|
+
sessionId: identifierStringSchema
|
|
21275
|
+
}).readonly();
|
|
21276
|
+
var eventRejectedSchema = external_exports.strictObject({
|
|
21277
|
+
code: external_exports.enum(SessionEventRejectionCode),
|
|
21278
|
+
eventId: identifierStringSchema,
|
|
21279
|
+
kind: external_exports.literal(SESSION_EVENT_REJECTED_KIND),
|
|
21280
|
+
sessionId: identifierStringSchema
|
|
21281
|
+
}).readonly();
|
|
21227
21282
|
var SESSION_QUEUE_CHANGED_KIND = "session.queue.changed";
|
|
21228
21283
|
var sessionQueueChangedSchema = external_exports.strictObject({ kind: external_exports.literal(SESSION_QUEUE_CHANGED_KIND), sessionId: identifierStringSchema }).readonly();
|
|
21229
21284
|
function decode5(encoded, schema) {
|
|
@@ -21252,6 +21307,12 @@ var encode4 = (message, schema) => {
|
|
|
21252
21307
|
};
|
|
21253
21308
|
var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
|
|
21254
21309
|
var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
|
|
21310
|
+
var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
|
|
21311
|
+
var encodeSessionEventAcknowledgement = (message) => encode4(message, eventAcknowledgementSchema);
|
|
21312
|
+
var decodeSessionEventDeferred = (encoded) => decode5(encoded, eventDeferredSchema);
|
|
21313
|
+
var encodeSessionEventDeferred = (message) => encode4(message, eventDeferredSchema);
|
|
21314
|
+
var decodeSessionEventRejected = (encoded) => decode5(encoded, eventRejectedSchema);
|
|
21315
|
+
var encodeSessionEventRejected = (message) => encode4(message, eventRejectedSchema);
|
|
21255
21316
|
function decodeRecordedSessionEvent(encoded) {
|
|
21256
21317
|
const kind = decodeMessageKind(encoded);
|
|
21257
21318
|
switch (kind) {
|
|
@@ -21423,6 +21484,21 @@ var serviceToDaemonSignatures = {
|
|
|
21423
21484
|
encodeSessionElicitationRespondCommand,
|
|
21424
21485
|
decodeSessionElicitationRespondCommand
|
|
21425
21486
|
),
|
|
21487
|
+
[SESSION_EVENT_ACK_KIND]: defineMessageSignature(
|
|
21488
|
+
SESSION_EVENT_ACK_KIND,
|
|
21489
|
+
encodeSessionEventAcknowledgement,
|
|
21490
|
+
decodeSessionEventAcknowledgement
|
|
21491
|
+
),
|
|
21492
|
+
[SESSION_EVENT_DEFERRED_KIND]: defineMessageSignature(
|
|
21493
|
+
SESSION_EVENT_DEFERRED_KIND,
|
|
21494
|
+
encodeSessionEventDeferred,
|
|
21495
|
+
decodeSessionEventDeferred
|
|
21496
|
+
),
|
|
21497
|
+
[SESSION_EVENT_REJECTED_KIND]: defineMessageSignature(
|
|
21498
|
+
SESSION_EVENT_REJECTED_KIND,
|
|
21499
|
+
encodeSessionEventRejected,
|
|
21500
|
+
decodeSessionEventRejected
|
|
21501
|
+
),
|
|
21426
21502
|
[SESSION_LIVE_HISTORY_LOAD_KIND]: defineMessageSignature(
|
|
21427
21503
|
SESSION_LIVE_HISTORY_LOAD_KIND,
|
|
21428
21504
|
encodeSessionLiveHistoryLoad,
|
|
@@ -37515,7 +37591,7 @@ function isRuntimeRelayRevocation(closeCode, closeReason) {
|
|
|
37515
37591
|
}
|
|
37516
37592
|
|
|
37517
37593
|
// src/run-daemon.ts
|
|
37518
|
-
import { createHash as createHash6, randomUUID as
|
|
37594
|
+
import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
|
|
37519
37595
|
import { join as join18 } from "node:path";
|
|
37520
37596
|
import { styleText } from "node:util";
|
|
37521
37597
|
|
|
@@ -43098,6 +43174,7 @@ async function runRelayConnection({
|
|
|
43098
43174
|
}) {
|
|
43099
43175
|
const relayUrl = new URL(config2.relayUrl);
|
|
43100
43176
|
relayUrl.searchParams.set("liveness", "1");
|
|
43177
|
+
relayUrl.searchParams.set("sessionEventDelivery", "reliable-v1");
|
|
43101
43178
|
const socket = await connectRelay(relayUrl, signal);
|
|
43102
43179
|
diagnostics.connectionOpened(connectionEpoch);
|
|
43103
43180
|
const connectionController = new AbortController();
|
|
@@ -43217,6 +43294,10 @@ async function runRelayConnection({
|
|
|
43217
43294
|
const message = decodeServiceToDaemonMessage(encoded);
|
|
43218
43295
|
const kind = message.kind;
|
|
43219
43296
|
if (kind === RUNTIME_LIVENESS_READY_KIND) {
|
|
43297
|
+
publisher.connect(
|
|
43298
|
+
socket,
|
|
43299
|
+
message.sessionEventDelivery === "reliable-v1" ? "reliable" : "ephemeral"
|
|
43300
|
+
);
|
|
43220
43301
|
connectionLiveness.start();
|
|
43221
43302
|
return;
|
|
43222
43303
|
}
|
|
@@ -43224,6 +43305,18 @@ async function runRelayConnection({
|
|
|
43224
43305
|
connectionLiveness.acknowledge(message);
|
|
43225
43306
|
return;
|
|
43226
43307
|
}
|
|
43308
|
+
if (kind === SESSION_EVENT_ACK_KIND) {
|
|
43309
|
+
publisher.acknowledge(message);
|
|
43310
|
+
return;
|
|
43311
|
+
}
|
|
43312
|
+
if (kind === SESSION_EVENT_DEFERRED_KIND) {
|
|
43313
|
+
publisher.defer(message);
|
|
43314
|
+
return;
|
|
43315
|
+
}
|
|
43316
|
+
if (kind === SESSION_EVENT_REJECTED_KIND) {
|
|
43317
|
+
publisher.quarantine(message);
|
|
43318
|
+
return;
|
|
43319
|
+
}
|
|
43227
43320
|
commands.handle(message);
|
|
43228
43321
|
} catch (error62) {
|
|
43229
43322
|
console.error("Mobius daemon rejected a message", error62);
|
|
@@ -43238,7 +43331,6 @@ async function runRelayConnection({
|
|
|
43238
43331
|
socket.addEventListener("close", handleClose);
|
|
43239
43332
|
socket.addEventListener("error", handleError);
|
|
43240
43333
|
socket.addEventListener("message", handleMessage);
|
|
43241
|
-
publisher.connect(socket);
|
|
43242
43334
|
if (signal.aborted) {
|
|
43243
43335
|
stop();
|
|
43244
43336
|
}
|
|
@@ -43249,54 +43341,377 @@ async function runRelayConnection({
|
|
|
43249
43341
|
}
|
|
43250
43342
|
}
|
|
43251
43343
|
|
|
43344
|
+
// src/relay-connection/session-event-outbox.ts
|
|
43345
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
43346
|
+
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
43347
|
+
import { dirname as dirname6 } from "node:path";
|
|
43348
|
+
import { DatabaseSync } from "node:sqlite";
|
|
43349
|
+
var DEFAULT_STANDARD_EVENT_BYTES = 96 * 1024 * 1024;
|
|
43350
|
+
var DEFAULT_CRITICAL_EVENT_BYTES = 128 * 1024 * 1024;
|
|
43351
|
+
var MAX_DEAD_LETTERS = 1024;
|
|
43352
|
+
var DEFAULT_LIMITS = {
|
|
43353
|
+
criticalEventBytes: DEFAULT_CRITICAL_EVENT_BYTES,
|
|
43354
|
+
standardEventBytes: DEFAULT_STANDARD_EVENT_BYTES
|
|
43355
|
+
};
|
|
43356
|
+
var SessionEventOutboxCapacityError = class extends Error {
|
|
43357
|
+
code = "session_event_outbox_full";
|
|
43358
|
+
constructor() {
|
|
43359
|
+
super("The persistent Session event outbox is full");
|
|
43360
|
+
this.name = "SessionEventOutboxCapacityError";
|
|
43361
|
+
}
|
|
43362
|
+
};
|
|
43363
|
+
function rowNumber(row, field) {
|
|
43364
|
+
const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
|
|
43365
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
43366
|
+
throw new Error(`SQLite returned an invalid ${field} value`);
|
|
43367
|
+
}
|
|
43368
|
+
return value;
|
|
43369
|
+
}
|
|
43370
|
+
function rowString(row, field) {
|
|
43371
|
+
const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
|
|
43372
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
43373
|
+
throw new Error(`SQLite returned an invalid ${field} value`);
|
|
43374
|
+
}
|
|
43375
|
+
return value;
|
|
43376
|
+
}
|
|
43377
|
+
function validateLimits(limits) {
|
|
43378
|
+
if (!Number.isSafeInteger(limits.standardEventBytes) || limits.standardEventBytes < 1 || !Number.isSafeInteger(limits.criticalEventBytes) || limits.criticalEventBytes < limits.standardEventBytes) {
|
|
43379
|
+
throw new Error("Session event outbox limits are invalid");
|
|
43380
|
+
}
|
|
43381
|
+
}
|
|
43382
|
+
var SessionEventOutbox = class {
|
|
43383
|
+
#database;
|
|
43384
|
+
#limits;
|
|
43385
|
+
constructor(path2, limits = DEFAULT_LIMITS) {
|
|
43386
|
+
validateLimits(limits);
|
|
43387
|
+
mkdirSync3(dirname6(path2), { mode: 448, recursive: true });
|
|
43388
|
+
this.#limits = limits;
|
|
43389
|
+
this.#database = new DatabaseSync(path2);
|
|
43390
|
+
this.#database.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL; PRAGMA busy_timeout = 5000;");
|
|
43391
|
+
this.#database.exec(`
|
|
43392
|
+
CREATE TABLE IF NOT EXISTS relay_session_event_outbox (
|
|
43393
|
+
ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
43394
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
43395
|
+
session_id TEXT NOT NULL,
|
|
43396
|
+
source_seq INTEGER NOT NULL UNIQUE,
|
|
43397
|
+
byte_length INTEGER NOT NULL,
|
|
43398
|
+
envelope_json TEXT NOT NULL
|
|
43399
|
+
) STRICT;
|
|
43400
|
+
CREATE INDEX IF NOT EXISTS relay_session_event_outbox_order
|
|
43401
|
+
ON relay_session_event_outbox(source_seq);
|
|
43402
|
+
CREATE TABLE IF NOT EXISTS relay_session_event_outbox_state (
|
|
43403
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
43404
|
+
stream_id TEXT NOT NULL,
|
|
43405
|
+
next_source_seq INTEGER NOT NULL,
|
|
43406
|
+
total_bytes INTEGER NOT NULL
|
|
43407
|
+
) STRICT;
|
|
43408
|
+
CREATE TABLE IF NOT EXISTS relay_session_event_dead_letters (
|
|
43409
|
+
ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
43410
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
43411
|
+
session_id TEXT NOT NULL,
|
|
43412
|
+
rejection_code TEXT NOT NULL,
|
|
43413
|
+
envelope_json TEXT NOT NULL
|
|
43414
|
+
) STRICT;
|
|
43415
|
+
`);
|
|
43416
|
+
this.#database.prepare(
|
|
43417
|
+
`INSERT OR IGNORE INTO relay_session_event_outbox_state (
|
|
43418
|
+
singleton, stream_id, next_source_seq, total_bytes
|
|
43419
|
+
) VALUES (1, ?, 1, 0)`
|
|
43420
|
+
).run(randomUUID7());
|
|
43421
|
+
}
|
|
43422
|
+
acknowledge = (acknowledgement) => this.#remove(acknowledgement.eventId, acknowledgement.sessionId);
|
|
43423
|
+
close = () => {
|
|
43424
|
+
this.#database.close();
|
|
43425
|
+
};
|
|
43426
|
+
enqueue = (sessionId, payload, critical) => {
|
|
43427
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
43428
|
+
try {
|
|
43429
|
+
const state = this.#database.prepare(
|
|
43430
|
+
`SELECT stream_id, next_source_seq, total_bytes
|
|
43431
|
+
FROM relay_session_event_outbox_state
|
|
43432
|
+
WHERE singleton = 1`
|
|
43433
|
+
).get();
|
|
43434
|
+
const sourceSeq = rowNumber(state, "next_source_seq");
|
|
43435
|
+
if (sourceSeq < 1 || !Number.isSafeInteger(sourceSeq + 1)) {
|
|
43436
|
+
throw new Error("The Session event source sequence is exhausted");
|
|
43437
|
+
}
|
|
43438
|
+
const envelope2 = {
|
|
43439
|
+
delivery: {
|
|
43440
|
+
eventId: randomUUID7(),
|
|
43441
|
+
kind: "reliable",
|
|
43442
|
+
sourceSeq: sourceSeq.toString(),
|
|
43443
|
+
streamId: rowString(state, "stream_id")
|
|
43444
|
+
},
|
|
43445
|
+
kind: SESSION_EVENT_KIND,
|
|
43446
|
+
payload,
|
|
43447
|
+
sessionId
|
|
43448
|
+
};
|
|
43449
|
+
const encoded = encodeSessionEventEnvelope(envelope2);
|
|
43450
|
+
const byteLength = Buffer.byteLength(encoded, "utf8");
|
|
43451
|
+
const maximumBytes = critical ? this.#limits.criticalEventBytes : this.#limits.standardEventBytes;
|
|
43452
|
+
if (rowNumber(state, "total_bytes") + byteLength > maximumBytes) {
|
|
43453
|
+
throw new SessionEventOutboxCapacityError();
|
|
43454
|
+
}
|
|
43455
|
+
this.#database.prepare(
|
|
43456
|
+
`INSERT INTO relay_session_event_outbox (
|
|
43457
|
+
event_id, session_id, source_seq, byte_length, envelope_json
|
|
43458
|
+
) VALUES (?, ?, ?, ?, ?)`
|
|
43459
|
+
).run(envelope2.delivery.eventId, sessionId, sourceSeq, byteLength, encoded);
|
|
43460
|
+
this.#database.prepare(
|
|
43461
|
+
`UPDATE relay_session_event_outbox_state
|
|
43462
|
+
SET next_source_seq = next_source_seq + 1,
|
|
43463
|
+
total_bytes = total_bytes + ?
|
|
43464
|
+
WHERE singleton = 1`
|
|
43465
|
+
).run(byteLength);
|
|
43466
|
+
this.#database.exec("COMMIT");
|
|
43467
|
+
return envelope2;
|
|
43468
|
+
} catch (error62) {
|
|
43469
|
+
this.#database.exec("ROLLBACK");
|
|
43470
|
+
throw error62;
|
|
43471
|
+
}
|
|
43472
|
+
};
|
|
43473
|
+
pending = (limit, excludedEventIds) => {
|
|
43474
|
+
if (!Number.isSafeInteger(limit) || limit < 1) {
|
|
43475
|
+
throw new Error("Session event replay limit must be a positive integer");
|
|
43476
|
+
}
|
|
43477
|
+
const exclusion = excludedEventIds.length === 0 ? "" : `WHERE event_id NOT IN (${excludedEventIds.map(() => "?").join(", ")})`;
|
|
43478
|
+
return this.#database.prepare(
|
|
43479
|
+
`SELECT envelope_json
|
|
43480
|
+
FROM relay_session_event_outbox
|
|
43481
|
+
${exclusion}
|
|
43482
|
+
ORDER BY source_seq
|
|
43483
|
+
LIMIT ?`
|
|
43484
|
+
).all(...excludedEventIds, limit).map((row) => {
|
|
43485
|
+
const envelope2 = decodeSessionEventEnvelope(rowString(row, "envelope_json"));
|
|
43486
|
+
if (!("delivery" in envelope2)) {
|
|
43487
|
+
throw new Error("SQLite returned an invalid reliable Session event envelope");
|
|
43488
|
+
}
|
|
43489
|
+
return envelope2;
|
|
43490
|
+
});
|
|
43491
|
+
};
|
|
43492
|
+
quarantine = (rejection) => {
|
|
43493
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
43494
|
+
try {
|
|
43495
|
+
const stored = this.#database.prepare(
|
|
43496
|
+
`SELECT byte_length, envelope_json
|
|
43497
|
+
FROM relay_session_event_outbox
|
|
43498
|
+
WHERE event_id = ? AND session_id = ?`
|
|
43499
|
+
).get(rejection.eventId, rejection.sessionId);
|
|
43500
|
+
if (stored === void 0) {
|
|
43501
|
+
this.#database.exec("COMMIT");
|
|
43502
|
+
return false;
|
|
43503
|
+
}
|
|
43504
|
+
this.#database.prepare(
|
|
43505
|
+
`INSERT INTO relay_session_event_dead_letters (
|
|
43506
|
+
event_id, session_id, rejection_code, envelope_json
|
|
43507
|
+
) VALUES (?, ?, ?, ?)`
|
|
43508
|
+
).run(rejection.eventId, rejection.sessionId, rejection.code, rowString(stored, "envelope_json"));
|
|
43509
|
+
this.#deleteStored(rejection.eventId, rejection.sessionId, rowNumber(stored, "byte_length"));
|
|
43510
|
+
this.#database.prepare(
|
|
43511
|
+
`DELETE FROM relay_session_event_dead_letters
|
|
43512
|
+
WHERE ordinal NOT IN (
|
|
43513
|
+
SELECT ordinal FROM relay_session_event_dead_letters
|
|
43514
|
+
ORDER BY ordinal DESC LIMIT ?
|
|
43515
|
+
)`
|
|
43516
|
+
).run(MAX_DEAD_LETTERS);
|
|
43517
|
+
this.#database.exec("COMMIT");
|
|
43518
|
+
return true;
|
|
43519
|
+
} catch (error62) {
|
|
43520
|
+
this.#database.exec("ROLLBACK");
|
|
43521
|
+
throw error62;
|
|
43522
|
+
}
|
|
43523
|
+
};
|
|
43524
|
+
#deleteStored = (eventId, sessionId, byteLength) => {
|
|
43525
|
+
const deletion = this.#database.prepare(
|
|
43526
|
+
`DELETE FROM relay_session_event_outbox
|
|
43527
|
+
WHERE event_id = ? AND session_id = ?`
|
|
43528
|
+
).run(eventId, sessionId);
|
|
43529
|
+
if (deletion.changes !== 1) {
|
|
43530
|
+
throw new Error("The Session event could not be removed from the outbox");
|
|
43531
|
+
}
|
|
43532
|
+
const state = this.#database.prepare(
|
|
43533
|
+
`UPDATE relay_session_event_outbox_state
|
|
43534
|
+
SET total_bytes = total_bytes - ?
|
|
43535
|
+
WHERE singleton = 1 AND total_bytes >= ?`
|
|
43536
|
+
).run(byteLength, byteLength);
|
|
43537
|
+
if (state.changes !== 1) {
|
|
43538
|
+
throw new Error("The Session event outbox capacity state is inconsistent");
|
|
43539
|
+
}
|
|
43540
|
+
};
|
|
43541
|
+
#remove = (eventId, sessionId) => {
|
|
43542
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
43543
|
+
try {
|
|
43544
|
+
const stored = this.#database.prepare(
|
|
43545
|
+
`SELECT byte_length
|
|
43546
|
+
FROM relay_session_event_outbox
|
|
43547
|
+
WHERE event_id = ? AND session_id = ?`
|
|
43548
|
+
).get(eventId, sessionId);
|
|
43549
|
+
if (stored === void 0) {
|
|
43550
|
+
this.#database.exec("COMMIT");
|
|
43551
|
+
return false;
|
|
43552
|
+
}
|
|
43553
|
+
this.#deleteStored(eventId, sessionId, rowNumber(stored, "byte_length"));
|
|
43554
|
+
this.#database.exec("COMMIT");
|
|
43555
|
+
return true;
|
|
43556
|
+
} catch (error62) {
|
|
43557
|
+
this.#database.exec("ROLLBACK");
|
|
43558
|
+
throw error62;
|
|
43559
|
+
}
|
|
43560
|
+
};
|
|
43561
|
+
};
|
|
43562
|
+
|
|
43252
43563
|
// src/relay-connection/session-event-publisher.ts
|
|
43253
43564
|
import { WebSocket as WebSocket9 } from "undici";
|
|
43254
|
-
var
|
|
43565
|
+
var SESSION_EVENT_REPLAY_WINDOW = 16;
|
|
43566
|
+
var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
|
|
43567
|
+
function reservesSessionEventCapacity(event) {
|
|
43568
|
+
if (event.kind === SESSION_COMPLETED_KIND || event.kind === SESSION_FAILED_KIND) {
|
|
43569
|
+
return true;
|
|
43570
|
+
}
|
|
43571
|
+
return event.kind === AGENT_ACTIVITY_KIND && (event.activityType === "progress" || event.activityType === "reasoning") && (event.status === "completed" || event.status === "failed");
|
|
43572
|
+
}
|
|
43255
43573
|
var DaemonSessionEventPublisher = class {
|
|
43256
|
-
#
|
|
43257
|
-
#
|
|
43258
|
-
|
|
43259
|
-
|
|
43260
|
-
|
|
43574
|
+
#inFlight = /* @__PURE__ */ new Map();
|
|
43575
|
+
#outbox;
|
|
43576
|
+
#reportedCapacityKinds = /* @__PURE__ */ new Set();
|
|
43577
|
+
#timer;
|
|
43578
|
+
#connection = { kind: "disconnected" };
|
|
43579
|
+
#deferredReplay = null;
|
|
43580
|
+
#inFlightBytes = 0;
|
|
43581
|
+
constructor(outbox, timer) {
|
|
43582
|
+
this.#outbox = outbox;
|
|
43583
|
+
this.#timer = timer;
|
|
43584
|
+
}
|
|
43585
|
+
acknowledge = (acknowledgement) => {
|
|
43586
|
+
if (!this.#outbox.acknowledge(acknowledgement)) {
|
|
43587
|
+
return;
|
|
43588
|
+
}
|
|
43589
|
+
this.#removeInFlight(acknowledgement.eventId);
|
|
43590
|
+
this.#publishNextBatch();
|
|
43591
|
+
};
|
|
43592
|
+
close = () => {
|
|
43593
|
+
this.#deferredReplay?.cancel();
|
|
43594
|
+
this.#deferredReplay = null;
|
|
43595
|
+
this.#connection = { kind: "disconnected" };
|
|
43596
|
+
this.#clearInFlight();
|
|
43597
|
+
this.#outbox.close();
|
|
43598
|
+
};
|
|
43599
|
+
connect = (socket, delivery) => {
|
|
43600
|
+
this.#connection = { delivery, kind: "connected", socket };
|
|
43601
|
+
this.#clearInFlight();
|
|
43602
|
+
this.#publishNextBatch();
|
|
43603
|
+
};
|
|
43604
|
+
defer = (deferred) => {
|
|
43605
|
+
if (!this.#inFlight.has(deferred.eventId)) {
|
|
43606
|
+
return;
|
|
43607
|
+
}
|
|
43608
|
+
this.#clearInFlight();
|
|
43609
|
+
if (this.#deferredReplay !== null) {
|
|
43610
|
+
return;
|
|
43611
|
+
}
|
|
43612
|
+
this.#deferredReplay = this.#timer.schedule(() => {
|
|
43613
|
+
this.#deferredReplay = null;
|
|
43614
|
+
this.#publishNextBatch();
|
|
43615
|
+
}, deferred.retryAfterMilliseconds);
|
|
43261
43616
|
};
|
|
43262
43617
|
disconnect = (socket) => {
|
|
43263
|
-
if (this.#socket
|
|
43264
|
-
|
|
43618
|
+
if (this.#connection.kind !== "connected" || this.#connection.socket !== socket) {
|
|
43619
|
+
return;
|
|
43265
43620
|
}
|
|
43621
|
+
this.#connection = { kind: "disconnected" };
|
|
43622
|
+
this.#clearInFlight();
|
|
43266
43623
|
};
|
|
43267
43624
|
publish = (sessionId, event) => {
|
|
43268
|
-
|
|
43269
|
-
|
|
43270
|
-
|
|
43625
|
+
try {
|
|
43626
|
+
this.#outbox.enqueue(sessionId, encodeRecordedSessionEvent(event), reservesSessionEventCapacity(event));
|
|
43627
|
+
} catch (error62) {
|
|
43628
|
+
if (!(error62 instanceof SessionEventOutboxCapacityError)) {
|
|
43629
|
+
throw error62;
|
|
43630
|
+
}
|
|
43631
|
+
if (!this.#reportedCapacityKinds.has(event.kind)) {
|
|
43632
|
+
this.#reportedCapacityKinds.add(event.kind);
|
|
43633
|
+
console.warn(
|
|
43634
|
+
`Mobius daemon could not persist "${event.kind}" because the Session event outbox is full`
|
|
43635
|
+
);
|
|
43636
|
+
}
|
|
43271
43637
|
return;
|
|
43272
43638
|
}
|
|
43273
|
-
|
|
43274
|
-
|
|
43275
|
-
|
|
43276
|
-
|
|
43639
|
+
this.#publishNextBatch();
|
|
43640
|
+
};
|
|
43641
|
+
quarantine = (rejection) => {
|
|
43642
|
+
if (!this.#outbox.quarantine(rejection)) {
|
|
43277
43643
|
return;
|
|
43278
43644
|
}
|
|
43279
|
-
|
|
43280
|
-
|
|
43281
|
-
|
|
43282
|
-
kind: SESSION_EVENT_KIND,
|
|
43283
|
-
payload: encodeRecordedSessionEvent(event),
|
|
43284
|
-
sessionId
|
|
43285
|
-
})
|
|
43286
|
-
);
|
|
43287
|
-
} catch {
|
|
43288
|
-
this.#reportDrop();
|
|
43289
|
-
}
|
|
43645
|
+
this.#removeInFlight(rejection.eventId);
|
|
43646
|
+
console.error(`Mobius Relay rejected Session event ${rejection.eventId} with code ${rejection.code}`);
|
|
43647
|
+
this.#publishNextBatch();
|
|
43290
43648
|
};
|
|
43291
|
-
|
|
43292
|
-
this.#
|
|
43649
|
+
#clearInFlight = () => {
|
|
43650
|
+
this.#inFlight.clear();
|
|
43651
|
+
this.#inFlightBytes = 0;
|
|
43652
|
+
};
|
|
43653
|
+
#publishNextBatch = () => {
|
|
43654
|
+
if (this.#deferredReplay !== null || this.#connection.kind !== "connected") {
|
|
43655
|
+
return;
|
|
43656
|
+
}
|
|
43657
|
+
while (this.#inFlight.size < SESSION_EVENT_REPLAY_WINDOW) {
|
|
43658
|
+
const batch = this.#outbox.pending(SESSION_EVENT_REPLAY_WINDOW - this.#inFlight.size, [
|
|
43659
|
+
...this.#inFlight.keys()
|
|
43660
|
+
]);
|
|
43661
|
+
let sent = 0;
|
|
43662
|
+
for (const envelope2 of batch) {
|
|
43663
|
+
const encoded = encodeDaemonToServiceMessage(
|
|
43664
|
+
this.#connection.delivery === "reliable" ? envelope2 : {
|
|
43665
|
+
kind: envelope2.kind,
|
|
43666
|
+
payload: envelope2.payload,
|
|
43667
|
+
sessionId: envelope2.sessionId
|
|
43668
|
+
}
|
|
43669
|
+
);
|
|
43670
|
+
const byteLength = Buffer.byteLength(encoded, "utf8");
|
|
43671
|
+
if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
|
|
43672
|
+
return;
|
|
43673
|
+
}
|
|
43674
|
+
if (!this.#send(encoded)) {
|
|
43675
|
+
this.#clearInFlight();
|
|
43676
|
+
return;
|
|
43677
|
+
}
|
|
43678
|
+
if (this.#connection.delivery === "ephemeral") {
|
|
43679
|
+
this.#outbox.acknowledge({
|
|
43680
|
+
eventId: envelope2.delivery.eventId,
|
|
43681
|
+
kind: "session.event.ack",
|
|
43682
|
+
outcome: { kind: "relayed" },
|
|
43683
|
+
sessionId: envelope2.sessionId
|
|
43684
|
+
});
|
|
43685
|
+
sent += 1;
|
|
43686
|
+
continue;
|
|
43687
|
+
}
|
|
43688
|
+
this.#inFlight.set(envelope2.delivery.eventId, { byteLength });
|
|
43689
|
+
this.#inFlightBytes += byteLength;
|
|
43690
|
+
sent += 1;
|
|
43691
|
+
}
|
|
43692
|
+
if (sent === 0) {
|
|
43693
|
+
return;
|
|
43694
|
+
}
|
|
43695
|
+
}
|
|
43293
43696
|
};
|
|
43294
|
-
#
|
|
43295
|
-
|
|
43697
|
+
#removeInFlight = (eventId) => {
|
|
43698
|
+
const inFlight = this.#inFlight.get(eventId);
|
|
43699
|
+
if (inFlight === void 0) {
|
|
43296
43700
|
return;
|
|
43297
43701
|
}
|
|
43298
|
-
this.#
|
|
43299
|
-
|
|
43702
|
+
this.#inFlight.delete(eventId);
|
|
43703
|
+
this.#inFlightBytes -= inFlight.byteLength;
|
|
43704
|
+
};
|
|
43705
|
+
#send = (encoded) => {
|
|
43706
|
+
if (this.#connection.kind !== "connected" || this.#connection.socket.readyState !== WebSocket9.OPEN) {
|
|
43707
|
+
return false;
|
|
43708
|
+
}
|
|
43709
|
+
try {
|
|
43710
|
+
this.#connection.socket.send(encoded);
|
|
43711
|
+
return true;
|
|
43712
|
+
} catch {
|
|
43713
|
+
return false;
|
|
43714
|
+
}
|
|
43300
43715
|
};
|
|
43301
43716
|
};
|
|
43302
43717
|
|
|
@@ -43357,7 +43772,7 @@ async function installDetectedAgentIntegrations(config2, installations, signal)
|
|
|
43357
43772
|
{
|
|
43358
43773
|
agentId: agent.id,
|
|
43359
43774
|
kind: AGENT_INSTALL_REQUEST_KIND,
|
|
43360
|
-
requestId: `automatic-integration-${
|
|
43775
|
+
requestId: `automatic-integration-${randomUUID8()}`
|
|
43361
43776
|
},
|
|
43362
43777
|
signal
|
|
43363
43778
|
);
|
|
@@ -43379,7 +43794,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
|
|
|
43379
43794
|
join18(stateDirectory, "sessions", `${createHash6("sha256").update(config2.channelId).digest("hex")}.sqlite`)
|
|
43380
43795
|
);
|
|
43381
43796
|
const operations = new DaemonOperationState();
|
|
43382
|
-
const
|
|
43797
|
+
const channelHash = createHash6("sha256").update(config2.channelId).digest("hex");
|
|
43798
|
+
const publisher = new DaemonSessionEventPublisher(
|
|
43799
|
+
new SessionEventOutbox(join18(stateDirectory, "events", `${channelHash}.sqlite`)),
|
|
43800
|
+
timer
|
|
43801
|
+
);
|
|
43383
43802
|
const activeTurns = new DaemonTurnRegistry(operations);
|
|
43384
43803
|
const recordSessionEvent = (sessionId, event) => {
|
|
43385
43804
|
publisher.publish(sessionId, activeTurns.recordEvent(event));
|