@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
package/dist/cli.js
CHANGED
|
@@ -3381,7 +3381,7 @@ var program = new Command();
|
|
|
3381
3381
|
// package.json
|
|
3382
3382
|
var package_default = {
|
|
3383
3383
|
name: "@moxt-ai/mobius",
|
|
3384
|
-
version: "0.0.
|
|
3384
|
+
version: "0.0.150",
|
|
3385
3385
|
description: "Mobius CLI for pairing and running local AI agent services.",
|
|
3386
3386
|
type: "module",
|
|
3387
3387
|
bin: {
|
|
@@ -29330,9 +29330,17 @@ function decodeSessionQueuedEvent(encoded) {
|
|
|
29330
29330
|
}
|
|
29331
29331
|
function decodeRuntimeLivenessReadyEvent(encoded) {
|
|
29332
29332
|
const value = parseJson2(encoded);
|
|
29333
|
-
assertExactKeys(value, ["kind"]);
|
|
29334
29333
|
assertKind(value, RUNTIME_LIVENESS_READY_KIND);
|
|
29335
|
-
|
|
29334
|
+
if (value["sessionEventDelivery"] === void 0) {
|
|
29335
|
+
assertExactKeys(value, ["kind"]);
|
|
29336
|
+
return { kind: RUNTIME_LIVENESS_READY_KIND };
|
|
29337
|
+
}
|
|
29338
|
+
assertExactKeys(value, ["kind", "sessionEventDelivery"]);
|
|
29339
|
+
const sessionEventDelivery = readString2(value, "sessionEventDelivery", 32);
|
|
29340
|
+
if (sessionEventDelivery !== "reliable-v1") {
|
|
29341
|
+
throw new ProtocolMessageError("Unsupported Session event delivery mode");
|
|
29342
|
+
}
|
|
29343
|
+
return { kind: RUNTIME_LIVENESS_READY_KIND, sessionEventDelivery };
|
|
29336
29344
|
}
|
|
29337
29345
|
function decodeRuntimeHeartbeatEvent(encoded) {
|
|
29338
29346
|
const value = parseJson2(encoded);
|
|
@@ -29782,12 +29790,59 @@ var encodeNativeSessionCancel = (value) => JSON.stringify(nativeSessionCancelSch
|
|
|
29782
29790
|
|
|
29783
29791
|
// ../protocol/src/session-events.ts
|
|
29784
29792
|
var SESSION_EVENT_KIND = "session.event";
|
|
29793
|
+
var SESSION_EVENT_ACK_KIND = "session.event.ack";
|
|
29794
|
+
var SESSION_EVENT_DEFERRED_KIND = "session.event.deferred";
|
|
29795
|
+
var SESSION_EVENT_REJECTED_KIND = "session.event.rejected";
|
|
29796
|
+
var SessionEventRejectionCode = {
|
|
29797
|
+
IdempotencyConflict: "idempotency_conflict",
|
|
29798
|
+
InvalidEvent: "invalid_event",
|
|
29799
|
+
RecordingRejected: "recording_rejected"
|
|
29800
|
+
};
|
|
29785
29801
|
var MAX_RECORDED_EVENT_LENGTH = 8 * 1024 * 1024;
|
|
29786
|
-
var
|
|
29802
|
+
var ephemeralEventEnvelopeSchema = external_exports.strictObject({
|
|
29787
29803
|
kind: external_exports.literal(SESSION_EVENT_KIND),
|
|
29788
29804
|
payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
|
|
29789
29805
|
sessionId: identifierStringSchema
|
|
29790
29806
|
}).readonly();
|
|
29807
|
+
var reliableDeliverySchema = external_exports.strictObject({
|
|
29808
|
+
eventId: identifierStringSchema,
|
|
29809
|
+
kind: external_exports.literal("reliable"),
|
|
29810
|
+
sourceSeq: external_exports.string().regex(/^(?:0|[1-9][0-9]*)$/u).max(40),
|
|
29811
|
+
streamId: identifierStringSchema
|
|
29812
|
+
}).readonly();
|
|
29813
|
+
var reliableEventEnvelopeSchema = external_exports.strictObject({
|
|
29814
|
+
delivery: reliableDeliverySchema,
|
|
29815
|
+
kind: external_exports.literal(SESSION_EVENT_KIND),
|
|
29816
|
+
payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
|
|
29817
|
+
sessionId: identifierStringSchema
|
|
29818
|
+
}).readonly();
|
|
29819
|
+
var eventEnvelopeSchema = external_exports.union([ephemeralEventEnvelopeSchema, reliableEventEnvelopeSchema]).readonly();
|
|
29820
|
+
var acknowledgementOutcomeSchema = external_exports.discriminatedUnion("kind", [
|
|
29821
|
+
external_exports.strictObject({ kind: external_exports.literal("relayed") }).readonly(),
|
|
29822
|
+
external_exports.strictObject({
|
|
29823
|
+
kind: external_exports.literal("recorded"),
|
|
29824
|
+
providerId: identifierStringSchema,
|
|
29825
|
+
recordingEpoch: identifierStringSchema
|
|
29826
|
+
}).readonly()
|
|
29827
|
+
]);
|
|
29828
|
+
var eventAcknowledgementSchema = external_exports.strictObject({
|
|
29829
|
+
eventId: identifierStringSchema,
|
|
29830
|
+
kind: external_exports.literal(SESSION_EVENT_ACK_KIND),
|
|
29831
|
+
outcome: acknowledgementOutcomeSchema,
|
|
29832
|
+
sessionId: identifierStringSchema
|
|
29833
|
+
}).readonly();
|
|
29834
|
+
var eventDeferredSchema = external_exports.strictObject({
|
|
29835
|
+
eventId: identifierStringSchema,
|
|
29836
|
+
kind: external_exports.literal(SESSION_EVENT_DEFERRED_KIND),
|
|
29837
|
+
retryAfterMilliseconds: external_exports.number().int().min(100).max(6e4),
|
|
29838
|
+
sessionId: identifierStringSchema
|
|
29839
|
+
}).readonly();
|
|
29840
|
+
var eventRejectedSchema = external_exports.strictObject({
|
|
29841
|
+
code: external_exports.enum(SessionEventRejectionCode),
|
|
29842
|
+
eventId: identifierStringSchema,
|
|
29843
|
+
kind: external_exports.literal(SESSION_EVENT_REJECTED_KIND),
|
|
29844
|
+
sessionId: identifierStringSchema
|
|
29845
|
+
}).readonly();
|
|
29791
29846
|
var SESSION_QUEUE_CHANGED_KIND = "session.queue.changed";
|
|
29792
29847
|
var sessionQueueChangedSchema = external_exports.strictObject({ kind: external_exports.literal(SESSION_QUEUE_CHANGED_KIND), sessionId: identifierStringSchema }).readonly();
|
|
29793
29848
|
function decode5(encoded, schema) {
|
|
@@ -29816,6 +29871,12 @@ var encode4 = (message, schema) => {
|
|
|
29816
29871
|
};
|
|
29817
29872
|
var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
|
|
29818
29873
|
var encodeSessionEventEnvelope = (message) => encode4(message, eventEnvelopeSchema);
|
|
29874
|
+
var decodeSessionEventAcknowledgement = (encoded) => decode5(encoded, eventAcknowledgementSchema);
|
|
29875
|
+
var encodeSessionEventAcknowledgement = (message) => encode4(message, eventAcknowledgementSchema);
|
|
29876
|
+
var decodeSessionEventDeferred = (encoded) => decode5(encoded, eventDeferredSchema);
|
|
29877
|
+
var encodeSessionEventDeferred = (message) => encode4(message, eventDeferredSchema);
|
|
29878
|
+
var decodeSessionEventRejected = (encoded) => decode5(encoded, eventRejectedSchema);
|
|
29879
|
+
var encodeSessionEventRejected = (message) => encode4(message, eventRejectedSchema);
|
|
29819
29880
|
function decodeRecordedSessionEvent(encoded) {
|
|
29820
29881
|
const kind = decodeMessageKind(encoded);
|
|
29821
29882
|
switch (kind) {
|
|
@@ -29987,6 +30048,21 @@ var serviceToDaemonSignatures = {
|
|
|
29987
30048
|
encodeSessionElicitationRespondCommand,
|
|
29988
30049
|
decodeSessionElicitationRespondCommand
|
|
29989
30050
|
),
|
|
30051
|
+
[SESSION_EVENT_ACK_KIND]: defineMessageSignature(
|
|
30052
|
+
SESSION_EVENT_ACK_KIND,
|
|
30053
|
+
encodeSessionEventAcknowledgement,
|
|
30054
|
+
decodeSessionEventAcknowledgement
|
|
30055
|
+
),
|
|
30056
|
+
[SESSION_EVENT_DEFERRED_KIND]: defineMessageSignature(
|
|
30057
|
+
SESSION_EVENT_DEFERRED_KIND,
|
|
30058
|
+
encodeSessionEventDeferred,
|
|
30059
|
+
decodeSessionEventDeferred
|
|
30060
|
+
),
|
|
30061
|
+
[SESSION_EVENT_REJECTED_KIND]: defineMessageSignature(
|
|
30062
|
+
SESSION_EVENT_REJECTED_KIND,
|
|
30063
|
+
encodeSessionEventRejected,
|
|
30064
|
+
decodeSessionEventRejected
|
|
30065
|
+
),
|
|
29990
30066
|
[SESSION_LIVE_HISTORY_LOAD_KIND]: defineMessageSignature(
|
|
29991
30067
|
SESSION_LIVE_HISTORY_LOAD_KIND,
|
|
29992
30068
|
encodeSessionLiveHistoryLoad,
|
|
@@ -31045,7 +31121,7 @@ var AuditStore = class {
|
|
|
31045
31121
|
};
|
|
31046
31122
|
|
|
31047
31123
|
// src/run-daemon.ts
|
|
31048
|
-
import { createHash as createHash6, randomUUID as
|
|
31124
|
+
import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
|
|
31049
31125
|
import { join as join18 } from "node:path";
|
|
31050
31126
|
import { styleText } from "node:util";
|
|
31051
31127
|
|
|
@@ -46594,6 +46670,7 @@ async function runRelayConnection({
|
|
|
46594
46670
|
}) {
|
|
46595
46671
|
const relayUrl = new URL(config2.relayUrl);
|
|
46596
46672
|
relayUrl.searchParams.set("liveness", "1");
|
|
46673
|
+
relayUrl.searchParams.set("sessionEventDelivery", "reliable-v1");
|
|
46597
46674
|
const socket = await connectRelay(relayUrl, signal);
|
|
46598
46675
|
diagnostics.connectionOpened(connectionEpoch);
|
|
46599
46676
|
const connectionController = new AbortController();
|
|
@@ -46713,6 +46790,10 @@ async function runRelayConnection({
|
|
|
46713
46790
|
const message = decodeServiceToDaemonMessage(encoded);
|
|
46714
46791
|
const kind = message.kind;
|
|
46715
46792
|
if (kind === RUNTIME_LIVENESS_READY_KIND) {
|
|
46793
|
+
publisher.connect(
|
|
46794
|
+
socket,
|
|
46795
|
+
message.sessionEventDelivery === "reliable-v1" ? "reliable" : "ephemeral"
|
|
46796
|
+
);
|
|
46716
46797
|
connectionLiveness.start();
|
|
46717
46798
|
return;
|
|
46718
46799
|
}
|
|
@@ -46720,6 +46801,18 @@ async function runRelayConnection({
|
|
|
46720
46801
|
connectionLiveness.acknowledge(message);
|
|
46721
46802
|
return;
|
|
46722
46803
|
}
|
|
46804
|
+
if (kind === SESSION_EVENT_ACK_KIND) {
|
|
46805
|
+
publisher.acknowledge(message);
|
|
46806
|
+
return;
|
|
46807
|
+
}
|
|
46808
|
+
if (kind === SESSION_EVENT_DEFERRED_KIND) {
|
|
46809
|
+
publisher.defer(message);
|
|
46810
|
+
return;
|
|
46811
|
+
}
|
|
46812
|
+
if (kind === SESSION_EVENT_REJECTED_KIND) {
|
|
46813
|
+
publisher.quarantine(message);
|
|
46814
|
+
return;
|
|
46815
|
+
}
|
|
46723
46816
|
commands.handle(message);
|
|
46724
46817
|
} catch (error62) {
|
|
46725
46818
|
console.error("Mobius daemon rejected a message", error62);
|
|
@@ -46734,7 +46827,6 @@ async function runRelayConnection({
|
|
|
46734
46827
|
socket.addEventListener("close", handleClose);
|
|
46735
46828
|
socket.addEventListener("error", handleError);
|
|
46736
46829
|
socket.addEventListener("message", handleMessage);
|
|
46737
|
-
publisher.connect(socket);
|
|
46738
46830
|
if (signal.aborted) {
|
|
46739
46831
|
stop();
|
|
46740
46832
|
}
|
|
@@ -46745,54 +46837,377 @@ async function runRelayConnection({
|
|
|
46745
46837
|
}
|
|
46746
46838
|
}
|
|
46747
46839
|
|
|
46840
|
+
// src/relay-connection/session-event-outbox.ts
|
|
46841
|
+
import { randomUUID as randomUUID7 } from "node:crypto";
|
|
46842
|
+
import { mkdirSync as mkdirSync3 } from "node:fs";
|
|
46843
|
+
import { dirname as dirname6 } from "node:path";
|
|
46844
|
+
import { DatabaseSync } from "node:sqlite";
|
|
46845
|
+
var DEFAULT_STANDARD_EVENT_BYTES = 96 * 1024 * 1024;
|
|
46846
|
+
var DEFAULT_CRITICAL_EVENT_BYTES = 128 * 1024 * 1024;
|
|
46847
|
+
var MAX_DEAD_LETTERS = 1024;
|
|
46848
|
+
var DEFAULT_LIMITS = {
|
|
46849
|
+
criticalEventBytes: DEFAULT_CRITICAL_EVENT_BYTES,
|
|
46850
|
+
standardEventBytes: DEFAULT_STANDARD_EVENT_BYTES
|
|
46851
|
+
};
|
|
46852
|
+
var SessionEventOutboxCapacityError = class extends Error {
|
|
46853
|
+
code = "session_event_outbox_full";
|
|
46854
|
+
constructor() {
|
|
46855
|
+
super("The persistent Session event outbox is full");
|
|
46856
|
+
this.name = "SessionEventOutboxCapacityError";
|
|
46857
|
+
}
|
|
46858
|
+
};
|
|
46859
|
+
function rowNumber(row, field) {
|
|
46860
|
+
const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
|
|
46861
|
+
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
46862
|
+
throw new Error(`SQLite returned an invalid ${field} value`);
|
|
46863
|
+
}
|
|
46864
|
+
return value;
|
|
46865
|
+
}
|
|
46866
|
+
function rowString(row, field) {
|
|
46867
|
+
const value = typeof row === "object" && row !== null ? Reflect.get(row, field) : void 0;
|
|
46868
|
+
if (typeof value !== "string" || value.length === 0) {
|
|
46869
|
+
throw new Error(`SQLite returned an invalid ${field} value`);
|
|
46870
|
+
}
|
|
46871
|
+
return value;
|
|
46872
|
+
}
|
|
46873
|
+
function validateLimits(limits) {
|
|
46874
|
+
if (!Number.isSafeInteger(limits.standardEventBytes) || limits.standardEventBytes < 1 || !Number.isSafeInteger(limits.criticalEventBytes) || limits.criticalEventBytes < limits.standardEventBytes) {
|
|
46875
|
+
throw new Error("Session event outbox limits are invalid");
|
|
46876
|
+
}
|
|
46877
|
+
}
|
|
46878
|
+
var SessionEventOutbox = class {
|
|
46879
|
+
#database;
|
|
46880
|
+
#limits;
|
|
46881
|
+
constructor(path3, limits = DEFAULT_LIMITS) {
|
|
46882
|
+
validateLimits(limits);
|
|
46883
|
+
mkdirSync3(dirname6(path3), { mode: 448, recursive: true });
|
|
46884
|
+
this.#limits = limits;
|
|
46885
|
+
this.#database = new DatabaseSync(path3);
|
|
46886
|
+
this.#database.exec("PRAGMA journal_mode = WAL; PRAGMA synchronous = FULL; PRAGMA busy_timeout = 5000;");
|
|
46887
|
+
this.#database.exec(`
|
|
46888
|
+
CREATE TABLE IF NOT EXISTS relay_session_event_outbox (
|
|
46889
|
+
ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46890
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
46891
|
+
session_id TEXT NOT NULL,
|
|
46892
|
+
source_seq INTEGER NOT NULL UNIQUE,
|
|
46893
|
+
byte_length INTEGER NOT NULL,
|
|
46894
|
+
envelope_json TEXT NOT NULL
|
|
46895
|
+
) STRICT;
|
|
46896
|
+
CREATE INDEX IF NOT EXISTS relay_session_event_outbox_order
|
|
46897
|
+
ON relay_session_event_outbox(source_seq);
|
|
46898
|
+
CREATE TABLE IF NOT EXISTS relay_session_event_outbox_state (
|
|
46899
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
46900
|
+
stream_id TEXT NOT NULL,
|
|
46901
|
+
next_source_seq INTEGER NOT NULL,
|
|
46902
|
+
total_bytes INTEGER NOT NULL
|
|
46903
|
+
) STRICT;
|
|
46904
|
+
CREATE TABLE IF NOT EXISTS relay_session_event_dead_letters (
|
|
46905
|
+
ordinal INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
46906
|
+
event_id TEXT NOT NULL UNIQUE,
|
|
46907
|
+
session_id TEXT NOT NULL,
|
|
46908
|
+
rejection_code TEXT NOT NULL,
|
|
46909
|
+
envelope_json TEXT NOT NULL
|
|
46910
|
+
) STRICT;
|
|
46911
|
+
`);
|
|
46912
|
+
this.#database.prepare(
|
|
46913
|
+
`INSERT OR IGNORE INTO relay_session_event_outbox_state (
|
|
46914
|
+
singleton, stream_id, next_source_seq, total_bytes
|
|
46915
|
+
) VALUES (1, ?, 1, 0)`
|
|
46916
|
+
).run(randomUUID7());
|
|
46917
|
+
}
|
|
46918
|
+
acknowledge = (acknowledgement) => this.#remove(acknowledgement.eventId, acknowledgement.sessionId);
|
|
46919
|
+
close = () => {
|
|
46920
|
+
this.#database.close();
|
|
46921
|
+
};
|
|
46922
|
+
enqueue = (sessionId, payload, critical) => {
|
|
46923
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
46924
|
+
try {
|
|
46925
|
+
const state = this.#database.prepare(
|
|
46926
|
+
`SELECT stream_id, next_source_seq, total_bytes
|
|
46927
|
+
FROM relay_session_event_outbox_state
|
|
46928
|
+
WHERE singleton = 1`
|
|
46929
|
+
).get();
|
|
46930
|
+
const sourceSeq = rowNumber(state, "next_source_seq");
|
|
46931
|
+
if (sourceSeq < 1 || !Number.isSafeInteger(sourceSeq + 1)) {
|
|
46932
|
+
throw new Error("The Session event source sequence is exhausted");
|
|
46933
|
+
}
|
|
46934
|
+
const envelope2 = {
|
|
46935
|
+
delivery: {
|
|
46936
|
+
eventId: randomUUID7(),
|
|
46937
|
+
kind: "reliable",
|
|
46938
|
+
sourceSeq: sourceSeq.toString(),
|
|
46939
|
+
streamId: rowString(state, "stream_id")
|
|
46940
|
+
},
|
|
46941
|
+
kind: SESSION_EVENT_KIND,
|
|
46942
|
+
payload,
|
|
46943
|
+
sessionId
|
|
46944
|
+
};
|
|
46945
|
+
const encoded = encodeSessionEventEnvelope(envelope2);
|
|
46946
|
+
const byteLength = Buffer.byteLength(encoded, "utf8");
|
|
46947
|
+
const maximumBytes = critical ? this.#limits.criticalEventBytes : this.#limits.standardEventBytes;
|
|
46948
|
+
if (rowNumber(state, "total_bytes") + byteLength > maximumBytes) {
|
|
46949
|
+
throw new SessionEventOutboxCapacityError();
|
|
46950
|
+
}
|
|
46951
|
+
this.#database.prepare(
|
|
46952
|
+
`INSERT INTO relay_session_event_outbox (
|
|
46953
|
+
event_id, session_id, source_seq, byte_length, envelope_json
|
|
46954
|
+
) VALUES (?, ?, ?, ?, ?)`
|
|
46955
|
+
).run(envelope2.delivery.eventId, sessionId, sourceSeq, byteLength, encoded);
|
|
46956
|
+
this.#database.prepare(
|
|
46957
|
+
`UPDATE relay_session_event_outbox_state
|
|
46958
|
+
SET next_source_seq = next_source_seq + 1,
|
|
46959
|
+
total_bytes = total_bytes + ?
|
|
46960
|
+
WHERE singleton = 1`
|
|
46961
|
+
).run(byteLength);
|
|
46962
|
+
this.#database.exec("COMMIT");
|
|
46963
|
+
return envelope2;
|
|
46964
|
+
} catch (error62) {
|
|
46965
|
+
this.#database.exec("ROLLBACK");
|
|
46966
|
+
throw error62;
|
|
46967
|
+
}
|
|
46968
|
+
};
|
|
46969
|
+
pending = (limit, excludedEventIds) => {
|
|
46970
|
+
if (!Number.isSafeInteger(limit) || limit < 1) {
|
|
46971
|
+
throw new Error("Session event replay limit must be a positive integer");
|
|
46972
|
+
}
|
|
46973
|
+
const exclusion = excludedEventIds.length === 0 ? "" : `WHERE event_id NOT IN (${excludedEventIds.map(() => "?").join(", ")})`;
|
|
46974
|
+
return this.#database.prepare(
|
|
46975
|
+
`SELECT envelope_json
|
|
46976
|
+
FROM relay_session_event_outbox
|
|
46977
|
+
${exclusion}
|
|
46978
|
+
ORDER BY source_seq
|
|
46979
|
+
LIMIT ?`
|
|
46980
|
+
).all(...excludedEventIds, limit).map((row) => {
|
|
46981
|
+
const envelope2 = decodeSessionEventEnvelope(rowString(row, "envelope_json"));
|
|
46982
|
+
if (!("delivery" in envelope2)) {
|
|
46983
|
+
throw new Error("SQLite returned an invalid reliable Session event envelope");
|
|
46984
|
+
}
|
|
46985
|
+
return envelope2;
|
|
46986
|
+
});
|
|
46987
|
+
};
|
|
46988
|
+
quarantine = (rejection) => {
|
|
46989
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
46990
|
+
try {
|
|
46991
|
+
const stored = this.#database.prepare(
|
|
46992
|
+
`SELECT byte_length, envelope_json
|
|
46993
|
+
FROM relay_session_event_outbox
|
|
46994
|
+
WHERE event_id = ? AND session_id = ?`
|
|
46995
|
+
).get(rejection.eventId, rejection.sessionId);
|
|
46996
|
+
if (stored === void 0) {
|
|
46997
|
+
this.#database.exec("COMMIT");
|
|
46998
|
+
return false;
|
|
46999
|
+
}
|
|
47000
|
+
this.#database.prepare(
|
|
47001
|
+
`INSERT INTO relay_session_event_dead_letters (
|
|
47002
|
+
event_id, session_id, rejection_code, envelope_json
|
|
47003
|
+
) VALUES (?, ?, ?, ?)`
|
|
47004
|
+
).run(rejection.eventId, rejection.sessionId, rejection.code, rowString(stored, "envelope_json"));
|
|
47005
|
+
this.#deleteStored(rejection.eventId, rejection.sessionId, rowNumber(stored, "byte_length"));
|
|
47006
|
+
this.#database.prepare(
|
|
47007
|
+
`DELETE FROM relay_session_event_dead_letters
|
|
47008
|
+
WHERE ordinal NOT IN (
|
|
47009
|
+
SELECT ordinal FROM relay_session_event_dead_letters
|
|
47010
|
+
ORDER BY ordinal DESC LIMIT ?
|
|
47011
|
+
)`
|
|
47012
|
+
).run(MAX_DEAD_LETTERS);
|
|
47013
|
+
this.#database.exec("COMMIT");
|
|
47014
|
+
return true;
|
|
47015
|
+
} catch (error62) {
|
|
47016
|
+
this.#database.exec("ROLLBACK");
|
|
47017
|
+
throw error62;
|
|
47018
|
+
}
|
|
47019
|
+
};
|
|
47020
|
+
#deleteStored = (eventId, sessionId, byteLength) => {
|
|
47021
|
+
const deletion = this.#database.prepare(
|
|
47022
|
+
`DELETE FROM relay_session_event_outbox
|
|
47023
|
+
WHERE event_id = ? AND session_id = ?`
|
|
47024
|
+
).run(eventId, sessionId);
|
|
47025
|
+
if (deletion.changes !== 1) {
|
|
47026
|
+
throw new Error("The Session event could not be removed from the outbox");
|
|
47027
|
+
}
|
|
47028
|
+
const state = this.#database.prepare(
|
|
47029
|
+
`UPDATE relay_session_event_outbox_state
|
|
47030
|
+
SET total_bytes = total_bytes - ?
|
|
47031
|
+
WHERE singleton = 1 AND total_bytes >= ?`
|
|
47032
|
+
).run(byteLength, byteLength);
|
|
47033
|
+
if (state.changes !== 1) {
|
|
47034
|
+
throw new Error("The Session event outbox capacity state is inconsistent");
|
|
47035
|
+
}
|
|
47036
|
+
};
|
|
47037
|
+
#remove = (eventId, sessionId) => {
|
|
47038
|
+
this.#database.exec("BEGIN IMMEDIATE");
|
|
47039
|
+
try {
|
|
47040
|
+
const stored = this.#database.prepare(
|
|
47041
|
+
`SELECT byte_length
|
|
47042
|
+
FROM relay_session_event_outbox
|
|
47043
|
+
WHERE event_id = ? AND session_id = ?`
|
|
47044
|
+
).get(eventId, sessionId);
|
|
47045
|
+
if (stored === void 0) {
|
|
47046
|
+
this.#database.exec("COMMIT");
|
|
47047
|
+
return false;
|
|
47048
|
+
}
|
|
47049
|
+
this.#deleteStored(eventId, sessionId, rowNumber(stored, "byte_length"));
|
|
47050
|
+
this.#database.exec("COMMIT");
|
|
47051
|
+
return true;
|
|
47052
|
+
} catch (error62) {
|
|
47053
|
+
this.#database.exec("ROLLBACK");
|
|
47054
|
+
throw error62;
|
|
47055
|
+
}
|
|
47056
|
+
};
|
|
47057
|
+
};
|
|
47058
|
+
|
|
46748
47059
|
// src/relay-connection/session-event-publisher.ts
|
|
46749
47060
|
import { WebSocket as WebSocket9 } from "undici";
|
|
46750
|
-
var
|
|
47061
|
+
var SESSION_EVENT_REPLAY_WINDOW = 16;
|
|
47062
|
+
var SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
|
|
47063
|
+
function reservesSessionEventCapacity(event) {
|
|
47064
|
+
if (event.kind === SESSION_COMPLETED_KIND || event.kind === SESSION_FAILED_KIND) {
|
|
47065
|
+
return true;
|
|
47066
|
+
}
|
|
47067
|
+
return event.kind === AGENT_ACTIVITY_KIND && (event.activityType === "progress" || event.activityType === "reasoning") && (event.status === "completed" || event.status === "failed");
|
|
47068
|
+
}
|
|
46751
47069
|
var DaemonSessionEventPublisher = class {
|
|
46752
|
-
#
|
|
46753
|
-
#
|
|
46754
|
-
|
|
46755
|
-
|
|
46756
|
-
|
|
47070
|
+
#inFlight = /* @__PURE__ */ new Map();
|
|
47071
|
+
#outbox;
|
|
47072
|
+
#reportedCapacityKinds = /* @__PURE__ */ new Set();
|
|
47073
|
+
#timer;
|
|
47074
|
+
#connection = { kind: "disconnected" };
|
|
47075
|
+
#deferredReplay = null;
|
|
47076
|
+
#inFlightBytes = 0;
|
|
47077
|
+
constructor(outbox, timer) {
|
|
47078
|
+
this.#outbox = outbox;
|
|
47079
|
+
this.#timer = timer;
|
|
47080
|
+
}
|
|
47081
|
+
acknowledge = (acknowledgement) => {
|
|
47082
|
+
if (!this.#outbox.acknowledge(acknowledgement)) {
|
|
47083
|
+
return;
|
|
47084
|
+
}
|
|
47085
|
+
this.#removeInFlight(acknowledgement.eventId);
|
|
47086
|
+
this.#publishNextBatch();
|
|
47087
|
+
};
|
|
47088
|
+
close = () => {
|
|
47089
|
+
this.#deferredReplay?.cancel();
|
|
47090
|
+
this.#deferredReplay = null;
|
|
47091
|
+
this.#connection = { kind: "disconnected" };
|
|
47092
|
+
this.#clearInFlight();
|
|
47093
|
+
this.#outbox.close();
|
|
47094
|
+
};
|
|
47095
|
+
connect = (socket, delivery) => {
|
|
47096
|
+
this.#connection = { delivery, kind: "connected", socket };
|
|
47097
|
+
this.#clearInFlight();
|
|
47098
|
+
this.#publishNextBatch();
|
|
47099
|
+
};
|
|
47100
|
+
defer = (deferred) => {
|
|
47101
|
+
if (!this.#inFlight.has(deferred.eventId)) {
|
|
47102
|
+
return;
|
|
47103
|
+
}
|
|
47104
|
+
this.#clearInFlight();
|
|
47105
|
+
if (this.#deferredReplay !== null) {
|
|
47106
|
+
return;
|
|
47107
|
+
}
|
|
47108
|
+
this.#deferredReplay = this.#timer.schedule(() => {
|
|
47109
|
+
this.#deferredReplay = null;
|
|
47110
|
+
this.#publishNextBatch();
|
|
47111
|
+
}, deferred.retryAfterMilliseconds);
|
|
46757
47112
|
};
|
|
46758
47113
|
disconnect = (socket) => {
|
|
46759
|
-
if (this.#socket
|
|
46760
|
-
|
|
47114
|
+
if (this.#connection.kind !== "connected" || this.#connection.socket !== socket) {
|
|
47115
|
+
return;
|
|
46761
47116
|
}
|
|
47117
|
+
this.#connection = { kind: "disconnected" };
|
|
47118
|
+
this.#clearInFlight();
|
|
46762
47119
|
};
|
|
46763
47120
|
publish = (sessionId, event) => {
|
|
46764
|
-
|
|
46765
|
-
|
|
46766
|
-
|
|
47121
|
+
try {
|
|
47122
|
+
this.#outbox.enqueue(sessionId, encodeRecordedSessionEvent(event), reservesSessionEventCapacity(event));
|
|
47123
|
+
} catch (error62) {
|
|
47124
|
+
if (!(error62 instanceof SessionEventOutboxCapacityError)) {
|
|
47125
|
+
throw error62;
|
|
47126
|
+
}
|
|
47127
|
+
if (!this.#reportedCapacityKinds.has(event.kind)) {
|
|
47128
|
+
this.#reportedCapacityKinds.add(event.kind);
|
|
47129
|
+
console.warn(
|
|
47130
|
+
`Mobius daemon could not persist "${event.kind}" because the Session event outbox is full`
|
|
47131
|
+
);
|
|
47132
|
+
}
|
|
46767
47133
|
return;
|
|
46768
47134
|
}
|
|
46769
|
-
|
|
46770
|
-
|
|
46771
|
-
|
|
46772
|
-
|
|
47135
|
+
this.#publishNextBatch();
|
|
47136
|
+
};
|
|
47137
|
+
quarantine = (rejection) => {
|
|
47138
|
+
if (!this.#outbox.quarantine(rejection)) {
|
|
46773
47139
|
return;
|
|
46774
47140
|
}
|
|
46775
|
-
|
|
46776
|
-
|
|
46777
|
-
|
|
46778
|
-
kind: SESSION_EVENT_KIND,
|
|
46779
|
-
payload: encodeRecordedSessionEvent(event),
|
|
46780
|
-
sessionId
|
|
46781
|
-
})
|
|
46782
|
-
);
|
|
46783
|
-
} catch {
|
|
46784
|
-
this.#reportDrop();
|
|
46785
|
-
}
|
|
47141
|
+
this.#removeInFlight(rejection.eventId);
|
|
47142
|
+
console.error(`Mobius Relay rejected Session event ${rejection.eventId} with code ${rejection.code}`);
|
|
47143
|
+
this.#publishNextBatch();
|
|
46786
47144
|
};
|
|
46787
|
-
|
|
46788
|
-
this.#
|
|
47145
|
+
#clearInFlight = () => {
|
|
47146
|
+
this.#inFlight.clear();
|
|
47147
|
+
this.#inFlightBytes = 0;
|
|
47148
|
+
};
|
|
47149
|
+
#publishNextBatch = () => {
|
|
47150
|
+
if (this.#deferredReplay !== null || this.#connection.kind !== "connected") {
|
|
47151
|
+
return;
|
|
47152
|
+
}
|
|
47153
|
+
while (this.#inFlight.size < SESSION_EVENT_REPLAY_WINDOW) {
|
|
47154
|
+
const batch = this.#outbox.pending(SESSION_EVENT_REPLAY_WINDOW - this.#inFlight.size, [
|
|
47155
|
+
...this.#inFlight.keys()
|
|
47156
|
+
]);
|
|
47157
|
+
let sent = 0;
|
|
47158
|
+
for (const envelope2 of batch) {
|
|
47159
|
+
const encoded = encodeDaemonToServiceMessage(
|
|
47160
|
+
this.#connection.delivery === "reliable" ? envelope2 : {
|
|
47161
|
+
kind: envelope2.kind,
|
|
47162
|
+
payload: envelope2.payload,
|
|
47163
|
+
sessionId: envelope2.sessionId
|
|
47164
|
+
}
|
|
47165
|
+
);
|
|
47166
|
+
const byteLength = Buffer.byteLength(encoded, "utf8");
|
|
47167
|
+
if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
|
|
47168
|
+
return;
|
|
47169
|
+
}
|
|
47170
|
+
if (!this.#send(encoded)) {
|
|
47171
|
+
this.#clearInFlight();
|
|
47172
|
+
return;
|
|
47173
|
+
}
|
|
47174
|
+
if (this.#connection.delivery === "ephemeral") {
|
|
47175
|
+
this.#outbox.acknowledge({
|
|
47176
|
+
eventId: envelope2.delivery.eventId,
|
|
47177
|
+
kind: "session.event.ack",
|
|
47178
|
+
outcome: { kind: "relayed" },
|
|
47179
|
+
sessionId: envelope2.sessionId
|
|
47180
|
+
});
|
|
47181
|
+
sent += 1;
|
|
47182
|
+
continue;
|
|
47183
|
+
}
|
|
47184
|
+
this.#inFlight.set(envelope2.delivery.eventId, { byteLength });
|
|
47185
|
+
this.#inFlightBytes += byteLength;
|
|
47186
|
+
sent += 1;
|
|
47187
|
+
}
|
|
47188
|
+
if (sent === 0) {
|
|
47189
|
+
return;
|
|
47190
|
+
}
|
|
47191
|
+
}
|
|
46789
47192
|
};
|
|
46790
|
-
#
|
|
46791
|
-
|
|
47193
|
+
#removeInFlight = (eventId) => {
|
|
47194
|
+
const inFlight = this.#inFlight.get(eventId);
|
|
47195
|
+
if (inFlight === void 0) {
|
|
46792
47196
|
return;
|
|
46793
47197
|
}
|
|
46794
|
-
this.#
|
|
46795
|
-
|
|
47198
|
+
this.#inFlight.delete(eventId);
|
|
47199
|
+
this.#inFlightBytes -= inFlight.byteLength;
|
|
47200
|
+
};
|
|
47201
|
+
#send = (encoded) => {
|
|
47202
|
+
if (this.#connection.kind !== "connected" || this.#connection.socket.readyState !== WebSocket9.OPEN) {
|
|
47203
|
+
return false;
|
|
47204
|
+
}
|
|
47205
|
+
try {
|
|
47206
|
+
this.#connection.socket.send(encoded);
|
|
47207
|
+
return true;
|
|
47208
|
+
} catch {
|
|
47209
|
+
return false;
|
|
47210
|
+
}
|
|
46796
47211
|
};
|
|
46797
47212
|
};
|
|
46798
47213
|
|
|
@@ -46853,7 +47268,7 @@ async function installDetectedAgentIntegrations(config2, installations, signal)
|
|
|
46853
47268
|
{
|
|
46854
47269
|
agentId: agent.id,
|
|
46855
47270
|
kind: AGENT_INSTALL_REQUEST_KIND,
|
|
46856
|
-
requestId: `automatic-integration-${
|
|
47271
|
+
requestId: `automatic-integration-${randomUUID8()}`
|
|
46857
47272
|
},
|
|
46858
47273
|
signal
|
|
46859
47274
|
);
|
|
@@ -46875,7 +47290,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
|
|
|
46875
47290
|
join18(stateDirectory, "sessions", `${createHash6("sha256").update(config2.channelId).digest("hex")}.sqlite`)
|
|
46876
47291
|
);
|
|
46877
47292
|
const operations = new DaemonOperationState();
|
|
46878
|
-
const
|
|
47293
|
+
const channelHash = createHash6("sha256").update(config2.channelId).digest("hex");
|
|
47294
|
+
const publisher = new DaemonSessionEventPublisher(
|
|
47295
|
+
new SessionEventOutbox(join18(stateDirectory, "events", `${channelHash}.sqlite`)),
|
|
47296
|
+
timer
|
|
47297
|
+
);
|
|
46879
47298
|
const activeTurns = new DaemonTurnRegistry(operations);
|
|
46880
47299
|
const recordSessionEvent = (sessionId, event) => {
|
|
46881
47300
|
publisher.publish(sessionId, activeTurns.recordEvent(event));
|