@moxt-ai/mobius 0.0.5 → 0.0.6
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/native-session.d.ts +30 -0
- package/dist/acp/native-session.d.ts.map +1 -0
- package/dist/acp/native-session.js +25 -0
- package/dist/acp/native-session.js.map +1 -0
- package/dist/acp/session-history.d.ts +9 -0
- package/dist/acp/session-history.d.ts.map +1 -0
- package/dist/acp/session-history.js +69 -0
- package/dist/acp/session-history.js.map +1 -0
- package/dist/acp/session-manager.d.ts +16 -5
- package/dist/acp/session-manager.d.ts.map +1 -1
- package/dist/acp/session-manager.js +211 -12
- package/dist/acp/session-manager.js.map +1 -1
- package/dist/acp/session-store.d.ts +9 -0
- package/dist/acp/session-store.d.ts.map +1 -1
- package/dist/acp/session-store.js +43 -0
- package/dist/acp/session-store.js.map +1 -1
- package/dist/agent-catalog/discover-local-agents.d.ts +1 -1
- package/dist/agent-catalog/discover-local-agents.d.ts.map +1 -1
- package/dist/agent-catalog/discover-local-agents.js +19 -30
- package/dist/agent-catalog/discover-local-agents.js.map +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.d.ts +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.d.ts.map +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.js +1 -1
- package/dist/agent-installation/automatic-adapter-provisioner.js.map +1 -1
- package/dist/agent-installation/fixed-installation-executor.d.ts +1 -1
- package/dist/agent-installation/fixed-installation-executor.d.ts.map +1 -1
- package/dist/agent-installation/fixed-installation-executor.js +9 -18
- package/dist/agent-installation/fixed-installation-executor.js.map +1 -1
- package/dist/agent-installation/managed-adapter-installer.d.ts +0 -1
- package/dist/agent-installation/managed-adapter-installer.d.ts.map +1 -1
- package/dist/agent-installation/managed-adapter-installer.js +1 -3
- package/dist/agent-installation/managed-adapter-installer.js.map +1 -1
- package/dist/authorization/local-authorization-error.d.ts +5 -0
- package/dist/authorization/local-authorization-error.d.ts.map +1 -0
- package/dist/authorization/local-authorization-error.js +8 -0
- package/dist/authorization/local-authorization-error.js.map +1 -0
- package/dist/authorization/workspace-directory.d.ts +16 -5
- package/dist/authorization/workspace-directory.d.ts.map +1 -1
- package/dist/authorization/workspace-directory.js +109 -119
- package/dist/authorization/workspace-directory.js.map +1 -1
- package/dist/cli.js +1851 -543
- package/dist/cli.js.map +4 -4
- package/dist/config.d.ts +3 -3
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +7 -5
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1866 -593
- package/dist/index.js.map +4 -4
- package/dist/native-sessions/codex-history.d.ts +11 -0
- package/dist/native-sessions/codex-history.d.ts.map +1 -0
- package/dist/native-sessions/codex-history.js +223 -0
- package/dist/native-sessions/codex-history.js.map +1 -0
- package/dist/native-sessions/service.d.ts +14 -0
- package/dist/native-sessions/service.d.ts.map +1 -0
- package/dist/native-sessions/service.js +249 -0
- package/dist/native-sessions/service.js.map +1 -0
- package/dist/pairing/pair-local-machine.d.ts.map +1 -1
- package/dist/pairing/pair-local-machine.js +29 -26
- package/dist/pairing/pair-local-machine.js.map +1 -1
- package/dist/process/executable.d.ts +1 -1
- package/dist/process/executable.d.ts.map +1 -1
- package/dist/process/executable.js +3 -11
- package/dist/process/executable.js.map +1 -1
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +274 -193
- package/dist/run-daemon.js.map +1 -1
- package/package.json +3 -2
- package/dist/authorization/authorize-binding.d.ts +0 -6
- package/dist/authorization/authorize-binding.d.ts.map +0 -1
- package/dist/authorization/authorize-binding.js +0 -30
- package/dist/authorization/authorize-binding.js.map +0 -1
package/dist/run-daemon.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { basename } from "node:path";
|
|
3
|
-
import { AGENT_ACTIVITY_KIND, AGENT_INSTALL_AWAITING_APPROVAL_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, decodeAgentInstallCancelCommand, decodeAgentInstallRequestCommand, decodeMessageKind, decodeRuntimeHeartbeatAckEvent, decodeRuntimeLivenessReadyEvent, decodeSessionCancelCommand, decodeSessionConfigureCommand, decodeSessionElicitationRespondCommand, decodeSessionPermissionRespondCommand, decodeSessionPrepareCommand, decodeSessionPromptCommand, decodeWorkspaceListCommand, encodeAgentActivityEvent, encodeAgentContentEvent, encodeAgentInstallAwaitingApprovalEvent, encodeAgentInstallCancelledEvent, encodeAgentInstallCompletedEvent, encodeAgentInstallFailedEvent, encodeAgentInstallProgressEvent, encodeAgentInstallStartedEvent, encodeAgentMessageDeltaEvent, encodeSessionCommandsUpdatedEvent, encodeSessionCompletedEvent, encodeSessionConfigUpdatedEvent, encodeSessionElicitationRequestedEvent, encodeSessionElicitationResolvedEvent, encodeSessionFailedEvent, encodeSessionInfoUpdatedEvent, encodeSessionOperationFailedEvent, encodeSessionPermissionRequestedEvent, encodeSessionPermissionResolvedEvent, encodeSessionReadyEvent, encodeSessionStartedEvent, encodeSessionUsageUpdatedEvent, encodeWorkspaceEntriesEvent, MOBIUS_PROTOCOL_VERSION, RUNTIME_HEARTBEAT_ACK_KIND, RUNTIME_LIVENESS_READY_KIND, SESSION_CANCEL_KIND, SESSION_COMPLETED_KIND, SESSION_CONFIGURE_KIND, SESSION_ELICITATION_RESPOND_KIND, SESSION_FAILED_KIND, SESSION_PERMISSION_RESPOND_KIND, SESSION_PREPARE_KIND, SESSION_PROMPT_KIND, SESSION_STARTED_KIND, WORKSPACE_LIST_KIND, } from "@moxt-ai/mobius-protocol";
|
|
3
|
+
import { AGENT_ACTIVITY_KIND, AGENT_INSTALL_AWAITING_APPROVAL_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, decodeAgentInstallCancelCommand, decodeAgentInstallRequestCommand, decodeMessageKind, decodeNativeSessionCancel, decodeNativeSessionCommand, decodeRuntimeHeartbeatAckEvent, decodeRuntimeLivenessReadyEvent, decodeSessionCancelCommand, decodeSessionConfigureCommand, decodeSessionElicitationRespondCommand, decodeSessionPermissionRespondCommand, decodeSessionPrepareCommand, decodeSessionPromptCommand, decodeWorkspaceListCommand, encodeAgentActivityEvent, encodeAgentContentEvent, encodeAgentInstallAwaitingApprovalEvent, encodeAgentInstallCancelledEvent, encodeAgentInstallCompletedEvent, encodeAgentInstallFailedEvent, encodeAgentInstallProgressEvent, encodeAgentInstallStartedEvent, encodeAgentMessageDeltaEvent, encodeNativeSessionResult, encodeSessionCommandsUpdatedEvent, encodeSessionCompletedEvent, encodeSessionConfigUpdatedEvent, encodeSessionElicitationRequestedEvent, encodeSessionElicitationResolvedEvent, encodeSessionFailedEvent, encodeSessionInfoUpdatedEvent, encodeSessionOperationFailedEvent, encodeSessionPermissionRequestedEvent, encodeSessionPermissionResolvedEvent, encodeSessionReadyEvent, encodeSessionStartedEvent, encodeSessionUsageUpdatedEvent, encodeWorkspaceEntriesEvent, MOBIUS_PROTOCOL_VERSION, NATIVE_SESSION_CANCEL_KIND, NATIVE_SESSION_REQUEST_KIND, RUNTIME_HEARTBEAT_ACK_KIND, RUNTIME_LIVENESS_READY_KIND, SESSION_CANCEL_KIND, SESSION_COMPLETED_KIND, SESSION_CONFIGURE_KIND, SESSION_ELICITATION_RESPOND_KIND, SESSION_FAILED_KIND, SESSION_PERMISSION_RESPOND_KIND, SESSION_PREPARE_KIND, SESSION_PROMPT_KIND, SESSION_STARTED_KIND, WORKSPACE_LIST_KIND, } from "@moxt-ai/mobius-protocol";
|
|
4
4
|
import { WebSocket } from "undici";
|
|
5
5
|
import { AcpExecutionError, AcpSessionManager, NullAcpSessionObserver, } from "./acp/session-manager.js";
|
|
6
6
|
import { SqliteAcpSessionStore } from "./acp/session-store.js";
|
|
@@ -12,8 +12,9 @@ import { AutomaticAdapterProvisioner } from "./agent-installation/automatic-adap
|
|
|
12
12
|
import { FixedInstallationExecutor } from "./agent-installation/fixed-installation-executor.js";
|
|
13
13
|
import { ConsoleInstallationAuthorizer } from "./agent-installation/local-installation-authorizer.js";
|
|
14
14
|
import { ManagedAdapterInstaller } from "./agent-installation/managed-adapter-installer.js";
|
|
15
|
-
import { LocalAuthorizationError } from "./authorization/
|
|
15
|
+
import { LocalAuthorizationError } from "./authorization/local-authorization-error.js";
|
|
16
16
|
import { WorkspaceDirectoryService } from "./authorization/workspace-directory.js";
|
|
17
|
+
import { NativeSessionService } from "./native-sessions/service.js";
|
|
17
18
|
import { connectRelay } from "./relay-connection/connect.js";
|
|
18
19
|
import { RelayConnectionLiveness } from "./relay-connection/liveness.js";
|
|
19
20
|
import { systemDaemonTimer, } from "./relay-connection/timer.js";
|
|
@@ -298,13 +299,13 @@ function reportOperationFailure(socket, requestId, sessionId, error) {
|
|
|
298
299
|
sessionId,
|
|
299
300
|
}));
|
|
300
301
|
}
|
|
301
|
-
async function resolveSessionDirectories(directories, bindingId, directoryId, additionalDirectoryIds) {
|
|
302
|
-
const cwd = await directories.resolve(bindingId, directoryId);
|
|
302
|
+
async function resolveSessionDirectories(directories, bindingId, directoryId, additionalDirectoryIds, signal) {
|
|
303
|
+
const cwd = await directories.resolve(bindingId, directoryId, signal);
|
|
303
304
|
const additionalDirectories = [];
|
|
304
305
|
const additionalDirectoryEntries = [];
|
|
305
306
|
const unique = new Set([cwd]);
|
|
306
307
|
for (const additionalDirectoryId of additionalDirectoryIds) {
|
|
307
|
-
const directory = await directories.resolve(bindingId, additionalDirectoryId);
|
|
308
|
+
const directory = await directories.resolve(bindingId, additionalDirectoryId, signal);
|
|
308
309
|
if (unique.has(directory)) {
|
|
309
310
|
throw new LocalAuthorizationError("Workspace directories must resolve to distinct locations");
|
|
310
311
|
}
|
|
@@ -317,9 +318,9 @@ async function resolveSessionDirectories(directories, bindingId, directoryId, ad
|
|
|
317
318
|
}
|
|
318
319
|
return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
|
|
319
320
|
}
|
|
320
|
-
async function executeWorkspaceList(socket, command, directories, audit) {
|
|
321
|
+
async function executeWorkspaceList(socket, command, directories, audit, signal) {
|
|
321
322
|
try {
|
|
322
|
-
const listing = await directories.list(command.bindingId, command.directoryId);
|
|
323
|
+
const listing = await directories.list(command.bindingId, command.directoryId, signal);
|
|
323
324
|
audit.record({
|
|
324
325
|
commandId: command.requestId,
|
|
325
326
|
event: "workspace_list_authorized",
|
|
@@ -342,7 +343,7 @@ async function executeWorkspaceList(socket, command, directories, audit) {
|
|
|
342
343
|
}
|
|
343
344
|
async function executeSessionPrepare(socket, command, config, directories, audit, sessions, signal) {
|
|
344
345
|
try {
|
|
345
|
-
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
|
|
346
|
+
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds, signal);
|
|
346
347
|
const agent = config.catalog.find(command.agentId);
|
|
347
348
|
const prepared = await sessions.prepareSession({
|
|
348
349
|
additionalDirectories: resolved.additionalDirectories,
|
|
@@ -351,7 +352,7 @@ async function executeSessionPrepare(socket, command, config, directories, audit
|
|
|
351
352
|
sessionId: command.sessionId,
|
|
352
353
|
signal,
|
|
353
354
|
});
|
|
354
|
-
const listing = await directories.list(command.bindingId, command.directoryId);
|
|
355
|
+
const listing = await directories.list(command.bindingId, command.directoryId, signal);
|
|
355
356
|
audit.record({
|
|
356
357
|
commandId: command.requestId,
|
|
357
358
|
event: "session_prepared",
|
|
@@ -380,7 +381,7 @@ async function executeSessionPrepare(socket, command, config, directories, audit
|
|
|
380
381
|
}
|
|
381
382
|
async function executeSessionConfigure(socket, command, config, directories, audit, sessions, signal) {
|
|
382
383
|
try {
|
|
383
|
-
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
|
|
384
|
+
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds, signal);
|
|
384
385
|
const configOptions = await sessions.configureSession({
|
|
385
386
|
additionalDirectories: resolved.additionalDirectories,
|
|
386
387
|
agent: config.catalog.find(command.agentId),
|
|
@@ -412,7 +413,7 @@ async function executeSessionConfigure(socket, command, config, directories, aud
|
|
|
412
413
|
}
|
|
413
414
|
async function executePrompt(socket, command, config, directories, audit, sessions, signal) {
|
|
414
415
|
try {
|
|
415
|
-
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
|
|
416
|
+
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds, signal);
|
|
416
417
|
const agent = config.catalog.find(command.agentId);
|
|
417
418
|
const processAttemptId = randomUUID();
|
|
418
419
|
audit.record({
|
|
@@ -510,215 +511,295 @@ function waitForRelayRetry(signal, timer) {
|
|
|
510
511
|
signal.addEventListener("abort", finish, { once: true });
|
|
511
512
|
});
|
|
512
513
|
}
|
|
513
|
-
async function runRelayConnection(config, directories, audit, sessions, installations, installationObserver, signal, timer) {
|
|
514
|
+
async function runRelayConnection(config, directories, audit, sessions, installations, installationObserver, nativeSessions, signal, timer) {
|
|
514
515
|
const relayUrl = new URL(config.relayUrl);
|
|
515
516
|
relayUrl.searchParams.set("liveness", "1");
|
|
516
517
|
const socket = await connectRelay(relayUrl, signal);
|
|
517
518
|
const activeTurns = new Map();
|
|
518
519
|
const connectionController = new AbortController();
|
|
520
|
+
const nativeRequests = new Map();
|
|
519
521
|
sessions.setObserver(new DaemonAcpObserver(socket));
|
|
520
522
|
installationObserver.connect(socket);
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
turn.
|
|
534
|
-
|
|
535
|
-
activeTurns.clear();
|
|
536
|
-
};
|
|
537
|
-
const resolveConnection = () => {
|
|
538
|
-
if (finished) {
|
|
539
|
-
return;
|
|
540
|
-
}
|
|
541
|
-
finished = true;
|
|
542
|
-
cleanup();
|
|
543
|
-
resolve();
|
|
544
|
-
};
|
|
545
|
-
const rejectConnection = () => {
|
|
546
|
-
if (finished) {
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
finished = true;
|
|
550
|
-
cleanup();
|
|
551
|
-
reject(new Error("Relay WebSocket failed"));
|
|
552
|
-
};
|
|
553
|
-
const stop = () => {
|
|
554
|
-
socket.close(1000, "Daemon stopped");
|
|
555
|
-
resolveConnection();
|
|
556
|
-
};
|
|
557
|
-
const refreshConnection = () => {
|
|
558
|
-
socket.close(1000, "Refreshing idle relay connection");
|
|
559
|
-
resolveConnection();
|
|
560
|
-
};
|
|
561
|
-
const connectionLiveness = new RelayConnectionLiveness(timer, {
|
|
562
|
-
send: (message) => send(socket, message),
|
|
563
|
-
unresponsive: () => {
|
|
564
|
-
socket.close(4001, "Relay liveness check timed out");
|
|
565
|
-
resolveConnection();
|
|
566
|
-
},
|
|
567
|
-
});
|
|
568
|
-
const connectionRefresh = new RelayConnectionRefresh(timer, () => activeTurns.size === 0, refreshConnection);
|
|
569
|
-
signal.addEventListener("abort", stop, { once: true });
|
|
570
|
-
const handleClose = () => {
|
|
571
|
-
resolveConnection();
|
|
572
|
-
};
|
|
573
|
-
const handleError = () => {
|
|
574
|
-
if (signal.aborted) {
|
|
575
|
-
resolveConnection();
|
|
576
|
-
return;
|
|
577
|
-
}
|
|
578
|
-
rejectConnection();
|
|
579
|
-
};
|
|
580
|
-
const handleMessage = (event) => {
|
|
581
|
-
const encoded = event.data;
|
|
582
|
-
if (typeof encoded !== "string") {
|
|
583
|
-
socket.close(4003, "Text message required");
|
|
584
|
-
rejectConnection();
|
|
585
|
-
return;
|
|
586
|
-
}
|
|
587
|
-
try {
|
|
588
|
-
const kind = decodeMessageKind(encoded);
|
|
589
|
-
if (kind === RUNTIME_LIVENESS_READY_KIND) {
|
|
590
|
-
decodeRuntimeLivenessReadyEvent(encoded);
|
|
591
|
-
connectionLiveness.start();
|
|
592
|
-
return;
|
|
593
|
-
}
|
|
594
|
-
if (kind === RUNTIME_HEARTBEAT_ACK_KIND) {
|
|
595
|
-
connectionLiveness.acknowledge(decodeRuntimeHeartbeatAckEvent(encoded));
|
|
596
|
-
return;
|
|
523
|
+
try {
|
|
524
|
+
await new Promise((resolve, reject) => {
|
|
525
|
+
let finished = false;
|
|
526
|
+
const cleanup = () => {
|
|
527
|
+
connectionRefresh.close();
|
|
528
|
+
connectionLiveness.close();
|
|
529
|
+
signal.removeEventListener("abort", stop);
|
|
530
|
+
socket.removeEventListener("close", handleClose);
|
|
531
|
+
socket.removeEventListener("error", handleError);
|
|
532
|
+
socket.removeEventListener("message", handleMessage);
|
|
533
|
+
installationObserver.disconnect(socket);
|
|
534
|
+
connectionController.abort();
|
|
535
|
+
for (const turn of activeTurns.values()) {
|
|
536
|
+
turn.abortConnection();
|
|
597
537
|
}
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
console.warn("Mobius daemon ignored cancellation for an inactive turn");
|
|
603
|
-
return;
|
|
604
|
-
}
|
|
605
|
-
if (!turn.requestCancellation()) {
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
audit.record({
|
|
609
|
-
commandId: cancellation.commandId,
|
|
610
|
-
event: "session_cancellation_requested",
|
|
611
|
-
sessionId: cancellation.sessionId,
|
|
612
|
-
});
|
|
613
|
-
void sessions.cancelPrompt(cancellation.sessionId).catch((error) => {
|
|
614
|
-
console.error("Mobius daemon could not cancel the agent turn", error);
|
|
615
|
-
});
|
|
538
|
+
activeTurns.clear();
|
|
539
|
+
};
|
|
540
|
+
const resolveConnection = () => {
|
|
541
|
+
if (finished) {
|
|
616
542
|
return;
|
|
617
543
|
}
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
544
|
+
finished = true;
|
|
545
|
+
cleanup();
|
|
546
|
+
resolve();
|
|
547
|
+
};
|
|
548
|
+
const rejectConnection = () => {
|
|
549
|
+
if (finished) {
|
|
621
550
|
return;
|
|
622
551
|
}
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
552
|
+
finished = true;
|
|
553
|
+
cleanup();
|
|
554
|
+
reject(new Error("Relay WebSocket failed"));
|
|
555
|
+
};
|
|
556
|
+
const stop = () => {
|
|
557
|
+
socket.close(1000, "Daemon stopped");
|
|
558
|
+
resolveConnection();
|
|
559
|
+
};
|
|
560
|
+
const refreshConnection = () => {
|
|
561
|
+
socket.close(1000, "Refreshing idle relay connection");
|
|
562
|
+
resolveConnection();
|
|
563
|
+
};
|
|
564
|
+
const connectionLiveness = new RelayConnectionLiveness(timer, {
|
|
565
|
+
send: (message) => send(socket, message),
|
|
566
|
+
unresponsive: () => {
|
|
567
|
+
socket.close(4001, "Relay liveness check timed out");
|
|
568
|
+
resolveConnection();
|
|
569
|
+
},
|
|
570
|
+
});
|
|
571
|
+
const connectionRefresh = new RelayConnectionRefresh(timer, () => activeTurns.size === 0, refreshConnection);
|
|
572
|
+
signal.addEventListener("abort", stop, { once: true });
|
|
573
|
+
const handleClose = () => {
|
|
574
|
+
resolveConnection();
|
|
575
|
+
};
|
|
576
|
+
const handleError = () => {
|
|
577
|
+
if (signal.aborted) {
|
|
578
|
+
resolveConnection();
|
|
626
579
|
return;
|
|
627
580
|
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
581
|
+
rejectConnection();
|
|
582
|
+
};
|
|
583
|
+
const handleMessage = (event) => {
|
|
584
|
+
const encoded = event.data;
|
|
585
|
+
if (typeof encoded !== "string") {
|
|
586
|
+
socket.close(4003, "Text message required");
|
|
587
|
+
rejectConnection();
|
|
631
588
|
return;
|
|
632
589
|
}
|
|
633
|
-
|
|
634
|
-
const
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
.
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
590
|
+
try {
|
|
591
|
+
const kind = decodeMessageKind(encoded);
|
|
592
|
+
if (kind === NATIVE_SESSION_CANCEL_KIND) {
|
|
593
|
+
const command = decodeNativeSessionCancel(encoded);
|
|
594
|
+
const pending = nativeRequests.get(command.requestId);
|
|
595
|
+
if (pending?.sessionId === command.sessionId)
|
|
596
|
+
pending.controller.abort(new AcpExecutionError("Native session operation cancelled", {}, "native_session_cancelled"));
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
if (kind === NATIVE_SESSION_REQUEST_KIND) {
|
|
600
|
+
const command = decodeNativeSessionCommand(encoded);
|
|
601
|
+
const previous = [...nativeRequests.values()].filter((request) => request.agentId === command.agentId);
|
|
602
|
+
if (nativeRequests.has(command.requestId) ||
|
|
603
|
+
nativeRequests.size >= 4 ||
|
|
604
|
+
previous.some((request) => !request.controller.signal.aborted)) {
|
|
605
|
+
reportOperationFailure(socket, command.requestId, command.sessionId, new AcpExecutionError("Another native session operation is running", {}, "native_session_busy"));
|
|
606
|
+
return;
|
|
607
|
+
}
|
|
608
|
+
const controller = new AbortController();
|
|
609
|
+
const operationSignal = AbortSignal.any([
|
|
610
|
+
connectionController.signal,
|
|
611
|
+
controller.signal,
|
|
612
|
+
]);
|
|
613
|
+
const timeout = timer.schedule(() => controller.abort(new AcpExecutionError("Native session operation timed out", {}, "native_session_timeout")), 90_000);
|
|
614
|
+
const completion = (async () => {
|
|
615
|
+
try {
|
|
616
|
+
operationSignal.throwIfAborted();
|
|
617
|
+
if (previous.length > 0) {
|
|
618
|
+
await new Promise((resolve, reject) => {
|
|
619
|
+
const cancel = () => reject(operationSignal.reason);
|
|
620
|
+
operationSignal.addEventListener("abort", cancel, {
|
|
621
|
+
once: true,
|
|
622
|
+
});
|
|
623
|
+
void Promise.all(previous.map((request) => request.completion)).then(() => {
|
|
624
|
+
operationSignal.removeEventListener("abort", cancel);
|
|
625
|
+
resolve();
|
|
626
|
+
}, (error) => {
|
|
627
|
+
operationSignal.removeEventListener("abort", cancel);
|
|
628
|
+
reject(error);
|
|
629
|
+
});
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
operationSignal.throwIfAborted();
|
|
633
|
+
const result = await nativeSessions.execute(command, operationSignal);
|
|
634
|
+
if (socket.readyState === WebSocket.OPEN)
|
|
635
|
+
send(socket, encodeNativeSessionResult(result));
|
|
636
|
+
}
|
|
637
|
+
catch (error) {
|
|
638
|
+
const failure = operationSignal.aborted
|
|
639
|
+
? (controller.signal.reason ??
|
|
640
|
+
new AcpExecutionError("Native session operation cancelled", {}, "native_session_cancelled"))
|
|
641
|
+
: error;
|
|
642
|
+
audit.record({
|
|
643
|
+
commandId: command.requestId,
|
|
644
|
+
sessionId: command.sessionId,
|
|
645
|
+
event: "native_session_operation_failed",
|
|
646
|
+
details: { code: operationErrorCode(failure) },
|
|
647
|
+
});
|
|
648
|
+
reportOperationFailure(socket, command.requestId, command.sessionId, failure);
|
|
649
|
+
}
|
|
650
|
+
finally {
|
|
651
|
+
timeout.cancel();
|
|
652
|
+
nativeRequests.delete(command.requestId);
|
|
653
|
+
}
|
|
654
|
+
})();
|
|
655
|
+
nativeRequests.set(command.requestId, {
|
|
656
|
+
agentId: command.agentId,
|
|
641
657
|
sessionId: command.sessionId,
|
|
658
|
+
controller,
|
|
659
|
+
completion,
|
|
642
660
|
});
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
if (kind === RUNTIME_LIVENESS_READY_KIND) {
|
|
664
|
+
decodeRuntimeLivenessReadyEvent(encoded);
|
|
665
|
+
connectionLiveness.start();
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
if (kind === RUNTIME_HEARTBEAT_ACK_KIND) {
|
|
669
|
+
connectionLiveness.acknowledge(decodeRuntimeHeartbeatAckEvent(encoded));
|
|
670
|
+
return;
|
|
671
|
+
}
|
|
672
|
+
if (kind === SESSION_CANCEL_KIND) {
|
|
673
|
+
const cancellation = decodeSessionCancelCommand(encoded);
|
|
674
|
+
const turn = activeTurns.get(cancellation.commandId);
|
|
675
|
+
if (turn === undefined || !turn.matches(cancellation)) {
|
|
676
|
+
console.warn("Mobius daemon ignored cancellation for an inactive turn");
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
if (!turn.requestCancellation()) {
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
654
682
|
audit.record({
|
|
655
|
-
commandId:
|
|
656
|
-
event: "
|
|
657
|
-
sessionId:
|
|
683
|
+
commandId: cancellation.commandId,
|
|
684
|
+
event: "session_cancellation_requested",
|
|
685
|
+
sessionId: cancellation.sessionId,
|
|
658
686
|
});
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
687
|
+
void sessions
|
|
688
|
+
.cancelPrompt(cancellation.sessionId)
|
|
689
|
+
.catch((error) => {
|
|
690
|
+
console.error("Mobius daemon could not cancel the agent turn", error);
|
|
691
|
+
});
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (kind === WORKSPACE_LIST_KIND) {
|
|
695
|
+
const command = decodeWorkspaceListCommand(encoded);
|
|
696
|
+
void executeWorkspaceList(socket, command, directories, audit, connectionController.signal);
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
if (kind === SESSION_PREPARE_KIND) {
|
|
700
|
+
const command = decodeSessionPrepareCommand(encoded);
|
|
701
|
+
void executeSessionPrepare(socket, command, config, directories, audit, sessions, connectionController.signal);
|
|
702
|
+
return;
|
|
703
|
+
}
|
|
704
|
+
if (kind === SESSION_CONFIGURE_KIND) {
|
|
705
|
+
const command = decodeSessionConfigureCommand(encoded);
|
|
706
|
+
void executeSessionConfigure(socket, command, config, directories, audit, sessions, connectionController.signal);
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
if (kind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
710
|
+
const command = decodeSessionPermissionRespondCommand(encoded);
|
|
711
|
+
void sessions
|
|
712
|
+
.respondPermission(command)
|
|
713
|
+
.then(() => {
|
|
714
|
+
audit.record({
|
|
715
|
+
commandId: command.interactionId,
|
|
716
|
+
event: "session_permission_resolved",
|
|
717
|
+
sessionId: command.sessionId,
|
|
718
|
+
});
|
|
719
|
+
})
|
|
720
|
+
.catch((error) => {
|
|
721
|
+
reportOperationFailure(socket, command.interactionId, command.sessionId, error);
|
|
722
|
+
});
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
if (kind === SESSION_ELICITATION_RESPOND_KIND) {
|
|
726
|
+
const command = decodeSessionElicitationRespondCommand(encoded);
|
|
727
|
+
void sessions
|
|
728
|
+
.respondElicitation(command)
|
|
729
|
+
.then(() => {
|
|
730
|
+
audit.record({
|
|
731
|
+
commandId: command.interactionId,
|
|
732
|
+
event: "session_elicitation_resolved",
|
|
733
|
+
sessionId: command.sessionId,
|
|
734
|
+
});
|
|
735
|
+
})
|
|
736
|
+
.catch((error) => {
|
|
737
|
+
reportOperationFailure(socket, command.interactionId, command.sessionId, error);
|
|
738
|
+
});
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (kind === AGENT_INSTALL_REQUEST_KIND) {
|
|
742
|
+
const command = decodeAgentInstallRequestCommand(encoded);
|
|
743
|
+
void installations
|
|
744
|
+
.request(command, signal)
|
|
745
|
+
.catch((error) => {
|
|
746
|
+
console.error("Mobius Agent installation failed", error);
|
|
747
|
+
});
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
if (kind === AGENT_INSTALL_CANCEL_KIND) {
|
|
751
|
+
const command = decodeAgentInstallCancelCommand(encoded);
|
|
752
|
+
void installations
|
|
753
|
+
.cancel(command.requestId)
|
|
754
|
+
.catch((error) => {
|
|
755
|
+
reportOperationFailure(socket, command.requestId, "", error);
|
|
756
|
+
});
|
|
757
|
+
return;
|
|
758
|
+
}
|
|
759
|
+
if (kind !== SESSION_PROMPT_KIND) {
|
|
760
|
+
throw new Error("Unsupported protocol message");
|
|
761
|
+
}
|
|
762
|
+
const command = decodeSessionPromptCommand(encoded);
|
|
763
|
+
if (activeTurns.has(command.commandId)) {
|
|
764
|
+
reportFailure(socket, command, "duplicate_command", "The command is already running");
|
|
765
|
+
return;
|
|
766
|
+
}
|
|
767
|
+
const turn = new ActiveDaemonTurn(command, new AbortController());
|
|
768
|
+
activeTurns.set(command.commandId, turn);
|
|
769
|
+
void executePrompt(socket, command, config, directories, audit, sessions, turn.signal)
|
|
678
770
|
.catch((error) => {
|
|
679
|
-
|
|
771
|
+
console.error("Mobius daemon turn failed", error);
|
|
772
|
+
})
|
|
773
|
+
.finally(() => {
|
|
774
|
+
activeTurns.delete(command.commandId);
|
|
775
|
+
connectionRefresh.afterTurnFinished();
|
|
680
776
|
});
|
|
681
|
-
return;
|
|
682
777
|
}
|
|
683
|
-
|
|
684
|
-
|
|
778
|
+
catch (error) {
|
|
779
|
+
console.error("Mobius daemon rejected a message", error);
|
|
780
|
+
socket.close(4008, "Invalid protocol message");
|
|
781
|
+
rejectConnection();
|
|
685
782
|
}
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
activeTurns.set(command.commandId, turn);
|
|
693
|
-
void executePrompt(socket, command, config, directories, audit, sessions, turn.signal)
|
|
694
|
-
.catch((error) => {
|
|
695
|
-
console.error("Mobius daemon turn failed", error);
|
|
696
|
-
})
|
|
697
|
-
.finally(() => {
|
|
698
|
-
activeTurns.delete(command.commandId);
|
|
699
|
-
connectionRefresh.afterTurnFinished();
|
|
700
|
-
});
|
|
701
|
-
}
|
|
702
|
-
catch (error) {
|
|
703
|
-
console.error("Mobius daemon rejected a message", error);
|
|
704
|
-
socket.close(4008, "Invalid protocol message");
|
|
705
|
-
rejectConnection();
|
|
783
|
+
};
|
|
784
|
+
socket.addEventListener("close", handleClose);
|
|
785
|
+
socket.addEventListener("error", handleError);
|
|
786
|
+
socket.addEventListener("message", handleMessage);
|
|
787
|
+
if (signal.aborted) {
|
|
788
|
+
stop();
|
|
706
789
|
}
|
|
707
|
-
};
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
714
|
-
});
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
finally {
|
|
793
|
+
connectionController.abort();
|
|
794
|
+
await Promise.all([...nativeRequests.values()].map((request) => request.completion));
|
|
795
|
+
}
|
|
715
796
|
}
|
|
716
797
|
export async function runDaemon(config, audit, signal, timer = systemDaemonTimer) {
|
|
717
798
|
const installationAudit = new AuditInstallationEvents(audit);
|
|
718
799
|
const stateDirectory = readStateDirectory();
|
|
719
800
|
const specifications = supportedLocalAgentSpecifications();
|
|
720
801
|
const installationExecutor = new FixedInstallationExecutor({
|
|
721
|
-
|
|
802
|
+
defaultWorkspace: config.defaultWorkspace,
|
|
722
803
|
audit: installationAudit,
|
|
723
804
|
catalog: config.catalog,
|
|
724
805
|
environment: process.env,
|
|
@@ -726,26 +807,26 @@ export async function runDaemon(config, audit, signal, timer = systemDaemonTimer
|
|
|
726
807
|
stateDirectory,
|
|
727
808
|
});
|
|
728
809
|
await new AutomaticAdapterProvisioner({
|
|
729
|
-
|
|
810
|
+
defaultWorkspace: config.defaultWorkspace,
|
|
730
811
|
audit: installationAudit,
|
|
731
812
|
catalog: config.catalog,
|
|
732
813
|
environment: process.env,
|
|
733
814
|
}, new ManagedAdapterInstaller({
|
|
734
|
-
approvedRoot: config.approvedRoot,
|
|
735
815
|
environment: process.env,
|
|
736
816
|
stateDirectory,
|
|
737
817
|
}), specifications).prepare(signal);
|
|
738
818
|
const sessionStore = new SqliteAcpSessionStore(config.auditDatabasePath);
|
|
739
819
|
const sessions = new AcpSessionManager(new NullAcpSessionObserver(), sessionStore);
|
|
740
|
-
const directories = new WorkspaceDirectoryService(config.bindingId, config.
|
|
820
|
+
const directories = new WorkspaceDirectoryService(config.bindingId, config.defaultWorkspace);
|
|
741
821
|
const installationObserver = new DaemonInstallationObserver();
|
|
822
|
+
const nativeSessions = new NativeSessionService(config, directories, sessions, audit);
|
|
742
823
|
const installations = new AgentInstallationManager(specifications, new ConsoleInstallationAuthorizer(stateDirectory), installationExecutor, installationObserver, installationAudit);
|
|
743
824
|
let retryAnnounced = false;
|
|
744
825
|
try {
|
|
745
826
|
while (!signal.aborted) {
|
|
746
827
|
try {
|
|
747
828
|
await publishAgentCatalog(config, signal);
|
|
748
|
-
await runRelayConnection(config, directories, audit, sessions, installations, installationObserver, signal, timer);
|
|
829
|
+
await runRelayConnection(config, directories, audit, sessions, installations, installationObserver, nativeSessions, signal, timer);
|
|
749
830
|
retryAnnounced = false;
|
|
750
831
|
}
|
|
751
832
|
catch {
|