@moxt-ai/mobius 0.0.26 → 0.0.28
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/acp/activity.d.ts +1 -0
- package/dist/acp/activity.d.ts.map +1 -1
- package/dist/acp/activity.js +14 -1
- package/dist/acp/activity.js.map +1 -1
- package/dist/acp/session-history.d.ts.map +1 -1
- package/dist/acp/session-history.js +10 -16
- package/dist/acp/session-history.js.map +1 -1
- package/dist/acp/session-manager.d.ts +7 -0
- package/dist/acp/session-manager.d.ts.map +1 -1
- package/dist/acp/session-manager.js +65 -11
- package/dist/acp/session-manager.js.map +1 -1
- package/dist/agent-catalog/publish-agent-catalog.js +3 -3
- package/dist/agent-catalog/publish-agent-catalog.js.map +1 -1
- package/dist/artifacts/runtime-artifact-uploader.d.ts +12 -0
- package/dist/artifacts/runtime-artifact-uploader.d.ts.map +1 -0
- package/dist/artifacts/runtime-artifact-uploader.js +157 -0
- package/dist/artifacts/runtime-artifact-uploader.js.map +1 -0
- package/dist/authorization/local-raster.d.ts +10 -0
- package/dist/authorization/local-raster.d.ts.map +1 -0
- package/dist/authorization/local-raster.js +134 -0
- package/dist/authorization/local-raster.js.map +1 -0
- package/dist/cli.js +1644 -235
- package/dist/cli.js.map +4 -4
- package/dist/index.js +1652 -251
- package/dist/index.js.map +4 -4
- package/dist/native-sessions/codex-history.d.ts.map +1 -1
- package/dist/native-sessions/codex-history.js +11 -11
- package/dist/native-sessions/codex-history.js.map +1 -1
- package/dist/native-sessions/history-images.d.ts +3 -0
- package/dist/native-sessions/history-images.d.ts.map +1 -0
- package/dist/native-sessions/history-images.js +74 -0
- package/dist/native-sessions/history-images.js.map +1 -0
- package/dist/prompt-artifact-resolver.d.ts.map +1 -1
- package/dist/prompt-artifact-resolver.js +7 -4
- package/dist/prompt-artifact-resolver.js.map +1 -1
- package/dist/relay-connection/connect.d.ts.map +1 -1
- package/dist/relay-connection/connect.js +3 -6
- package/dist/relay-connection/connect.js.map +1 -1
- package/dist/relay-connection/runtime-authentication.d.ts +3 -0
- package/dist/relay-connection/runtime-authentication.d.ts.map +1 -0
- package/dist/relay-connection/runtime-authentication.js +14 -0
- package/dist/relay-connection/runtime-authentication.js.map +1 -0
- package/dist/relay-connection/session-event-outbox.d.ts +32 -2
- package/dist/relay-connection/session-event-outbox.d.ts.map +1 -1
- package/dist/relay-connection/session-event-outbox.js +362 -11
- package/dist/relay-connection/session-event-outbox.js.map +1 -1
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +297 -24
- package/dist/run-daemon.js.map +1 -1
- package/dist/runtime.js +1654 -253
- package/dist/runtime.js.map +4 -4
- package/package.json +2 -1
package/dist/run-daemon.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { basename, join } from "node:path";
|
|
3
3
|
import { styleText } from "node:util";
|
|
4
|
-
import { AGENT_ACTIVITY_KIND, AGENT_INSTALL_CANCEL_KIND, AGENT_INSTALL_CANCELLED_KIND, AGENT_INSTALL_COMPLETED_KIND, AGENT_INSTALL_FAILED_KIND, AGENT_INSTALL_PROGRESS_KIND, AGENT_INSTALL_REQUEST_KIND, AGENT_INSTALL_STARTED_KIND, AGENT_MESSAGE_DELTA_KIND, BOT_BINDING_APPROVAL_REQUEST_KIND, BOT_BINDING_APPROVED_KIND, BOT_BINDING_UNAVAILABLE_KIND, BOT_SESSION_PROMPT_KIND, BotExecutionBindingUnavailableEvent, decodeAgentInstallCancelCommand, decodeAgentInstallRequestCommand, decodeBotExecutionBindingApprovalRequest, decodeBotExecutionBindingApprovedEvent, decodeBotExecutionBindingUnavailableEvent, decodeBotSessionPromptCommand, decodeMessageKind, decodeNativeSessionCancel, decodeNativeSessionCommand, decodeNativeSessionTurnForkCommand, decodeRuntimeHeartbeatAckEvent, decodeRuntimeLivenessReadyEvent, decodeSessionCancelCommand, decodeSessionConfigureCommand, decodeSessionElicitationRespondCommand, decodeSessionEventAcknowledgement, decodeSessionEventEnvelope, decodeSessionEventRejection, decodeSessionPermissionRespondCommand, decodeSessionPrepareCommand, decodeSessionPromptCommand, decodeWorkspaceListCommand, encodeAgentActivityEvent, encodeAgentContentEvent, encodeAgentInstallCancelledEvent, encodeAgentInstallCompletedEvent, encodeAgentInstallFailedEvent, encodeAgentInstallProgressEvent, encodeAgentInstallStartedEvent, encodeAgentMessageDeltaEvent, encodeBotExecutionBindingApprovedEvent, encodeBotExecutionBindingUnavailableEvent, encodeNativeSessionResult, encodeSessionCommandsUpdatedEvent, encodeSessionCompletedEvent, encodeSessionConfigUpdatedEvent, encodeSessionElicitationRequestedEvent, encodeSessionElicitationResolvedEvent, encodeSessionFailedEvent, encodeSessionInfoUpdatedEvent, encodeSessionOperationFailedEvent, encodeSessionPermissionRequestedEvent, encodeSessionPermissionResolvedEvent, encodeSessionReadyEvent, encodeSessionStartedEvent, encodeSessionUsageUpdatedEvent, encodeWorkspaceEntriesEvent, NATIVE_SESSION_CANCEL_KIND, NATIVE_SESSION_REQUEST_KIND, NATIVE_SESSION_TURN_FORK_KIND, OwnerBotReplyRoute, RUNTIME_HEARTBEAT_ACK_KIND, RUNTIME_LIVENESS_READY_KIND, SESSION_CANCEL_KIND, SESSION_COMPLETED_KIND, SESSION_CONFIGURE_KIND, SESSION_ELICITATION_RESPOND_KIND, SESSION_EVENT_ACK_KIND, SESSION_EVENT_REJECTED_KIND, SESSION_FAILED_KIND, SESSION_PERMISSION_RESPOND_KIND, SESSION_PREPARE_KIND, SESSION_PROMPT_KIND, SESSION_STARTED_KIND, SessionPromptDisposition, WORKSPACE_LIST_KIND, } from "@moxt-ai/mobius-protocol";
|
|
4
|
+
import { AGENT_ACTIVITY_KIND, AGENT_INSTALL_CANCEL_KIND, AGENT_INSTALL_CANCELLED_KIND, AGENT_INSTALL_COMPLETED_KIND, AGENT_INSTALL_FAILED_KIND, AGENT_INSTALL_PROGRESS_KIND, AGENT_INSTALL_REQUEST_KIND, AGENT_INSTALL_STARTED_KIND, AGENT_MESSAGE_DELTA_KIND, BOT_BINDING_APPROVAL_REQUEST_KIND, BOT_BINDING_APPROVED_KIND, BOT_BINDING_UNAVAILABLE_KIND, BOT_SESSION_PROMPT_KIND, BotExecutionBindingUnavailableEvent, decodeAgentInstallCancelCommand, decodeAgentInstallRequestCommand, decodeBotExecutionBindingApprovalRequest, decodeBotExecutionBindingApprovedEvent, decodeBotExecutionBindingUnavailableEvent, decodeBotSessionPromptCommand, decodeMessageKind, decodeNativeSessionCancel, decodeNativeSessionCommand, decodeNativeSessionTurnForkCommand, decodeRuntimeDelivery, decodeRuntimeDeliveryCommitted, decodeRuntimeHeartbeatAckEvent, decodeRuntimeLivenessReadyEvent, decodeSessionCancelCommand, decodeSessionConfigureCommand, decodeSessionElicitationRespondCommand, decodeSessionEventAcknowledgement, decodeSessionEventEnvelope, decodeSessionEventRejection, decodeSessionPermissionRespondCommand, decodeSessionPrepareCommand, decodeSessionPromptCommand, decodeWorkspaceListCommand, encodeAgentActivityEvent, encodeAgentContentEvent, encodeAgentInstallCancelledEvent, encodeAgentInstallCompletedEvent, encodeAgentInstallFailedEvent, encodeAgentInstallProgressEvent, encodeAgentInstallStartedEvent, encodeAgentMessageDeltaEvent, encodeBotExecutionBindingApprovedEvent, encodeBotExecutionBindingUnavailableEvent, encodeNativeSessionResult, encodeRuntimeDeliveryReceived, encodeSessionCommandsUpdatedEvent, encodeSessionCompletedEvent, encodeSessionConfigUpdatedEvent, encodeSessionElicitationRequestedEvent, encodeSessionElicitationResolvedEvent, encodeSessionFailedEvent, encodeSessionInfoUpdatedEvent, encodeSessionOperationFailedEvent, encodeSessionPermissionRequestedEvent, encodeSessionPermissionResolvedEvent, encodeSessionReadyEvent, encodeSessionStartedEvent, encodeSessionUsageUpdatedEvent, encodeWorkspaceEntriesEvent, NATIVE_SESSION_CANCEL_KIND, NATIVE_SESSION_REQUEST_KIND, NATIVE_SESSION_TURN_FORK_KIND, OwnerBotReplyRoute, RUNTIME_DELIVERY_COMMITTED_KIND, RUNTIME_DELIVERY_KIND, RUNTIME_DELIVERY_RECEIVED_KIND, RUNTIME_HEARTBEAT_ACK_KIND, RUNTIME_LIVENESS_READY_KIND, SESSION_CANCEL_KIND, SESSION_COMPLETED_KIND, SESSION_CONFIGURE_KIND, SESSION_ELICITATION_RESPOND_KIND, SESSION_EVENT_ACK_KIND, SESSION_EVENT_REJECTED_KIND, SESSION_FAILED_KIND, SESSION_PERMISSION_RESPOND_KIND, SESSION_PREPARE_KIND, SESSION_PROMPT_KIND, SESSION_STARTED_KIND, SessionPromptDisposition, WORKSPACE_LIST_KIND, } from "@moxt-ai/mobius-protocol";
|
|
5
5
|
import { fetch, WebSocket } from "undici";
|
|
6
6
|
import { AcpExecutionError, AcpSessionManager } from "./acp/session-manager.js";
|
|
7
7
|
import { SqliteAcpSessionStore } from "./acp/session-store.js";
|
|
@@ -10,6 +10,7 @@ import { LocalAgentCatalogError, LocalAgentDefinition } from "./agent-catalog/lo
|
|
|
10
10
|
import { publishAgentCatalog } from "./agent-catalog/publish-agent-catalog.js";
|
|
11
11
|
import { AgentInstallationManager, CancelledInstallation, FailedInstallation, RequestedInstallation, RunningInstallation, SucceededInstallation, } from "./agent-installation/agent-installation-manager.js";
|
|
12
12
|
import { FixedInstallationExecutor } from "./agent-installation/fixed-installation-executor.js";
|
|
13
|
+
import { uploadRuntimeArtifact } from "./artifacts/runtime-artifact-uploader.js";
|
|
13
14
|
import { LocalAuthorizationError } from "./authorization/local-authorization-error.js";
|
|
14
15
|
import { WorkspaceDirectoryService } from "./authorization/workspace-directory.js";
|
|
15
16
|
import { EmbeddedBotContextApplicator } from "./bots/bot-context-applicator.js";
|
|
@@ -22,7 +23,8 @@ import { PromptArtifactError, resolvePromptArtifacts } from "./prompt-artifact-r
|
|
|
22
23
|
import { connectRelay } from "./relay-connection/connect.js";
|
|
23
24
|
import { RelayConnectionDiagnostics } from "./relay-connection/diagnostics.js";
|
|
24
25
|
import { RelayConnectionLiveness } from "./relay-connection/liveness.js";
|
|
25
|
-
import {
|
|
26
|
+
import { relayHttpEndpoint, runtimeAuthorization } from "./relay-connection/runtime-authentication.js";
|
|
27
|
+
import { RuntimeDeliveryInboxCapacityError, SessionEventOutbox } from "./relay-connection/session-event-outbox.js";
|
|
26
28
|
import { systemDaemonTimer } from "./relay-connection/timer.js";
|
|
27
29
|
import { readStateDirectory } from "./storage/state-directory.js";
|
|
28
30
|
const RELAY_RECONNECT_DELAY_MILLISECONDS = 500;
|
|
@@ -36,10 +38,24 @@ function botIpcSocketPath() {
|
|
|
36
38
|
return join("/tmp", `mobius-bot-${randomUUID()}.sock`);
|
|
37
39
|
}
|
|
38
40
|
class DaemonAcpObserver {
|
|
41
|
+
#config;
|
|
39
42
|
#record;
|
|
40
|
-
constructor(record) {
|
|
43
|
+
constructor(config, record) {
|
|
44
|
+
this.#config = config;
|
|
41
45
|
this.#record = record;
|
|
42
46
|
}
|
|
47
|
+
importLocalImage = async (sessionId, image, signal) => {
|
|
48
|
+
try {
|
|
49
|
+
return (await uploadRuntimeArtifact(this.#config, sessionId, image, signal)).artifactId;
|
|
50
|
+
}
|
|
51
|
+
catch (cause) {
|
|
52
|
+
signal.throwIfAborted();
|
|
53
|
+
if (cause instanceof Error && cause.name === "AbortError") {
|
|
54
|
+
throw cause;
|
|
55
|
+
}
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
};
|
|
43
59
|
onCommands = (event) => {
|
|
44
60
|
this.#record(event.sessionId, encodeSessionCommandsUpdatedEvent(event));
|
|
45
61
|
return Promise.resolve();
|
|
@@ -215,8 +231,7 @@ class ActiveTurnReconciliationError extends Error {
|
|
|
215
231
|
}
|
|
216
232
|
}
|
|
217
233
|
function activeTurnReconciliationEndpoint(config, turn) {
|
|
218
|
-
const endpoint =
|
|
219
|
-
endpoint.protocol = endpoint.protocol === "wss:" ? "https:" : "http:";
|
|
234
|
+
const endpoint = relayHttpEndpoint(config.relayUrl);
|
|
220
235
|
let channelPath = "";
|
|
221
236
|
if (endpoint.pathname.endsWith("/daemon")) {
|
|
222
237
|
channelPath = endpoint.pathname.slice(0, -"/daemon".length);
|
|
@@ -229,7 +244,6 @@ function activeTurnReconciliationEndpoint(config, turn) {
|
|
|
229
244
|
channelPath = `${endpoint.pathname.slice(0, -runtimePath.length)}/channels/${encodeURIComponent(config.channelId)}`;
|
|
230
245
|
}
|
|
231
246
|
endpoint.pathname = `${channelPath}/sessions/${encodeURIComponent(turn.command.sessionId)}/attempts/${encodeURIComponent(turn.attemptId)}/reconciliation`;
|
|
232
|
-
endpoint.searchParams.delete("liveness");
|
|
233
247
|
return endpoint;
|
|
234
248
|
}
|
|
235
249
|
async function reconcileActiveTurn(config, turn, signal) {
|
|
@@ -241,7 +255,7 @@ async function reconcileActiveTurn(config, turn, signal) {
|
|
|
241
255
|
response = await fetch(activeTurnReconciliationEndpoint(config, turn), {
|
|
242
256
|
body: JSON.stringify({ outcome: { kind: "running" } }),
|
|
243
257
|
dispatcher: environmentProxyDispatcher,
|
|
244
|
-
headers: { "content-type": "application/json" },
|
|
258
|
+
headers: { ...runtimeAuthorization(config.relayUrl), "content-type": "application/json" },
|
|
245
259
|
method: "POST",
|
|
246
260
|
signal: requestSignal,
|
|
247
261
|
});
|
|
@@ -311,17 +325,33 @@ class ConnectedDaemonSessionEventState extends DaemonSessionEventConnectionState
|
|
|
311
325
|
}
|
|
312
326
|
};
|
|
313
327
|
}
|
|
314
|
-
const SESSION_EVENT_REPLAY_WINDOW =
|
|
328
|
+
const SESSION_EVENT_REPLAY_WINDOW = 16;
|
|
329
|
+
const SESSION_EVENT_SOURCE_REPLAY_WINDOW = 4;
|
|
330
|
+
const SESSION_EVENT_REPLAY_BYTE_LIMIT = 32 * 1024 * 1024;
|
|
331
|
+
class InFlightSessionEvent {
|
|
332
|
+
byteLength;
|
|
333
|
+
sourceKey;
|
|
334
|
+
constructor(sourceKey, byteLength) {
|
|
335
|
+
this.byteLength = byteLength;
|
|
336
|
+
this.sourceKey = sourceKey;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function sessionEventSourceKey(envelope) {
|
|
340
|
+
return envelope.source.kind === "attempt"
|
|
341
|
+
? `attempt:${envelope.sessionId}:${envelope.source.attemptId}`
|
|
342
|
+
: `runtime:${envelope.sessionId}`;
|
|
343
|
+
}
|
|
315
344
|
class DaemonSessionEventPublisher {
|
|
316
345
|
#outbox;
|
|
317
|
-
#
|
|
346
|
+
#inFlightEvents = new Map();
|
|
347
|
+
#inFlightBytes = 0;
|
|
318
348
|
#connection = new DisconnectedDaemonSessionEventState();
|
|
319
349
|
constructor(outbox) {
|
|
320
350
|
this.#outbox = outbox;
|
|
321
351
|
}
|
|
322
352
|
connect = (socket) => {
|
|
323
353
|
this.#connection = new ConnectedDaemonSessionEventState(socket);
|
|
324
|
-
this.#
|
|
354
|
+
this.#clearInFlight();
|
|
325
355
|
this.#publishNextBatch();
|
|
326
356
|
};
|
|
327
357
|
disconnect = (socket) => {
|
|
@@ -329,10 +359,16 @@ class DaemonSessionEventPublisher {
|
|
|
329
359
|
return;
|
|
330
360
|
}
|
|
331
361
|
this.#connection = this.#connection.disconnect(socket);
|
|
332
|
-
this.#
|
|
362
|
+
this.#clearInFlight();
|
|
333
363
|
};
|
|
334
364
|
recordAttempt = (sessionId, payload, attemptId) => {
|
|
335
|
-
|
|
365
|
+
const kind = decodeMessageKind(payload);
|
|
366
|
+
if (kind === SESSION_COMPLETED_KIND || kind === SESSION_FAILED_KIND) {
|
|
367
|
+
this.#outbox.enqueueCritical(sessionId, payload, attemptId);
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
this.#outbox.enqueue(sessionId, payload, attemptId);
|
|
371
|
+
}
|
|
336
372
|
this.#publishNextBatch();
|
|
337
373
|
};
|
|
338
374
|
recordRuntime = (sessionId, payload) => {
|
|
@@ -343,7 +379,7 @@ class DaemonSessionEventPublisher {
|
|
|
343
379
|
if (!this.#outbox.acknowledge(acknowledgement)) {
|
|
344
380
|
return;
|
|
345
381
|
}
|
|
346
|
-
this.#
|
|
382
|
+
this.#removeInFlight(acknowledgement.eventId);
|
|
347
383
|
this.#publishNextBatch();
|
|
348
384
|
};
|
|
349
385
|
quarantine = (rejection) => {
|
|
@@ -351,24 +387,58 @@ class DaemonSessionEventPublisher {
|
|
|
351
387
|
if (!quarantined) {
|
|
352
388
|
return false;
|
|
353
389
|
}
|
|
354
|
-
this.#
|
|
390
|
+
this.#removeInFlight(rejection.eventId);
|
|
355
391
|
this.#publishNextBatch();
|
|
356
392
|
return true;
|
|
357
393
|
};
|
|
358
394
|
#publishNextBatch = () => {
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
395
|
+
while (this.#inFlightEvents.size < SESSION_EVENT_REPLAY_WINDOW) {
|
|
396
|
+
const sourceCounts = new Map();
|
|
397
|
+
for (const inFlight of this.#inFlightEvents.values()) {
|
|
398
|
+
sourceCounts.set(inFlight.sourceKey, (sourceCounts.get(inFlight.sourceKey) ?? 0) + 1);
|
|
399
|
+
}
|
|
400
|
+
const saturatedSources = [...sourceCounts.entries()]
|
|
401
|
+
.filter(([, count]) => count >= SESSION_EVENT_SOURCE_REPLAY_WINDOW)
|
|
402
|
+
.map(([sourceKey]) => sourceKey);
|
|
403
|
+
const batch = this.#outbox.pendingSourceHeads(SESSION_EVENT_REPLAY_WINDOW - this.#inFlightEvents.size, [...this.#inFlightEvents.keys()], saturatedSources);
|
|
404
|
+
let sent = 0;
|
|
405
|
+
for (const encoded of batch) {
|
|
406
|
+
const envelope = decodeSessionEventEnvelope(encoded);
|
|
407
|
+
const sourceKey = sessionEventSourceKey(envelope);
|
|
408
|
+
const sourceCount = sourceCounts.get(sourceKey) ?? 0;
|
|
409
|
+
if (sourceCount >= SESSION_EVENT_SOURCE_REPLAY_WINDOW) {
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
const byteLength = new TextEncoder().encode(encoded).byteLength;
|
|
413
|
+
if (this.#inFlightBytes + byteLength > SESSION_EVENT_REPLAY_BYTE_LIMIT) {
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (!this.#connection.send(encoded)) {
|
|
417
|
+
this.#clearInFlight();
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
this.#inFlightEvents.set(envelope.eventId, new InFlightSessionEvent(sourceKey, byteLength));
|
|
421
|
+
this.#inFlightBytes += byteLength;
|
|
422
|
+
sourceCounts.set(sourceKey, sourceCount + 1);
|
|
423
|
+
sent += 1;
|
|
424
|
+
}
|
|
425
|
+
if (sent === 0) {
|
|
367
426
|
return;
|
|
368
427
|
}
|
|
369
|
-
this.#inFlightEventIds.add(envelope.eventId);
|
|
370
428
|
}
|
|
371
429
|
};
|
|
430
|
+
#removeInFlight = (eventId) => {
|
|
431
|
+
const inFlight = this.#inFlightEvents.get(eventId);
|
|
432
|
+
if (inFlight === undefined) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
this.#inFlightEvents.delete(eventId);
|
|
436
|
+
this.#inFlightBytes -= inFlight.byteLength;
|
|
437
|
+
};
|
|
438
|
+
#clearInFlight = () => {
|
|
439
|
+
this.#inFlightEvents.clear();
|
|
440
|
+
this.#inFlightBytes = 0;
|
|
441
|
+
};
|
|
372
442
|
}
|
|
373
443
|
class CoalescingDaemonActivityPublisher {
|
|
374
444
|
#pending = new Map();
|
|
@@ -597,6 +667,9 @@ async function executeSessionConfigure(publisher, socket, command, config, direc
|
|
|
597
667
|
}));
|
|
598
668
|
}
|
|
599
669
|
catch (error) {
|
|
670
|
+
if (signal.aborted) {
|
|
671
|
+
signal.throwIfAborted();
|
|
672
|
+
}
|
|
600
673
|
reportOperationFailure(socket, command.requestId, command.sessionId, error);
|
|
601
674
|
}
|
|
602
675
|
}
|
|
@@ -865,6 +938,7 @@ async function publishAgentCatalogUntilAvailable(config, diagnostics, signal, ti
|
|
|
865
938
|
}
|
|
866
939
|
async function runRelayConnection(config, directories, audit, sessions, installations, installationObserver, nativeSessions, outbox, publisher, grants, contexts, applicator, ipc, activeTurns, activeAttemptIds, connectionEpoch, diagnostics, onConnected, signal, timer) {
|
|
867
940
|
const relayUrl = new URL(config.relayUrl);
|
|
941
|
+
relayUrl.searchParams.set("deliveryReceipts", "1");
|
|
868
942
|
relayUrl.searchParams.set("liveness", "1");
|
|
869
943
|
const socket = await connectRelay(relayUrl, signal);
|
|
870
944
|
const reconnectingTurns = [...activeTurns.values()];
|
|
@@ -913,6 +987,189 @@ async function runRelayConnection(config, directories, audit, sessions, installa
|
|
|
913
987
|
closeRelaySocket(socket, code, reason);
|
|
914
988
|
rejectConnection(failure);
|
|
915
989
|
};
|
|
990
|
+
const processingDeliveryIds = new Set();
|
|
991
|
+
const processDeliveredPrompt = async (delivery, command, trackActiveAttempt, execute) => {
|
|
992
|
+
if (command.sessionId !== delivery.sessionId) {
|
|
993
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
994
|
+
}
|
|
995
|
+
const active = activeTurns.get(command.commandId);
|
|
996
|
+
if (active !== undefined) {
|
|
997
|
+
await active.completion;
|
|
998
|
+
return;
|
|
999
|
+
}
|
|
1000
|
+
const receipt = outbox.claimExecution(command.commandId, command.sessionId, delivery.payload);
|
|
1001
|
+
if (receipt.kind === "duplicate") {
|
|
1002
|
+
if (receipt.state === "running") {
|
|
1003
|
+
reportFailure(publisher, command, "agent_execution_interrupted", "The local Agent execution was interrupted before it completed", receipt.attemptId);
|
|
1004
|
+
outbox.transitionExecution(command.commandId, "failed");
|
|
1005
|
+
return;
|
|
1006
|
+
}
|
|
1007
|
+
if (receipt.state !== "dispatching") {
|
|
1008
|
+
return;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
const turn = new ActiveDaemonTurn(command, receipt.attemptId);
|
|
1012
|
+
activeTurns.set(command.commandId, turn);
|
|
1013
|
+
if (trackActiveAttempt) {
|
|
1014
|
+
activeAttemptIds.set(command.sessionId, receipt.attemptId);
|
|
1015
|
+
}
|
|
1016
|
+
try {
|
|
1017
|
+
await execute(receipt.attemptId, turn.signal);
|
|
1018
|
+
}
|
|
1019
|
+
finally {
|
|
1020
|
+
activeTurns.delete(command.commandId);
|
|
1021
|
+
if (activeAttemptIds.get(command.sessionId) === receipt.attemptId) {
|
|
1022
|
+
activeAttemptIds.delete(command.sessionId);
|
|
1023
|
+
}
|
|
1024
|
+
turn.finish();
|
|
1025
|
+
}
|
|
1026
|
+
};
|
|
1027
|
+
const processDeliveredPayload = async (delivery) => {
|
|
1028
|
+
const kind = decodeMessageKind(delivery.payload);
|
|
1029
|
+
if (kind === SESSION_CANCEL_KIND) {
|
|
1030
|
+
const cancellation = decodeSessionCancelCommand(delivery.payload);
|
|
1031
|
+
if (cancellation.sessionId !== delivery.sessionId) {
|
|
1032
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
1033
|
+
}
|
|
1034
|
+
const turn = activeTurns.get(cancellation.commandId);
|
|
1035
|
+
if (turn === undefined || !turn.matches(cancellation) || !turn.requestCancellation()) {
|
|
1036
|
+
return;
|
|
1037
|
+
}
|
|
1038
|
+
audit.record({
|
|
1039
|
+
commandId: cancellation.commandId,
|
|
1040
|
+
event: "session_cancellation_requested",
|
|
1041
|
+
sessionId: cancellation.sessionId,
|
|
1042
|
+
});
|
|
1043
|
+
await sessions.cancelPrompt(cancellation.sessionId);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
if (kind === SESSION_CONFIGURE_KIND) {
|
|
1047
|
+
const command = decodeSessionConfigureCommand(delivery.payload);
|
|
1048
|
+
if (command.sessionId !== delivery.sessionId) {
|
|
1049
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
1050
|
+
}
|
|
1051
|
+
await executeSessionConfigure(publisher, socket, command, config, directories, audit, sessions, connectionController.signal);
|
|
1052
|
+
return;
|
|
1053
|
+
}
|
|
1054
|
+
if (kind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
1055
|
+
const command = decodeSessionPermissionRespondCommand(delivery.payload);
|
|
1056
|
+
if (command.sessionId !== delivery.sessionId) {
|
|
1057
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
1058
|
+
}
|
|
1059
|
+
try {
|
|
1060
|
+
await sessions.respondPermission(command);
|
|
1061
|
+
audit.record({
|
|
1062
|
+
commandId: command.interactionId,
|
|
1063
|
+
event: "session_permission_resolved",
|
|
1064
|
+
sessionId: command.sessionId,
|
|
1065
|
+
});
|
|
1066
|
+
}
|
|
1067
|
+
catch (error) {
|
|
1068
|
+
reportOperationFailure(socket, command.interactionId, command.sessionId, error);
|
|
1069
|
+
}
|
|
1070
|
+
return;
|
|
1071
|
+
}
|
|
1072
|
+
if (kind === SESSION_ELICITATION_RESPOND_KIND) {
|
|
1073
|
+
const command = decodeSessionElicitationRespondCommand(delivery.payload);
|
|
1074
|
+
if (command.sessionId !== delivery.sessionId) {
|
|
1075
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
1076
|
+
}
|
|
1077
|
+
try {
|
|
1078
|
+
await sessions.respondElicitation(command);
|
|
1079
|
+
audit.record({
|
|
1080
|
+
commandId: command.interactionId,
|
|
1081
|
+
event: "session_elicitation_resolved",
|
|
1082
|
+
sessionId: command.sessionId,
|
|
1083
|
+
});
|
|
1084
|
+
}
|
|
1085
|
+
catch (error) {
|
|
1086
|
+
reportOperationFailure(socket, command.interactionId, command.sessionId, error);
|
|
1087
|
+
}
|
|
1088
|
+
return;
|
|
1089
|
+
}
|
|
1090
|
+
if (kind === BOT_SESSION_PROMPT_KIND) {
|
|
1091
|
+
const command = decodeBotSessionPromptCommand(delivery.payload);
|
|
1092
|
+
await processDeliveredPrompt(delivery, command, true, (attemptId, executionSignal) => executeBotPrompt(outbox, publisher, command, config, directories, audit, sessions, grants, contexts, applicator, ipc, attemptId, timer, executionSignal));
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
if (kind === SESSION_PROMPT_KIND) {
|
|
1096
|
+
const command = decodeSessionPromptCommand(delivery.payload);
|
|
1097
|
+
await processDeliveredPrompt(delivery, command, command.disposition !== SessionPromptDisposition.Steer, (attemptId, executionSignal) => executePrompt(outbox, publisher, command, config, directories, audit, sessions, attemptId, timer, executionSignal));
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
throw new Error("Runtime delivery contains an unsupported protocol message");
|
|
1101
|
+
};
|
|
1102
|
+
const processRuntimeDelivery = (delivery, state) => {
|
|
1103
|
+
if (state === "processed" || processingDeliveryIds.has(delivery.deliveryId)) {
|
|
1104
|
+
return;
|
|
1105
|
+
}
|
|
1106
|
+
processingDeliveryIds.add(delivery.deliveryId);
|
|
1107
|
+
void processDeliveredPayload(delivery)
|
|
1108
|
+
.then(() => {
|
|
1109
|
+
outbox.markRuntimeDeliveryProcessed(delivery.deliveryId, delivery.sessionId);
|
|
1110
|
+
})
|
|
1111
|
+
.catch((error) => {
|
|
1112
|
+
if (connectionController.signal.aborted) {
|
|
1113
|
+
return;
|
|
1114
|
+
}
|
|
1115
|
+
console.error("Mobius daemon could not process a durable Runtime delivery", error);
|
|
1116
|
+
failAndClose(new Error("Runtime delivery processing failed", { cause: error }), 4008, "Invalid Runtime delivery", "delivery");
|
|
1117
|
+
})
|
|
1118
|
+
.finally(() => processingDeliveryIds.delete(delivery.deliveryId));
|
|
1119
|
+
};
|
|
1120
|
+
const receiveRuntimeDelivery = (delivery) => {
|
|
1121
|
+
const payloadKind = decodeMessageKind(delivery.payload);
|
|
1122
|
+
let payloadSessionId = "";
|
|
1123
|
+
if (payloadKind === SESSION_CANCEL_KIND) {
|
|
1124
|
+
payloadSessionId = decodeSessionCancelCommand(delivery.payload).sessionId;
|
|
1125
|
+
}
|
|
1126
|
+
else if (payloadKind === SESSION_CONFIGURE_KIND) {
|
|
1127
|
+
payloadSessionId = decodeSessionConfigureCommand(delivery.payload).sessionId;
|
|
1128
|
+
}
|
|
1129
|
+
else if (payloadKind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
1130
|
+
payloadSessionId = decodeSessionPermissionRespondCommand(delivery.payload).sessionId;
|
|
1131
|
+
}
|
|
1132
|
+
else if (payloadKind === SESSION_ELICITATION_RESPOND_KIND) {
|
|
1133
|
+
payloadSessionId = decodeSessionElicitationRespondCommand(delivery.payload).sessionId;
|
|
1134
|
+
}
|
|
1135
|
+
else if (payloadKind === SESSION_PROMPT_KIND) {
|
|
1136
|
+
payloadSessionId = decodeSessionPromptCommand(delivery.payload).sessionId;
|
|
1137
|
+
}
|
|
1138
|
+
else if (payloadKind === BOT_SESSION_PROMPT_KIND) {
|
|
1139
|
+
payloadSessionId = decodeBotSessionPromptCommand(delivery.payload).sessionId;
|
|
1140
|
+
}
|
|
1141
|
+
else {
|
|
1142
|
+
throw new Error("Runtime delivery contains an unsupported protocol message");
|
|
1143
|
+
}
|
|
1144
|
+
if (payloadSessionId !== delivery.sessionId) {
|
|
1145
|
+
throw new Error("Runtime delivery Session identity does not match its payload");
|
|
1146
|
+
}
|
|
1147
|
+
const stored = outbox.claimRuntimeDelivery(delivery);
|
|
1148
|
+
send(socket, encodeRuntimeDeliveryReceived({
|
|
1149
|
+
deliveryId: delivery.deliveryId,
|
|
1150
|
+
kind: RUNTIME_DELIVERY_RECEIVED_KIND,
|
|
1151
|
+
sessionId: delivery.sessionId,
|
|
1152
|
+
}));
|
|
1153
|
+
processRuntimeDelivery(delivery, stored.state);
|
|
1154
|
+
};
|
|
1155
|
+
const resumeRuntimeDeliveries = () => {
|
|
1156
|
+
for (const stored of outbox.runtimeDeliveries()) {
|
|
1157
|
+
const delivery = {
|
|
1158
|
+
deliveryId: stored.deliveryId,
|
|
1159
|
+
kind: RUNTIME_DELIVERY_KIND,
|
|
1160
|
+
payload: stored.payload,
|
|
1161
|
+
sessionId: stored.sessionId,
|
|
1162
|
+
};
|
|
1163
|
+
if (stored.state !== "relay_committed") {
|
|
1164
|
+
send(socket, encodeRuntimeDeliveryReceived({
|
|
1165
|
+
deliveryId: stored.deliveryId,
|
|
1166
|
+
kind: RUNTIME_DELIVERY_RECEIVED_KIND,
|
|
1167
|
+
sessionId: stored.sessionId,
|
|
1168
|
+
}));
|
|
1169
|
+
}
|
|
1170
|
+
processRuntimeDelivery(delivery, stored.state);
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
916
1173
|
const connectionLiveness = new RelayConnectionLiveness(timer, {
|
|
917
1174
|
send: (message) => send(socket, message),
|
|
918
1175
|
unresponsive: (heartbeatId, timeoutMilliseconds) => {
|
|
@@ -946,6 +1203,17 @@ async function runRelayConnection(config, directories, audit, sessions, installa
|
|
|
946
1203
|
}
|
|
947
1204
|
try {
|
|
948
1205
|
const kind = decodeMessageKind(encoded);
|
|
1206
|
+
if (kind === RUNTIME_DELIVERY_COMMITTED_KIND) {
|
|
1207
|
+
const committed = decodeRuntimeDeliveryCommitted(encoded);
|
|
1208
|
+
if (!outbox.markRuntimeDeliveryCommitted(committed.deliveryId, committed.sessionId)) {
|
|
1209
|
+
console.warn("Mobius daemon ignored confirmation for an unknown Runtime delivery");
|
|
1210
|
+
}
|
|
1211
|
+
return;
|
|
1212
|
+
}
|
|
1213
|
+
if (kind === RUNTIME_DELIVERY_KIND) {
|
|
1214
|
+
receiveRuntimeDelivery(decodeRuntimeDelivery(encoded));
|
|
1215
|
+
return;
|
|
1216
|
+
}
|
|
949
1217
|
if (kind === SESSION_EVENT_ACK_KIND) {
|
|
950
1218
|
publisher.acknowledge(decodeSessionEventAcknowledgement(encoded));
|
|
951
1219
|
return;
|
|
@@ -1291,6 +1559,10 @@ async function runRelayConnection(config, directories, audit, sessions, installa
|
|
|
1291
1559
|
}
|
|
1292
1560
|
catch (error) {
|
|
1293
1561
|
console.error("Mobius daemon rejected a message", error);
|
|
1562
|
+
if (error instanceof RuntimeDeliveryInboxCapacityError) {
|
|
1563
|
+
failAndClose(error, 4013, "Runtime delivery queue full", "delivery_backpressure");
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1294
1566
|
failAndClose(new Error("Relay sent an invalid protocol message", { cause: error }), 4008, "Invalid protocol message", "protocol");
|
|
1295
1567
|
}
|
|
1296
1568
|
};
|
|
@@ -1302,6 +1574,7 @@ async function runRelayConnection(config, directories, audit, sessions, installa
|
|
|
1302
1574
|
return;
|
|
1303
1575
|
}
|
|
1304
1576
|
publisher.connect(socket);
|
|
1577
|
+
resumeRuntimeDeliveries();
|
|
1305
1578
|
}, (error) => {
|
|
1306
1579
|
if (finished) {
|
|
1307
1580
|
return;
|
|
@@ -1348,7 +1621,7 @@ export async function runDaemon(config, audit, signal, timer = systemDaemonTimer
|
|
|
1348
1621
|
}
|
|
1349
1622
|
publisher.recordAttempt(sessionId, payload, attemptId);
|
|
1350
1623
|
};
|
|
1351
|
-
const sessions = new AcpSessionManager(new DaemonAcpObserver(recordSessionEvent), sessionStore);
|
|
1624
|
+
const sessions = new AcpSessionManager(new DaemonAcpObserver(config, recordSessionEvent), sessionStore);
|
|
1352
1625
|
const directories = new WorkspaceDirectoryService(config.bindingId, config.defaultWorkspace);
|
|
1353
1626
|
const installationObserver = new DaemonInstallationObserver();
|
|
1354
1627
|
const diagnostics = new RelayConnectionDiagnostics(audit, config.channelId, config.relayUrl);
|