@moxt-ai/mobius 0.0.147 → 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 +731 -197
- package/dist/cli.js.map +4 -4
- package/dist/daemon-active-turn.d.ts +15 -2
- package/dist/daemon-active-turn.d.ts.map +1 -1
- package/dist/daemon-active-turn.js +63 -25
- package/dist/daemon-active-turn.js.map +1 -1
- package/dist/daemon-command-handler.js +3 -3
- package/dist/daemon-history-commands.d.ts +4 -7
- package/dist/daemon-history-commands.d.ts.map +1 -1
- package/dist/daemon-history-commands.js.map +1 -1
- package/dist/daemon-prompt-executor.d.ts +1 -1
- package/dist/daemon-prompt-executor.d.ts.map +1 -1
- package/dist/daemon-prompt-executor.js +1 -3
- package/dist/daemon-prompt-executor.js.map +1 -1
- package/dist/daemon-turn-registry.d.ts +18 -56
- package/dist/daemon-turn-registry.d.ts.map +1 -1
- package/dist/daemon-turn-registry.js +40 -1
- package/dist/daemon-turn-registry.js.map +1 -1
- package/dist/index.js +730 -196
- package/dist/index.js.map +4 -4
- package/dist/native-sessions/live-history-page.d.ts +4 -7
- package/dist/native-sessions/live-history-page.d.ts.map +1 -1
- package/dist/native-sessions/live-history-page.js +163 -107
- package/dist/native-sessions/live-history-page.js.map +1 -1
- 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 +736 -202
- 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);
|
|
@@ -29624,18 +29632,21 @@ var activeTurnMessageSchema = external_exports.discriminatedUnion("role", [
|
|
|
29624
29632
|
activeTurnAssistantMessageSchema
|
|
29625
29633
|
]);
|
|
29626
29634
|
var activeTurnSnapshotSchema = external_exports.strictObject({
|
|
29627
|
-
activities: external_exports.array(activeTurnActivitySchema).readonly(),
|
|
29628
29635
|
commandId: identifierStringSchema,
|
|
29629
|
-
content: external_exports.array(displayContentSchema).readonly(),
|
|
29630
|
-
prompt: external_exports.string().max(1e5),
|
|
29631
|
-
text: external_exports.string(),
|
|
29632
|
-
truncated: external_exports.boolean(),
|
|
29633
29636
|
epoch: identifierStringSchema,
|
|
29637
|
+
hasOlder: external_exports.boolean(),
|
|
29634
29638
|
interaction: external_exports.discriminatedUnion("kind", [sessionPermissionRequestedEventSchema, sessionElicitationRequestedEventSchema]).nullable(),
|
|
29635
29639
|
messages: external_exports.array(activeTurnMessageSchema).max(SESSION_HISTORY_MESSAGE_LIMIT).readonly(),
|
|
29636
29640
|
sequence: external_exports.number().int().nonnegative(),
|
|
29637
|
-
state: external_exports.literal("running")
|
|
29638
|
-
|
|
29641
|
+
state: external_exports.literal("running"),
|
|
29642
|
+
truncated: external_exports.boolean()
|
|
29643
|
+
}).readonly().refine(
|
|
29644
|
+
(snapshot) => snapshot.messages.reduce(
|
|
29645
|
+
(total, message) => total + message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0),
|
|
29646
|
+
0
|
|
29647
|
+
) <= SESSION_HISTORY_CHARACTER_LIMIT,
|
|
29648
|
+
"Active Turn snapshot content is too large"
|
|
29649
|
+
);
|
|
29639
29650
|
var identity = {
|
|
29640
29651
|
...envelope,
|
|
29641
29652
|
kind: external_exports.literal(NATIVE_SESSION_REQUEST_KIND),
|
|
@@ -29779,27 +29790,61 @@ var encodeNativeSessionCancel = (value) => JSON.stringify(nativeSessionCancelSch
|
|
|
29779
29790
|
|
|
29780
29791
|
// ../protocol/src/session-events.ts
|
|
29781
29792
|
var SESSION_EVENT_KIND = "session.event";
|
|
29782
|
-
var
|
|
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
|
+
};
|
|
29783
29801
|
var MAX_RECORDED_EVENT_LENGTH = 8 * 1024 * 1024;
|
|
29784
|
-
var
|
|
29802
|
+
var ephemeralEventEnvelopeSchema = external_exports.strictObject({
|
|
29785
29803
|
kind: external_exports.literal(SESSION_EVENT_KIND),
|
|
29786
29804
|
payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
|
|
29787
29805
|
sessionId: identifierStringSchema
|
|
29788
29806
|
}).readonly();
|
|
29789
|
-
var
|
|
29790
|
-
|
|
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),
|
|
29791
29816
|
payload: external_exports.string().min(2).max(MAX_RECORDED_EVENT_LENGTH),
|
|
29792
29817
|
sessionId: identifierStringSchema
|
|
29793
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();
|
|
29794
29846
|
var SESSION_QUEUE_CHANGED_KIND = "session.queue.changed";
|
|
29795
29847
|
var sessionQueueChangedSchema = external_exports.strictObject({ kind: external_exports.literal(SESSION_QUEUE_CHANGED_KIND), sessionId: identifierStringSchema }).readonly();
|
|
29796
|
-
var SESSION_TURN_UPDATED_KIND = "session.turn.updated";
|
|
29797
|
-
var sessionTurnUpdatedSchema = external_exports.strictObject({
|
|
29798
|
-
kind: external_exports.literal(SESSION_TURN_UPDATED_KIND),
|
|
29799
|
-
sessionId: identifierStringSchema,
|
|
29800
|
-
turn: activeTurnSnapshotSchema
|
|
29801
|
-
}).readonly();
|
|
29802
|
-
var decodeSessionTurnUpdatedEvent = (encoded) => decode5(encoded, sessionTurnUpdatedSchema);
|
|
29803
29848
|
function decode5(encoded, schema) {
|
|
29804
29849
|
let value;
|
|
29805
29850
|
try {
|
|
@@ -29826,11 +29871,15 @@ var encode4 = (message, schema) => {
|
|
|
29826
29871
|
};
|
|
29827
29872
|
var decodeSessionEventEnvelope = (encoded) => decode5(encoded, eventEnvelopeSchema);
|
|
29828
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);
|
|
29829
29880
|
function decodeRecordedSessionEvent(encoded) {
|
|
29830
29881
|
const kind = decodeMessageKind(encoded);
|
|
29831
29882
|
switch (kind) {
|
|
29832
|
-
case SESSION_TURN_UPDATED_KIND:
|
|
29833
|
-
return decodeSessionTurnUpdatedEvent(encoded);
|
|
29834
29883
|
case SESSION_QUEUE_CHANGED_KIND:
|
|
29835
29884
|
return decode5(encoded, sessionQueueChangedSchema);
|
|
29836
29885
|
case AGENT_ACTIVITY_KIND:
|
|
@@ -29999,6 +30048,21 @@ var serviceToDaemonSignatures = {
|
|
|
29999
30048
|
encodeSessionElicitationRespondCommand,
|
|
30000
30049
|
decodeSessionElicitationRespondCommand
|
|
30001
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
|
+
),
|
|
30002
30066
|
[SESSION_LIVE_HISTORY_LOAD_KIND]: defineMessageSignature(
|
|
30003
30067
|
SESSION_LIVE_HISTORY_LOAD_KIND,
|
|
30004
30068
|
encodeSessionLiveHistoryLoad,
|
|
@@ -31057,7 +31121,7 @@ var AuditStore = class {
|
|
|
31057
31121
|
};
|
|
31058
31122
|
|
|
31059
31123
|
// src/run-daemon.ts
|
|
31060
|
-
import { createHash as createHash6, randomUUID as
|
|
31124
|
+
import { createHash as createHash6, randomUUID as randomUUID8 } from "node:crypto";
|
|
31061
31125
|
import { join as join18 } from "node:path";
|
|
31062
31126
|
import { styleText } from "node:util";
|
|
31063
31127
|
|
|
@@ -42207,6 +42271,28 @@ var DaemonInstallationObserver = class {
|
|
|
42207
42271
|
|
|
42208
42272
|
// src/daemon-active-turn.ts
|
|
42209
42273
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
42274
|
+
function activeMessageCharacters(message) {
|
|
42275
|
+
return message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0);
|
|
42276
|
+
}
|
|
42277
|
+
var ACTIVE_TURN_TEXT_CHUNK_LIMIT = Math.floor(SESSION_HISTORY_CHARACTER_LIMIT / 2);
|
|
42278
|
+
function boundedRecoveryMessages(messages) {
|
|
42279
|
+
const selected = [];
|
|
42280
|
+
let remainingCharacters = SESSION_HISTORY_CHARACTER_LIMIT;
|
|
42281
|
+
for (let index = messages.length - 1; index >= 0 && selected.length < SESSION_HISTORY_MESSAGE_LIMIT; index -= 1) {
|
|
42282
|
+
const message = messages[index];
|
|
42283
|
+
if (message === void 0) {
|
|
42284
|
+
break;
|
|
42285
|
+
}
|
|
42286
|
+
const characters = activeMessageCharacters(message);
|
|
42287
|
+
if (characters <= remainingCharacters) {
|
|
42288
|
+
selected.unshift(message);
|
|
42289
|
+
remainingCharacters -= characters;
|
|
42290
|
+
continue;
|
|
42291
|
+
}
|
|
42292
|
+
break;
|
|
42293
|
+
}
|
|
42294
|
+
return { hasOlder: selected.length !== messages.length, messages: selected };
|
|
42295
|
+
}
|
|
42210
42296
|
var ActiveDaemonTurn = class {
|
|
42211
42297
|
attemptId;
|
|
42212
42298
|
command;
|
|
@@ -42214,9 +42300,7 @@ var ActiveDaemonTurn = class {
|
|
|
42214
42300
|
#finished = Promise.withResolvers();
|
|
42215
42301
|
#cancellationRequested = false;
|
|
42216
42302
|
#dispatch;
|
|
42217
|
-
#text = "";
|
|
42218
42303
|
#activities = /* @__PURE__ */ new Map();
|
|
42219
|
-
#content = /* @__PURE__ */ new Map();
|
|
42220
42304
|
#timeline;
|
|
42221
42305
|
#sequence = 0;
|
|
42222
42306
|
#interaction = null;
|
|
@@ -42263,9 +42347,17 @@ var ActiveDaemonTurn = class {
|
|
|
42263
42347
|
effectiveDisposition = () => this.#dispatch.kind === "promoted" ? SessionPromptDisposition.Steer : this.command.disposition;
|
|
42264
42348
|
recordEvent = (event) => {
|
|
42265
42349
|
if (event.kind === AGENT_MESSAGE_DELTA_KIND) {
|
|
42266
|
-
|
|
42350
|
+
const sequence = this.advance();
|
|
42267
42351
|
this.appendMessageText(event.messageId, event.text);
|
|
42268
|
-
return
|
|
42352
|
+
return {
|
|
42353
|
+
commandId: this.command.commandId,
|
|
42354
|
+
epoch: this.#epoch,
|
|
42355
|
+
kind: AGENT_MESSAGE_DELTA_KIND,
|
|
42356
|
+
messageId: event.messageId,
|
|
42357
|
+
sequence,
|
|
42358
|
+
sessionId: this.command.sessionId,
|
|
42359
|
+
text: event.text
|
|
42360
|
+
};
|
|
42269
42361
|
}
|
|
42270
42362
|
if (event.sessionId !== this.command.sessionId || "commandId" in event && event.commandId !== this.command.commandId) {
|
|
42271
42363
|
return event;
|
|
@@ -42312,7 +42404,6 @@ var ActiveDaemonTurn = class {
|
|
|
42312
42404
|
toolKind: previous?.toolKind ?? ""
|
|
42313
42405
|
});
|
|
42314
42406
|
} else if (event.kind === AGENT_CONTENT_KIND) {
|
|
42315
|
-
this.#content.set(event.contentId, event.content);
|
|
42316
42407
|
const target = this.#timeline.findLastIndex(
|
|
42317
42408
|
(entry) => entry.kind === "message" && entry.sourceMessageId === event.messageId
|
|
42318
42409
|
);
|
|
@@ -42338,7 +42429,7 @@ var ActiveDaemonTurn = class {
|
|
|
42338
42429
|
this.#interaction = null;
|
|
42339
42430
|
}
|
|
42340
42431
|
if (event.kind === AGENT_ACTIVITY_KIND || event.kind === AGENT_ACTIVITY_NARRATION_KIND || event.kind === AGENT_CONTENT_KIND) {
|
|
42341
|
-
|
|
42432
|
+
this.advance();
|
|
42342
42433
|
}
|
|
42343
42434
|
return event;
|
|
42344
42435
|
};
|
|
@@ -42347,7 +42438,7 @@ var ActiveDaemonTurn = class {
|
|
|
42347
42438
|
while (remaining.length > 0) {
|
|
42348
42439
|
const latest = this.#timeline.at(-1);
|
|
42349
42440
|
if (latest?.kind === "message" && latest.sourceMessageId === messageId) {
|
|
42350
|
-
const available =
|
|
42441
|
+
const available = ACTIVE_TURN_TEXT_CHUNK_LIMIT - latest.text.length;
|
|
42351
42442
|
if (available > 0) {
|
|
42352
42443
|
this.#timeline = [
|
|
42353
42444
|
...this.#timeline.slice(0, -1),
|
|
@@ -42364,21 +42455,22 @@ var ActiveDaemonTurn = class {
|
|
|
42364
42455
|
id: decodeIdentifier(randomUUID2()),
|
|
42365
42456
|
kind: "message",
|
|
42366
42457
|
sourceMessageId: messageId,
|
|
42367
|
-
text: remaining.slice(0,
|
|
42458
|
+
text: remaining.slice(0, ACTIVE_TURN_TEXT_CHUNK_LIMIT)
|
|
42368
42459
|
}
|
|
42369
42460
|
];
|
|
42370
|
-
remaining = remaining.slice(
|
|
42461
|
+
remaining = remaining.slice(ACTIVE_TURN_TEXT_CHUNK_LIMIT);
|
|
42371
42462
|
}
|
|
42372
42463
|
}
|
|
42373
42464
|
recordSteeringPrompt = (command) => {
|
|
42374
42465
|
if (this.#dispatch.kind !== "running" || command.sessionId !== this.command.sessionId || command.targetCommandId !== this.command.commandId) {
|
|
42375
|
-
return
|
|
42466
|
+
return false;
|
|
42376
42467
|
}
|
|
42377
42468
|
this.#timeline = [
|
|
42378
42469
|
...this.#timeline,
|
|
42379
42470
|
{ id: decodeIdentifier(command.commandId), images: command.images, kind: "user", text: command.prompt }
|
|
42380
42471
|
];
|
|
42381
|
-
|
|
42472
|
+
this.advance();
|
|
42473
|
+
return true;
|
|
42382
42474
|
};
|
|
42383
42475
|
timelineMessages() {
|
|
42384
42476
|
return this.#timeline.flatMap((entry) => {
|
|
@@ -42400,32 +42492,39 @@ var ActiveDaemonTurn = class {
|
|
|
42400
42492
|
return activity2 === void 0 ? [] : [{ activities: [activity2], content: [], id: entry.id, role: "assistant", text: "" }];
|
|
42401
42493
|
});
|
|
42402
42494
|
}
|
|
42403
|
-
|
|
42404
|
-
this.#
|
|
42405
|
-
|
|
42406
|
-
if (turn === null) {
|
|
42407
|
-
throw new Error("Only a running turn can publish a snapshot");
|
|
42495
|
+
advance = () => {
|
|
42496
|
+
if (this.#dispatch.kind !== "running") {
|
|
42497
|
+
throw new Error("Only a running turn can advance its snapshot");
|
|
42408
42498
|
}
|
|
42409
|
-
|
|
42499
|
+
this.#sequence += 1;
|
|
42500
|
+
return this.#sequence;
|
|
42410
42501
|
};
|
|
42411
42502
|
snapshot = () => {
|
|
42412
42503
|
if (this.#dispatch.kind !== "running") {
|
|
42413
42504
|
return null;
|
|
42414
42505
|
}
|
|
42415
|
-
const
|
|
42416
|
-
const messages = completeMessages.slice(-SESSION_HISTORY_MESSAGE_LIMIT);
|
|
42506
|
+
const bounded2 = boundedRecoveryMessages(this.timelineMessages());
|
|
42417
42507
|
return {
|
|
42418
|
-
activities: [...this.#activities.values()].sort((left, right) => left.sequence - right.sequence),
|
|
42419
42508
|
commandId: decodeIdentifier(this.command.commandId),
|
|
42420
|
-
content: [...this.#content.values()],
|
|
42421
|
-
prompt: this.command.prompt,
|
|
42422
|
-
text: this.#text,
|
|
42423
|
-
truncated: messages.length !== completeMessages.length,
|
|
42424
42509
|
epoch: this.#epoch,
|
|
42510
|
+
hasOlder: bounded2.hasOlder,
|
|
42425
42511
|
interaction: this.#interaction,
|
|
42426
|
-
messages,
|
|
42512
|
+
messages: bounded2.messages,
|
|
42427
42513
|
sequence: this.#sequence,
|
|
42428
|
-
state: "running"
|
|
42514
|
+
state: "running",
|
|
42515
|
+
truncated: false
|
|
42516
|
+
};
|
|
42517
|
+
};
|
|
42518
|
+
recovery = () => {
|
|
42519
|
+
if (this.#dispatch.kind !== "running") {
|
|
42520
|
+
return null;
|
|
42521
|
+
}
|
|
42522
|
+
return {
|
|
42523
|
+
commandId: decodeIdentifier(this.command.commandId),
|
|
42524
|
+
epoch: this.#epoch,
|
|
42525
|
+
messages: this.timelineMessages(),
|
|
42526
|
+
state: "running",
|
|
42527
|
+
truncated: false
|
|
42429
42528
|
};
|
|
42430
42529
|
};
|
|
42431
42530
|
matches = (cancellation) => cancellation.commandId === this.command.commandId && cancellation.sessionId === this.command.sessionId;
|
|
@@ -42451,6 +42550,7 @@ var ActiveDaemonTurn = class {
|
|
|
42451
42550
|
};
|
|
42452
42551
|
|
|
42453
42552
|
// src/daemon-turn-registry.ts
|
|
42553
|
+
var COMPLETED_TURN_RECOVERY_LIMIT = 32;
|
|
42454
42554
|
var waitForTurnCompletion = (turn, waitSignal) => {
|
|
42455
42555
|
waitSignal.throwIfAborted();
|
|
42456
42556
|
return new Promise((resolve9, reject) => {
|
|
@@ -42477,6 +42577,7 @@ var waitForTurnCompletion = (turn, waitSignal) => {
|
|
|
42477
42577
|
};
|
|
42478
42578
|
var DaemonTurnRegistry = class {
|
|
42479
42579
|
#turns = /* @__PURE__ */ new Map();
|
|
42580
|
+
#completed = /* @__PURE__ */ new Map();
|
|
42480
42581
|
#operations;
|
|
42481
42582
|
constructor(operations) {
|
|
42482
42583
|
this.#operations = operations;
|
|
@@ -42488,7 +42589,22 @@ var DaemonTurnRegistry = class {
|
|
|
42488
42589
|
};
|
|
42489
42590
|
get = (commandId) => this.#turns.get(commandId);
|
|
42490
42591
|
finish = (turn) => {
|
|
42592
|
+
const recovery = turn.recovery();
|
|
42491
42593
|
this.#turns.delete(turn.command.commandId);
|
|
42594
|
+
if (recovery !== null) {
|
|
42595
|
+
this.#completed.delete(turn.command.sessionId);
|
|
42596
|
+
this.#completed.set(turn.command.sessionId, {
|
|
42597
|
+
command: turn.command,
|
|
42598
|
+
recovery: { ...recovery, state: "completed" }
|
|
42599
|
+
});
|
|
42600
|
+
while (this.#completed.size > COMPLETED_TURN_RECOVERY_LIMIT) {
|
|
42601
|
+
const oldestSessionId = this.#completed.keys().next().value;
|
|
42602
|
+
if (oldestSessionId === void 0) {
|
|
42603
|
+
break;
|
|
42604
|
+
}
|
|
42605
|
+
this.#completed.delete(oldestSessionId);
|
|
42606
|
+
}
|
|
42607
|
+
}
|
|
42492
42608
|
turn.finish();
|
|
42493
42609
|
};
|
|
42494
42610
|
recordEvent = (event) => {
|
|
@@ -42509,7 +42625,7 @@ var DaemonTurnRegistry = class {
|
|
|
42509
42625
|
};
|
|
42510
42626
|
recordSteeringPrompt = (command) => {
|
|
42511
42627
|
const target = this.#turns.get(command.targetCommandId);
|
|
42512
|
-
return target?.recordSteeringPrompt(command) ??
|
|
42628
|
+
return target?.recordSteeringPrompt(command) ?? false;
|
|
42513
42629
|
};
|
|
42514
42630
|
executionSnapshot = async (matches) => {
|
|
42515
42631
|
const matching = [];
|
|
@@ -42540,12 +42656,30 @@ var DaemonTurnRegistry = class {
|
|
|
42540
42656
|
}
|
|
42541
42657
|
return null;
|
|
42542
42658
|
};
|
|
42659
|
+
recovery = (matches) => {
|
|
42660
|
+
for (const turn of this.#turns.values()) {
|
|
42661
|
+
if (!matches(turn.command)) {
|
|
42662
|
+
continue;
|
|
42663
|
+
}
|
|
42664
|
+
const recovery = turn.recovery();
|
|
42665
|
+
if (recovery !== null) {
|
|
42666
|
+
return recovery;
|
|
42667
|
+
}
|
|
42668
|
+
}
|
|
42669
|
+
for (const completed of this.#completed.values()) {
|
|
42670
|
+
if (matches(completed.command)) {
|
|
42671
|
+
return completed.recovery;
|
|
42672
|
+
}
|
|
42673
|
+
}
|
|
42674
|
+
return null;
|
|
42675
|
+
};
|
|
42543
42676
|
close = async () => {
|
|
42544
42677
|
const turns = [...this.#turns.values()];
|
|
42545
42678
|
for (const turn of turns) {
|
|
42546
42679
|
turn.abortDaemon();
|
|
42547
42680
|
}
|
|
42548
42681
|
await Promise.all(turns.map((turn) => turn.completion));
|
|
42682
|
+
this.#completed.clear();
|
|
42549
42683
|
};
|
|
42550
42684
|
admitAndWait = async (turn, waitSignal, onQueued) => {
|
|
42551
42685
|
const command = turn.command;
|
|
@@ -44853,10 +44987,10 @@ async function resolveSessionDirectories(directories, bindingId, directoryId2, a
|
|
|
44853
44987
|
}
|
|
44854
44988
|
|
|
44855
44989
|
// src/native-sessions/live-history-page.ts
|
|
44856
|
-
function
|
|
44857
|
-
return Buffer.from(JSON.stringify(
|
|
44990
|
+
function encodeLiveHistoryCursor(cursor) {
|
|
44991
|
+
return Buffer.from(JSON.stringify(cursor), "utf8").toString("base64url");
|
|
44858
44992
|
}
|
|
44859
|
-
function
|
|
44993
|
+
function decodeLiveHistoryCursor(cursor, sessionId) {
|
|
44860
44994
|
try {
|
|
44861
44995
|
if (!/^[A-Za-z0-9_-]{1,512}$/u.test(cursor)) {
|
|
44862
44996
|
throw new Error("Invalid cursor alphabet");
|
|
@@ -44866,14 +45000,32 @@ function liveHistoryEndIndex(cursor, sessionId) {
|
|
|
44866
45000
|
throw new Error("Invalid cursor shape");
|
|
44867
45001
|
}
|
|
44868
45002
|
const record2 = Object.fromEntries(Object.entries(value));
|
|
44869
|
-
|
|
45003
|
+
const keys = Object.keys(record2).sort().join(",");
|
|
45004
|
+
const legacyNative = keys === "endIndex,sessionId";
|
|
45005
|
+
const kind = legacyNative ? "native" : record2["kind"];
|
|
45006
|
+
if (keys !== "endIndex,kind,sessionId" && keys !== "endIndex,epoch,kind,sessionId" && !legacyNative || record2["sessionId"] !== sessionId || kind !== "active" && kind !== "native" || typeof record2["endIndex"] !== "number" || !Number.isSafeInteger(record2["endIndex"]) || record2["endIndex"] < 0) {
|
|
44870
45007
|
throw new Error("Invalid cursor target");
|
|
44871
45008
|
}
|
|
44872
|
-
|
|
45009
|
+
if (kind === "active") {
|
|
45010
|
+
if (keys !== "endIndex,epoch,kind,sessionId" || typeof record2["epoch"] !== "string") {
|
|
45011
|
+
throw new Error("Invalid active cursor target");
|
|
45012
|
+
}
|
|
45013
|
+
return { endIndex: record2["endIndex"], epoch: record2["epoch"], kind, sessionId };
|
|
45014
|
+
}
|
|
45015
|
+
if (keys !== "endIndex,kind,sessionId" && !legacyNative) {
|
|
45016
|
+
throw new Error("Invalid native cursor target");
|
|
45017
|
+
}
|
|
45018
|
+
return { endIndex: record2["endIndex"], kind, sessionId };
|
|
44873
45019
|
} catch (cause) {
|
|
44874
45020
|
throw new AcpExecutionError("The native Session history cursor is invalid", { cause }, "invalid_cursor");
|
|
44875
45021
|
}
|
|
44876
45022
|
}
|
|
45023
|
+
function activeHistoryCursor(sessionId, epoch, endIndex) {
|
|
45024
|
+
return encodeLiveHistoryCursor({ endIndex, epoch, kind: "active", sessionId });
|
|
45025
|
+
}
|
|
45026
|
+
function nativeHistoryCursor(sessionId, endIndex) {
|
|
45027
|
+
return encodeLiveHistoryCursor({ endIndex, kind: "native", sessionId });
|
|
45028
|
+
}
|
|
44877
45029
|
function liveHistoryCoveredTurnRanges(history) {
|
|
44878
45030
|
const ranges = [];
|
|
44879
45031
|
let historyIndex = history.messages.length;
|
|
@@ -44894,139 +45046,180 @@ function liveHistoryCoveredTurnRanges(history) {
|
|
|
44894
45046
|
function historyMessageCharacters(message) {
|
|
44895
45047
|
return message.text.length + (message.role === "assistant" ? JSON.stringify(message.activities).length + JSON.stringify(message.content).length : 0);
|
|
44896
45048
|
}
|
|
44897
|
-
function
|
|
45049
|
+
function isNativeHistoryImageMimeType(mimeType) {
|
|
45050
|
+
return mimeType === "image/png" || mimeType === "image/jpeg" || mimeType === "image/gif" || mimeType === "image/webp";
|
|
45051
|
+
}
|
|
45052
|
+
function selectMessagesBefore(messages, endIndex, messageLimit, characterLimit) {
|
|
44898
45053
|
const selected = [];
|
|
44899
45054
|
let remainingCharacters = characterLimit;
|
|
44900
|
-
|
|
45055
|
+
let startIndex = endIndex;
|
|
45056
|
+
for (let index = endIndex - 1; index >= 0 && selected.length < messageLimit; index -= 1) {
|
|
44901
45057
|
const message = messages[index];
|
|
44902
45058
|
if (message === void 0) {
|
|
44903
45059
|
break;
|
|
44904
45060
|
}
|
|
44905
45061
|
const characters = historyMessageCharacters(message);
|
|
44906
|
-
if (characters
|
|
44907
|
-
|
|
44908
|
-
remainingCharacters -= characters;
|
|
44909
|
-
continue;
|
|
44910
|
-
}
|
|
44911
|
-
const structuredCharacters = characters - message.text.length;
|
|
44912
|
-
if (selected.length === 0 && structuredCharacters <= remainingCharacters) {
|
|
44913
|
-
const textCharacters = remainingCharacters - structuredCharacters;
|
|
44914
|
-
selected.unshift({ ...message, text: textCharacters === 0 ? "" : message.text.slice(-textCharacters) });
|
|
44915
|
-
remainingCharacters = 0;
|
|
45062
|
+
if (characters > remainingCharacters) {
|
|
45063
|
+
break;
|
|
44916
45064
|
}
|
|
44917
|
-
|
|
45065
|
+
selected.unshift(message);
|
|
45066
|
+
remainingCharacters -= characters;
|
|
45067
|
+
startIndex = index;
|
|
44918
45068
|
}
|
|
44919
|
-
|
|
44920
|
-
|
|
44921
|
-
|
|
44922
|
-
|
|
44923
|
-
|
|
45069
|
+
if (selected.length === 0 && endIndex > 0) {
|
|
45070
|
+
throw new AcpExecutionError(
|
|
45071
|
+
"A native Session history item exceeds the page budget",
|
|
45072
|
+
{},
|
|
45073
|
+
"history_item_too_large"
|
|
45074
|
+
);
|
|
45075
|
+
}
|
|
45076
|
+
return { characters: characterLimit - remainingCharacters, messages: selected, startIndex };
|
|
44924
45077
|
}
|
|
44925
45078
|
function nativeActiveMessage(message) {
|
|
44926
|
-
|
|
44927
|
-
|
|
44928
|
-
|
|
44929
|
-
|
|
44930
|
-
|
|
44931
|
-
|
|
45079
|
+
if (message.role === "assistant") {
|
|
45080
|
+
return {
|
|
45081
|
+
activities: message.activities,
|
|
45082
|
+
content: message.content,
|
|
45083
|
+
role: "assistant",
|
|
45084
|
+
text: message.text
|
|
45085
|
+
};
|
|
45086
|
+
}
|
|
45087
|
+
const images = (message.images ?? []).flatMap((image) => {
|
|
45088
|
+
if (image.data.length < 4 || image.data.length % 4 !== 0 || !/^[A-Za-z0-9+/]*={0,2}$/u.test(image.data) || !isNativeHistoryImageMimeType(image.mimeType)) {
|
|
45089
|
+
return [];
|
|
45090
|
+
}
|
|
45091
|
+
return [{ data: image.data, mimeType: image.mimeType, name: image.name }];
|
|
45092
|
+
});
|
|
45093
|
+
return { ...images.length === 0 ? {} : { images }, role: "user", text: message.text };
|
|
45094
|
+
}
|
|
45095
|
+
function nativeHistoryCoversCompletedRecovery(history, recovery) {
|
|
45096
|
+
const root = recovery.messages.find((message) => message.role === "user" && message.id === recovery.commandId);
|
|
45097
|
+
if (root === void 0) {
|
|
45098
|
+
return false;
|
|
45099
|
+
}
|
|
45100
|
+
const rootIndex = history.messages.findLastIndex(
|
|
45101
|
+
(message) => message.role === "user" && message.text === root.text
|
|
45102
|
+
);
|
|
45103
|
+
if (rootIndex < 0) {
|
|
45104
|
+
return false;
|
|
45105
|
+
}
|
|
45106
|
+
const nativeAssistant = history.messages.slice(rootIndex + 1).filter((message) => message.role === "assistant");
|
|
45107
|
+
if (nativeAssistant.length === 0) {
|
|
45108
|
+
return false;
|
|
45109
|
+
}
|
|
45110
|
+
const recoveryAssistant = recovery.messages.filter((message) => message.role === "assistant");
|
|
45111
|
+
const recoveryText = recoveryAssistant.map((message) => message.text).join("");
|
|
45112
|
+
const nativeText = nativeAssistant.map((message) => message.text).join("");
|
|
45113
|
+
if (recoveryText.length > 0 && !nativeText.includes(recoveryText)) {
|
|
45114
|
+
return false;
|
|
45115
|
+
}
|
|
45116
|
+
const nativeContent = new Set(
|
|
45117
|
+
nativeAssistant.flatMap((message) => message.content).map((content) => JSON.stringify(content))
|
|
45118
|
+
);
|
|
45119
|
+
return recoveryAssistant.flatMap((message) => message.content).every((content) => nativeContent.has(JSON.stringify(content)));
|
|
44932
45120
|
}
|
|
44933
|
-
function
|
|
45121
|
+
function historyBeforeActiveTurn(history, activeTurn) {
|
|
44934
45122
|
if (history.status === "unavailable") {
|
|
44935
|
-
|
|
44936
|
-
throw new AcpExecutionError("Unavailable native Session history has no cursor", {}, "invalid_cursor");
|
|
44937
|
-
}
|
|
44938
|
-
return { cause: history.cause, nextCursor: null, status: "unavailable" };
|
|
45123
|
+
return history;
|
|
44939
45124
|
}
|
|
44940
|
-
const
|
|
45125
|
+
const activeRoot = activeTurn.messages.find(
|
|
45126
|
+
(message) => message.role === "user" && message.id === activeTurn.commandId
|
|
45127
|
+
);
|
|
45128
|
+
const matchingActiveUserIndex = activeRoot === void 0 ? -1 : activeTurn.state === "completed" || history.messages.at(-1)?.role === "user" && history.messages.at(-1)?.text === activeRoot.text || history.coveredCommandIds.includes(activeTurn.commandId) ? history.messages.findLastIndex((message) => message.role === "user" && message.text === activeRoot.text) : -1;
|
|
45129
|
+
const activeUserIndex = matchingActiveUserIndex >= 0 ? matchingActiveUserIndex : liveHistoryCoveredTurnRanges(history).find((range) => range.commandId === activeTurn.commandId)?.userIndex ?? -1;
|
|
45130
|
+
if (activeUserIndex < 0) {
|
|
45131
|
+
return history;
|
|
45132
|
+
}
|
|
45133
|
+
return {
|
|
45134
|
+
...history,
|
|
45135
|
+
coveredCommandIds: history.coveredCommandIds.filter((id) => id !== activeTurn.commandId),
|
|
45136
|
+
coveredMessageIds: history.coveredMessageIds.filter(
|
|
45137
|
+
(id) => id !== `${activeTurn.commandId}:user` && id !== `${activeTurn.commandId}:agent`
|
|
45138
|
+
),
|
|
45139
|
+
messages: history.messages.slice(0, activeUserIndex)
|
|
45140
|
+
};
|
|
45141
|
+
}
|
|
45142
|
+
function paginateAvailableNativeHistory(history, sessionId, endIndex, limit, characterLimit) {
|
|
44941
45143
|
if (endIndex > history.messages.length) {
|
|
44942
45144
|
throw new AcpExecutionError("The native Session history cursor is invalid", {}, "invalid_cursor");
|
|
44943
45145
|
}
|
|
44944
|
-
const
|
|
45146
|
+
const selected = selectMessagesBefore(history.messages, endIndex, limit, characterLimit);
|
|
44945
45147
|
const coveredMessageIds = new Set(history.coveredMessageIds);
|
|
44946
45148
|
const ranges = liveHistoryCoveredTurnRanges(history);
|
|
44947
45149
|
return {
|
|
44948
|
-
coveredCommandIds: ranges.filter((range) => range.userIndex >= startIndex && range.assistantEndIndex <= endIndex).map((range) => range.commandId),
|
|
45150
|
+
coveredCommandIds: ranges.filter((range) => range.userIndex >= selected.startIndex && range.assistantEndIndex <= endIndex).map((range) => range.commandId),
|
|
44949
45151
|
coveredMessageIds: ranges.flatMap((range) => [
|
|
44950
|
-
...range.userIndex >= startIndex && range.userIndex < endIndex && coveredMessageIds.has(`${range.commandId}:user`) ? [`${range.commandId}:user`] : [],
|
|
44951
|
-
...range.assistantStartIndex >= startIndex && range.assistantEndIndex <= endIndex && coveredMessageIds.has(`${range.commandId}:agent`) ? [`${range.commandId}:agent`] : []
|
|
45152
|
+
...range.userIndex >= selected.startIndex && range.userIndex < endIndex && coveredMessageIds.has(`${range.commandId}:user`) ? [`${range.commandId}:user`] : [],
|
|
45153
|
+
...range.assistantStartIndex >= selected.startIndex && range.assistantEndIndex <= endIndex && coveredMessageIds.has(`${range.commandId}:agent`) ? [`${range.commandId}:agent`] : []
|
|
44952
45154
|
]),
|
|
44953
|
-
messages:
|
|
44954
|
-
nextCursor: startIndex > 0 ?
|
|
45155
|
+
messages: selected.messages,
|
|
45156
|
+
nextCursor: selected.startIndex > 0 ? nativeHistoryCursor(sessionId, selected.startIndex) : null,
|
|
44955
45157
|
status: "available",
|
|
44956
45158
|
truncated: history.truncated
|
|
44957
45159
|
};
|
|
44958
45160
|
}
|
|
44959
|
-
function
|
|
44960
|
-
if (
|
|
44961
|
-
|
|
45161
|
+
function paginateNativeHistory(history, sessionId, cursor, limit) {
|
|
45162
|
+
if (cursor?.kind === "active") {
|
|
45163
|
+
throw new AcpExecutionError("The native Session history cursor is invalid", {}, "invalid_cursor");
|
|
44962
45164
|
}
|
|
44963
45165
|
if (history.status === "unavailable") {
|
|
44964
|
-
|
|
44965
|
-
|
|
44966
|
-
|
|
44967
|
-
|
|
44968
|
-
);
|
|
44969
|
-
return {
|
|
44970
|
-
coveredCommandIds: [],
|
|
44971
|
-
coveredMessageIds: [],
|
|
44972
|
-
messages: bounded3.messages,
|
|
44973
|
-
nextCursor: null,
|
|
44974
|
-
status: "available",
|
|
44975
|
-
truncated: activeTurn.truncated || bounded3.truncated
|
|
44976
|
-
};
|
|
45166
|
+
if (cursor !== null) {
|
|
45167
|
+
throw new AcpExecutionError("Unavailable native Session history has no cursor", {}, "invalid_cursor");
|
|
45168
|
+
}
|
|
45169
|
+
return { cause: history.cause, nextCursor: null, status: "unavailable" };
|
|
44977
45170
|
}
|
|
44978
|
-
|
|
44979
|
-
|
|
45171
|
+
return paginateAvailableNativeHistory(
|
|
45172
|
+
history,
|
|
45173
|
+
sessionId,
|
|
45174
|
+
cursor?.endIndex ?? history.messages.length,
|
|
45175
|
+
limit,
|
|
45176
|
+
SESSION_HISTORY_CHARACTER_LIMIT
|
|
44980
45177
|
);
|
|
44981
|
-
|
|
44982
|
-
|
|
44983
|
-
|
|
44984
|
-
|
|
44985
|
-
const activeUserIndex = matchingActiveUserIndex >= 0 ? matchingActiveUserIndex : liveHistoryCoveredTurnRanges(history).find((range) => range.commandId === activeTurn.commandId)?.userIndex ?? -1;
|
|
44986
|
-
if (activeUserIndex >= 0) {
|
|
44987
|
-
history = {
|
|
44988
|
-
...history,
|
|
44989
|
-
coveredCommandIds: history.coveredCommandIds.filter((id) => id !== activeTurn.commandId),
|
|
44990
|
-
coveredMessageIds: history.coveredMessageIds.filter(
|
|
44991
|
-
(id) => id !== `${activeTurn.commandId}:user` && id !== `${activeTurn.commandId}:agent`
|
|
44992
|
-
),
|
|
44993
|
-
messages: history.messages.slice(0, activeUserIndex + 1)
|
|
44994
|
-
};
|
|
44995
|
-
}
|
|
44996
|
-
const latestNativeMessage = history.messages.at(-1);
|
|
44997
|
-
const firstActiveMessage = activeTurn.messages[0];
|
|
44998
|
-
const preserveNativeRoot = activeUserIndex >= 0 || firstActiveMessage?.role === "user" && latestNativeMessage?.role === "user" && latestNativeMessage.text === firstActiveMessage.text;
|
|
44999
|
-
const activeMessages = activeTurn.messages.slice(preserveNativeRoot ? 1 : 0).map(nativeActiveMessage);
|
|
45000
|
-
const bounded2 = boundedActiveMessages(activeMessages, limit, SESSION_HISTORY_CHARACTER_LIMIT);
|
|
45001
|
-
let historicalMessageCount = 0;
|
|
45002
|
-
let remainingCharacters = SESSION_HISTORY_CHARACTER_LIMIT - bounded2.characters;
|
|
45003
|
-
const historicalMessageLimit = Math.max(0, limit - bounded2.messages.length);
|
|
45004
|
-
for (let index = history.messages.length - 1; index >= 0 && historicalMessageCount < historicalMessageLimit; index -= 1) {
|
|
45005
|
-
const message = history.messages[index];
|
|
45006
|
-
if (message === void 0) {
|
|
45007
|
-
break;
|
|
45008
|
-
}
|
|
45009
|
-
const characters = historyMessageCharacters(message);
|
|
45010
|
-
if (characters > remainingCharacters) {
|
|
45011
|
-
break;
|
|
45012
|
-
}
|
|
45013
|
-
historicalMessageCount += 1;
|
|
45014
|
-
remainingCharacters -= characters;
|
|
45178
|
+
}
|
|
45179
|
+
function paginateActiveTurn(history, activeTurn, sessionId, endIndex, limit) {
|
|
45180
|
+
if (endIndex > activeTurn.messages.length) {
|
|
45181
|
+
throw new AcpExecutionError("The native Session history cursor is invalid", {}, "invalid_cursor");
|
|
45015
45182
|
}
|
|
45016
|
-
const
|
|
45017
|
-
|
|
45018
|
-
|
|
45019
|
-
|
|
45020
|
-
|
|
45021
|
-
|
|
45022
|
-
|
|
45023
|
-
|
|
45183
|
+
const activeMessages = activeTurn.messages.map(nativeActiveMessage);
|
|
45184
|
+
const active = selectMessagesBefore(activeMessages, endIndex, limit, SESSION_HISTORY_CHARACTER_LIMIT);
|
|
45185
|
+
const historicalLimit = limit - active.messages.length;
|
|
45186
|
+
const historicalCharacterLimit = SESSION_HISTORY_CHARACTER_LIMIT - active.characters;
|
|
45187
|
+
const pendingHistoricalCursor = history.status === "available" && history.messages.length > 0 ? nativeHistoryCursor(sessionId, history.messages.length) : null;
|
|
45188
|
+
const latestHistoricalMessage = history.status === "available" ? history.messages.at(-1) : void 0;
|
|
45189
|
+
const historicalPageFits = latestHistoricalMessage === void 0 || historyMessageCharacters(latestHistoricalMessage) <= historicalCharacterLimit;
|
|
45190
|
+
const historical = active.startIndex === 0 && history.status === "available" && historicalLimit > 0 && historicalPageFits ? paginateAvailableNativeHistory(
|
|
45191
|
+
history,
|
|
45192
|
+
sessionId,
|
|
45193
|
+
history.messages.length,
|
|
45194
|
+
historicalLimit,
|
|
45195
|
+
historicalCharacterLimit
|
|
45196
|
+
) : null;
|
|
45024
45197
|
return {
|
|
45025
|
-
|
|
45026
|
-
|
|
45027
|
-
|
|
45198
|
+
coveredCommandIds: historical?.coveredCommandIds ?? [],
|
|
45199
|
+
coveredMessageIds: [
|
|
45200
|
+
...historical?.coveredMessageIds ?? [],
|
|
45201
|
+
...active.startIndex === 0 ? [`${activeTurn.commandId}:user`, `${activeTurn.commandId}:agent`] : []
|
|
45202
|
+
],
|
|
45203
|
+
messages: [...historical?.messages ?? [], ...active.messages],
|
|
45204
|
+
nextCursor: active.startIndex > 0 ? activeHistoryCursor(sessionId, activeTurn.epoch, active.startIndex) : historical === null ? pendingHistoricalCursor : historical.nextCursor,
|
|
45205
|
+
status: "available",
|
|
45206
|
+
truncated: activeTurn.truncated || (historical?.truncated ?? false)
|
|
45028
45207
|
};
|
|
45029
45208
|
}
|
|
45209
|
+
function paginateLiveSessionHistory(history, activeTurn, sessionId, encodedCursor, limit) {
|
|
45210
|
+
const cursor = encodedCursor === null ? null : decodeLiveHistoryCursor(encodedCursor, sessionId);
|
|
45211
|
+
if (activeTurn === null || activeTurn.state === "completed" && history.status === "available" && nativeHistoryCoversCompletedRecovery(history, activeTurn)) {
|
|
45212
|
+
return paginateNativeHistory(history, sessionId, cursor, limit);
|
|
45213
|
+
}
|
|
45214
|
+
const historical = historyBeforeActiveTurn(history, activeTurn);
|
|
45215
|
+
if (cursor?.kind === "native") {
|
|
45216
|
+
return paginateNativeHistory(historical, sessionId, cursor, limit);
|
|
45217
|
+
}
|
|
45218
|
+
if (cursor !== null && cursor.epoch !== activeTurn.epoch) {
|
|
45219
|
+
throw new AcpExecutionError("The active Turn history cursor is stale", {}, "stale_cursor");
|
|
45220
|
+
}
|
|
45221
|
+
return paginateActiveTurn(historical, activeTurn, sessionId, cursor?.endIndex ?? activeTurn.messages.length, limit);
|
|
45222
|
+
}
|
|
45030
45223
|
function mergeLiveSessionHistory(history, activeTurn, sessionId) {
|
|
45031
45224
|
const page = paginateLiveSessionHistory(history, activeTurn, sessionId, null, 1e3);
|
|
45032
45225
|
if (page.status === "unavailable") {
|
|
@@ -45037,7 +45230,7 @@ function mergeLiveSessionHistory(history, activeTurn, sessionId) {
|
|
|
45037
45230
|
coveredMessageIds: page.coveredMessageIds,
|
|
45038
45231
|
messages: page.messages,
|
|
45039
45232
|
status: "available",
|
|
45040
|
-
truncated: page.truncated
|
|
45233
|
+
truncated: page.truncated || page.nextCursor !== null
|
|
45041
45234
|
};
|
|
45042
45235
|
}
|
|
45043
45236
|
|
|
@@ -45556,15 +45749,13 @@ async function executePrompt({
|
|
|
45556
45749
|
});
|
|
45557
45750
|
operations.transitionExecution(command.commandId, "running");
|
|
45558
45751
|
if (availability.disposition === SessionPromptDisposition.Steer) {
|
|
45559
|
-
|
|
45560
|
-
if (updated === null) {
|
|
45752
|
+
if (!recordSteeringPrompt()) {
|
|
45561
45753
|
throw new AcpExecutionError(
|
|
45562
45754
|
"The steering target is unavailable",
|
|
45563
45755
|
{},
|
|
45564
45756
|
"prompt_queue_target_unavailable"
|
|
45565
45757
|
);
|
|
45566
45758
|
}
|
|
45567
|
-
publisher.publish(command.sessionId, updated);
|
|
45568
45759
|
}
|
|
45569
45760
|
const stream = activateAttempt();
|
|
45570
45761
|
publisher.publish(command.sessionId, {
|
|
@@ -45952,7 +46143,7 @@ function createDaemonCommandHandler({
|
|
|
45952
46143
|
resolveEnvironment: resolveAgentCatalogEnvironment
|
|
45953
46144
|
});
|
|
45954
46145
|
const queueChanged = (sessionId) => publisher.publish(sessionId, { kind: "session.queue.changed", sessionId });
|
|
45955
|
-
const
|
|
46146
|
+
const activeTurnRecovery = (sessionId) => activeTurns.recovery((command) => command.sessionId === sessionId);
|
|
45956
46147
|
const nativeRequestHandler = createNativeSessionRequestHandler({
|
|
45957
46148
|
audit,
|
|
45958
46149
|
diagnostics,
|
|
@@ -45973,7 +46164,7 @@ function createDaemonCommandHandler({
|
|
|
45973
46164
|
directories,
|
|
45974
46165
|
audit,
|
|
45975
46166
|
sessions,
|
|
45976
|
-
|
|
46167
|
+
activeTurnRecovery,
|
|
45977
46168
|
signal
|
|
45978
46169
|
);
|
|
45979
46170
|
return;
|
|
@@ -45987,7 +46178,7 @@ function createDaemonCommandHandler({
|
|
|
45987
46178
|
directories,
|
|
45988
46179
|
audit,
|
|
45989
46180
|
sessions,
|
|
45990
|
-
|
|
46181
|
+
activeTurnRecovery,
|
|
45991
46182
|
signal
|
|
45992
46183
|
);
|
|
45993
46184
|
return;
|
|
@@ -46479,6 +46670,7 @@ async function runRelayConnection({
|
|
|
46479
46670
|
}) {
|
|
46480
46671
|
const relayUrl = new URL(config2.relayUrl);
|
|
46481
46672
|
relayUrl.searchParams.set("liveness", "1");
|
|
46673
|
+
relayUrl.searchParams.set("sessionEventDelivery", "reliable-v1");
|
|
46482
46674
|
const socket = await connectRelay(relayUrl, signal);
|
|
46483
46675
|
diagnostics.connectionOpened(connectionEpoch);
|
|
46484
46676
|
const connectionController = new AbortController();
|
|
@@ -46598,6 +46790,10 @@ async function runRelayConnection({
|
|
|
46598
46790
|
const message = decodeServiceToDaemonMessage(encoded);
|
|
46599
46791
|
const kind = message.kind;
|
|
46600
46792
|
if (kind === RUNTIME_LIVENESS_READY_KIND) {
|
|
46793
|
+
publisher.connect(
|
|
46794
|
+
socket,
|
|
46795
|
+
message.sessionEventDelivery === "reliable-v1" ? "reliable" : "ephemeral"
|
|
46796
|
+
);
|
|
46601
46797
|
connectionLiveness.start();
|
|
46602
46798
|
return;
|
|
46603
46799
|
}
|
|
@@ -46605,6 +46801,18 @@ async function runRelayConnection({
|
|
|
46605
46801
|
connectionLiveness.acknowledge(message);
|
|
46606
46802
|
return;
|
|
46607
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
|
+
}
|
|
46608
46816
|
commands.handle(message);
|
|
46609
46817
|
} catch (error62) {
|
|
46610
46818
|
console.error("Mobius daemon rejected a message", error62);
|
|
@@ -46619,7 +46827,6 @@ async function runRelayConnection({
|
|
|
46619
46827
|
socket.addEventListener("close", handleClose);
|
|
46620
46828
|
socket.addEventListener("error", handleError);
|
|
46621
46829
|
socket.addEventListener("message", handleMessage);
|
|
46622
|
-
publisher.connect(socket);
|
|
46623
46830
|
if (signal.aborted) {
|
|
46624
46831
|
stop();
|
|
46625
46832
|
}
|
|
@@ -46630,54 +46837,377 @@ async function runRelayConnection({
|
|
|
46630
46837
|
}
|
|
46631
46838
|
}
|
|
46632
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
|
+
|
|
46633
47059
|
// src/relay-connection/session-event-publisher.ts
|
|
46634
47060
|
import { WebSocket as WebSocket9 } from "undici";
|
|
46635
|
-
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
|
+
}
|
|
46636
47069
|
var DaemonSessionEventPublisher = class {
|
|
46637
|
-
#
|
|
46638
|
-
#
|
|
46639
|
-
|
|
46640
|
-
|
|
46641
|
-
|
|
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);
|
|
46642
47112
|
};
|
|
46643
47113
|
disconnect = (socket) => {
|
|
46644
|
-
if (this.#socket
|
|
46645
|
-
|
|
47114
|
+
if (this.#connection.kind !== "connected" || this.#connection.socket !== socket) {
|
|
47115
|
+
return;
|
|
46646
47116
|
}
|
|
47117
|
+
this.#connection = { kind: "disconnected" };
|
|
47118
|
+
this.#clearInFlight();
|
|
46647
47119
|
};
|
|
46648
47120
|
publish = (sessionId, event) => {
|
|
46649
|
-
|
|
46650
|
-
|
|
46651
|
-
|
|
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
|
+
}
|
|
46652
47133
|
return;
|
|
46653
47134
|
}
|
|
46654
|
-
|
|
46655
|
-
|
|
46656
|
-
|
|
46657
|
-
|
|
47135
|
+
this.#publishNextBatch();
|
|
47136
|
+
};
|
|
47137
|
+
quarantine = (rejection) => {
|
|
47138
|
+
if (!this.#outbox.quarantine(rejection)) {
|
|
46658
47139
|
return;
|
|
46659
47140
|
}
|
|
46660
|
-
|
|
46661
|
-
|
|
46662
|
-
|
|
46663
|
-
kind: SESSION_EVENT_KIND,
|
|
46664
|
-
payload: encodeRecordedSessionEvent(event),
|
|
46665
|
-
sessionId
|
|
46666
|
-
})
|
|
46667
|
-
);
|
|
46668
|
-
} catch {
|
|
46669
|
-
this.#reportDrop();
|
|
46670
|
-
}
|
|
47141
|
+
this.#removeInFlight(rejection.eventId);
|
|
47142
|
+
console.error(`Mobius Relay rejected Session event ${rejection.eventId} with code ${rejection.code}`);
|
|
47143
|
+
this.#publishNextBatch();
|
|
46671
47144
|
};
|
|
46672
|
-
|
|
46673
|
-
this.#
|
|
47145
|
+
#clearInFlight = () => {
|
|
47146
|
+
this.#inFlight.clear();
|
|
47147
|
+
this.#inFlightBytes = 0;
|
|
46674
47148
|
};
|
|
46675
|
-
#
|
|
46676
|
-
if (this.#
|
|
47149
|
+
#publishNextBatch = () => {
|
|
47150
|
+
if (this.#deferredReplay !== null || this.#connection.kind !== "connected") {
|
|
46677
47151
|
return;
|
|
46678
47152
|
}
|
|
46679
|
-
this.#
|
|
46680
|
-
|
|
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
|
+
}
|
|
47192
|
+
};
|
|
47193
|
+
#removeInFlight = (eventId) => {
|
|
47194
|
+
const inFlight = this.#inFlight.get(eventId);
|
|
47195
|
+
if (inFlight === void 0) {
|
|
47196
|
+
return;
|
|
47197
|
+
}
|
|
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
|
+
}
|
|
46681
47211
|
};
|
|
46682
47212
|
};
|
|
46683
47213
|
|
|
@@ -46738,7 +47268,7 @@ async function installDetectedAgentIntegrations(config2, installations, signal)
|
|
|
46738
47268
|
{
|
|
46739
47269
|
agentId: agent.id,
|
|
46740
47270
|
kind: AGENT_INSTALL_REQUEST_KIND,
|
|
46741
|
-
requestId: `automatic-integration-${
|
|
47271
|
+
requestId: `automatic-integration-${randomUUID8()}`
|
|
46742
47272
|
},
|
|
46743
47273
|
signal
|
|
46744
47274
|
);
|
|
@@ -46760,7 +47290,11 @@ async function runDaemon(config2, audit, signal, timer = systemDaemonTimer, opti
|
|
|
46760
47290
|
join18(stateDirectory, "sessions", `${createHash6("sha256").update(config2.channelId).digest("hex")}.sqlite`)
|
|
46761
47291
|
);
|
|
46762
47292
|
const operations = new DaemonOperationState();
|
|
46763
|
-
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
|
+
);
|
|
46764
47298
|
const activeTurns = new DaemonTurnRegistry(operations);
|
|
46765
47299
|
const recordSessionEvent = (sessionId, event) => {
|
|
46766
47300
|
publisher.publish(sessionId, activeTurns.recordEvent(event));
|