@moxt-ai/mobius 0.0.2 → 0.0.4
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 +5 -2
- package/dist/acp/activity.d.ts.map +1 -1
- package/dist/acp/activity.js +89 -5
- package/dist/acp/activity.js.map +1 -1
- package/dist/acp/create-client.d.ts +8 -2
- package/dist/acp/create-client.d.ts.map +1 -1
- package/dist/acp/create-client.js +10 -2
- package/dist/acp/create-client.js.map +1 -1
- package/dist/acp/run-prompt.d.ts.map +1 -1
- package/dist/acp/run-prompt.js +4 -0
- package/dist/acp/run-prompt.js.map +1 -1
- package/dist/acp/session-manager.d.ts +100 -4
- package/dist/acp/session-manager.d.ts.map +1 -1
- package/dist/acp/session-manager.js +1239 -138
- package/dist/acp/session-manager.js.map +1 -1
- package/dist/acp/session-store.d.ts +37 -0
- package/dist/acp/session-store.d.ts.map +1 -0
- package/dist/acp/session-store.js +158 -0
- package/dist/acp/session-store.js.map +1 -0
- package/dist/authorization/workspace-directory.d.ts +17 -0
- package/dist/authorization/workspace-directory.d.ts.map +1 -0
- package/dist/authorization/workspace-directory.js +164 -0
- package/dist/authorization/workspace-directory.js.map +1 -0
- package/dist/cli.js +22429 -697
- package/dist/cli.js.map +4 -4
- package/dist/index.js +22435 -677
- package/dist/index.js.map +4 -4
- package/dist/relay-connection/liveness.d.ts +14 -0
- package/dist/relay-connection/liveness.d.ts.map +1 -0
- package/dist/relay-connection/liveness.js +102 -0
- package/dist/relay-connection/liveness.js.map +1 -0
- package/dist/relay-connection/timer.d.ts +8 -0
- package/dist/relay-connection/timer.d.ts.map +1 -0
- package/dist/relay-connection/timer.js +8 -0
- package/dist/relay-connection/timer.js.map +1 -0
- package/dist/run-daemon.d.ts +2 -1
- package/dist/run-daemon.d.ts.map +1 -1
- package/dist/run-daemon.js +352 -20
- package/dist/run-daemon.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type RuntimeHeartbeatAckEvent } from "@moxt-ai/mobius-protocol";
|
|
2
|
+
import type { DaemonTimer } from "./timer.ts";
|
|
3
|
+
export interface RelayLivenessTransport {
|
|
4
|
+
readonly send: (message: string) => void;
|
|
5
|
+
readonly unresponsive: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare class RelayConnectionLiveness {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(timer: DaemonTimer, transport: RelayLivenessTransport);
|
|
10
|
+
readonly acknowledge: (event: RuntimeHeartbeatAckEvent) => void;
|
|
11
|
+
readonly close: () => void;
|
|
12
|
+
readonly start: () => void;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=liveness.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liveness.d.ts","sourceRoot":"","sources":["../../src/relay-connection/liveness.ts"],"names":[],"mappings":"AAEA,OAAO,EAIL,KAAK,wBAAwB,EAC9B,MAAM,0BAA0B,CAAC;AAElC,OAAO,KAAK,EAAuB,WAAW,EAAE,MAAM,YAAY,CAAC;AA+BnE,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,QAAQ,CAAC,YAAY,EAAE,MAAM,IAAI,CAAC;CACnC;AAED,qBAAa,uBAAuB;;IAKlC,YAAY,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,sBAAsB,EAGhE;IAED,QAAQ,CAAC,WAAW,UAAW,wBAAwB,KAAG,IAAI,CAc5D;IAEF,QAAQ,CAAC,KAAK,QAAO,IAAI,CAYvB;IAEF,QAAQ,CAAC,KAAK,QAAO,IAAI,CAevB;CAuCH"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { encodeRuntimeHeartbeatEvent, MOBIUS_PROTOCOL_VERSION, RUNTIME_HEARTBEAT_KIND, } from "@moxt-ai/mobius-protocol";
|
|
3
|
+
const HEARTBEAT_ACK_TIMEOUT_MILLISECONDS = 10_000;
|
|
4
|
+
const HEARTBEAT_INTERVAL_MILLISECONDS = 20_000;
|
|
5
|
+
class RelayLivenessState {
|
|
6
|
+
}
|
|
7
|
+
class UnsupportedRelayLivenessState extends RelayLivenessState {
|
|
8
|
+
}
|
|
9
|
+
class AwaitingHeartbeatAckState extends RelayLivenessState {
|
|
10
|
+
heartbeatId;
|
|
11
|
+
timeout;
|
|
12
|
+
constructor(heartbeatId, timeout) {
|
|
13
|
+
super();
|
|
14
|
+
this.heartbeatId = heartbeatId;
|
|
15
|
+
this.timeout = timeout;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
class WaitingForHeartbeatState extends RelayLivenessState {
|
|
19
|
+
schedule;
|
|
20
|
+
constructor(schedule) {
|
|
21
|
+
super();
|
|
22
|
+
this.schedule = schedule;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
class FinishedRelayLivenessState extends RelayLivenessState {
|
|
26
|
+
}
|
|
27
|
+
export class RelayConnectionLiveness {
|
|
28
|
+
#timer;
|
|
29
|
+
#transport;
|
|
30
|
+
#state = new UnsupportedRelayLivenessState();
|
|
31
|
+
constructor(timer, transport) {
|
|
32
|
+
this.#timer = timer;
|
|
33
|
+
this.#transport = transport;
|
|
34
|
+
}
|
|
35
|
+
acknowledge = (event) => {
|
|
36
|
+
if (!(this.#state instanceof AwaitingHeartbeatAckState)) {
|
|
37
|
+
throw new Error("Unexpected relay heartbeat acknowledgement");
|
|
38
|
+
}
|
|
39
|
+
if (event.heartbeatId !== this.#state.heartbeatId) {
|
|
40
|
+
throw new Error("Relay acknowledged the wrong heartbeat");
|
|
41
|
+
}
|
|
42
|
+
this.#state.timeout.cancel();
|
|
43
|
+
this.#state = new WaitingForHeartbeatState(this.#timer.schedule(this.#sendHeartbeat, HEARTBEAT_INTERVAL_MILLISECONDS));
|
|
44
|
+
};
|
|
45
|
+
close = () => {
|
|
46
|
+
if (this.#state instanceof AwaitingHeartbeatAckState) {
|
|
47
|
+
this.#state.timeout.cancel();
|
|
48
|
+
}
|
|
49
|
+
else if (this.#state instanceof WaitingForHeartbeatState) {
|
|
50
|
+
this.#state.schedule.cancel();
|
|
51
|
+
}
|
|
52
|
+
else if (!(this.#state instanceof UnsupportedRelayLivenessState) &&
|
|
53
|
+
!(this.#state instanceof FinishedRelayLivenessState)) {
|
|
54
|
+
throw new Error("Unknown relay liveness state");
|
|
55
|
+
}
|
|
56
|
+
this.#state = new FinishedRelayLivenessState();
|
|
57
|
+
};
|
|
58
|
+
start = () => {
|
|
59
|
+
if (this.#state instanceof UnsupportedRelayLivenessState) {
|
|
60
|
+
this.#sendHeartbeat();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (this.#state instanceof AwaitingHeartbeatAckState ||
|
|
64
|
+
this.#state instanceof WaitingForHeartbeatState) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (this.#state instanceof FinishedRelayLivenessState) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
throw new Error("Unknown relay liveness state");
|
|
71
|
+
};
|
|
72
|
+
#handleTimeout = () => {
|
|
73
|
+
if (this.#state instanceof FinishedRelayLivenessState) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (!(this.#state instanceof AwaitingHeartbeatAckState)) {
|
|
77
|
+
throw new Error("Heartbeat timeout occurred in an invalid state");
|
|
78
|
+
}
|
|
79
|
+
this.#state.timeout.cancel();
|
|
80
|
+
this.#state = new FinishedRelayLivenessState();
|
|
81
|
+
this.#transport.unresponsive();
|
|
82
|
+
};
|
|
83
|
+
#sendHeartbeat = () => {
|
|
84
|
+
if (this.#state instanceof WaitingForHeartbeatState) {
|
|
85
|
+
this.#state.schedule.cancel();
|
|
86
|
+
}
|
|
87
|
+
else if (!(this.#state instanceof UnsupportedRelayLivenessState)) {
|
|
88
|
+
if (this.#state instanceof FinishedRelayLivenessState) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
throw new Error("Heartbeat was scheduled in an invalid state");
|
|
92
|
+
}
|
|
93
|
+
const heartbeatId = randomUUID();
|
|
94
|
+
this.#state = new AwaitingHeartbeatAckState(heartbeatId, this.#timer.schedule(this.#handleTimeout, HEARTBEAT_ACK_TIMEOUT_MILLISECONDS));
|
|
95
|
+
this.#transport.send(encodeRuntimeHeartbeatEvent({
|
|
96
|
+
heartbeatId,
|
|
97
|
+
kind: RUNTIME_HEARTBEAT_KIND,
|
|
98
|
+
protocolVersion: MOBIUS_PROTOCOL_VERSION,
|
|
99
|
+
}));
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=liveness.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"liveness.js","sourceRoot":"","sources":["../../src/relay-connection/liveness.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface DaemonScheduledTask {
|
|
2
|
+
readonly cancel: () => void;
|
|
3
|
+
}
|
|
4
|
+
export interface DaemonTimer {
|
|
5
|
+
readonly schedule: (callback: () => void, milliseconds: number) => DaemonScheduledTask;
|
|
6
|
+
}
|
|
7
|
+
export declare const systemDaemonTimer: DaemonTimer;
|
|
8
|
+
//# sourceMappingURL=timer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timer.d.ts","sourceRoot":"","sources":["../../src/relay-connection/timer.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,QAAQ,EAAE,CACjB,QAAQ,EAAE,MAAM,IAAI,EACpB,YAAY,EAAE,MAAM,KACjB,mBAAmB,CAAC;CAC1B;AAYD,eAAO,MAAM,iBAAiB,EAAE,WAAqC,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
class SystemDaemonTimer {
|
|
2
|
+
schedule = (callback, milliseconds) => {
|
|
3
|
+
const timeout = setTimeout(callback, milliseconds);
|
|
4
|
+
return { cancel: () => clearTimeout(timeout) };
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export const systemDaemonTimer = new SystemDaemonTimer();
|
|
8
|
+
//# sourceMappingURL=timer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timer.js","sourceRoot":"","sources":["../../src/relay-connection/timer.ts"],"names":[],"mappings":"AAWA,MAAM,iBAAiB;IACZ,QAAQ,GAAG,CAClB,QAAoB,EACpB,YAAoB,EACC,EAAE;QACvB,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QACnD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;IACjD,CAAC,CAAC;CACH;AAED,MAAM,CAAC,MAAM,iBAAiB,GAAgB,IAAI,iBAAiB,EAAE,CAAC"}
|
package/dist/run-daemon.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { AuditStore } from "./audit/audit-store.ts";
|
|
2
2
|
import { type DaemonConfig } from "./config.ts";
|
|
3
|
-
|
|
3
|
+
import { type DaemonTimer } from "./relay-connection/timer.ts";
|
|
4
|
+
export declare function runDaemon(config: DaemonConfig, audit: AuditStore, signal: AbortSignal, timer?: DaemonTimer): Promise<void>;
|
|
4
5
|
//# sourceMappingURL=run-daemon.d.ts.map
|
package/dist/run-daemon.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-daemon.d.ts","sourceRoot":"","sources":["../src/run-daemon.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"run-daemon.d.ts","sourceRoot":"","sources":["../src/run-daemon.ts"],"names":[],"mappings":"AAsEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,aAAa,CAAC;AAGhE,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,6BAA6B,CAAC;AAqzBrC,wBAAsB,SAAS,CAC7B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,EACnB,KAAK,GAAE,WAA+B,GACrC,OAAO,CAAC,IAAI,CAAC,CAwCf"}
|
package/dist/run-daemon.js
CHANGED
|
@@ -1,11 +1,119 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
|
-
import {
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
import { AGENT_ACTIVITY_KIND, AGENT_MESSAGE_DELTA_KIND, decodeMessageKind, decodeRuntimeHeartbeatAckEvent, decodeRuntimeLivenessReadyEvent, decodeSessionCancelCommand, decodeSessionConfigureCommand, decodeSessionElicitationRespondCommand, decodeSessionPermissionRespondCommand, decodeSessionPrepareCommand, decodeSessionPromptCommand, decodeWorkspaceListCommand, encodeAgentActivityEvent, encodeAgentContentEvent, 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
4
|
import { WebSocket } from "undici";
|
|
4
|
-
import { AcpSessionManager } from "./acp/session-manager.js";
|
|
5
|
-
import {
|
|
5
|
+
import { AcpExecutionError, AcpSessionManager, NullAcpSessionObserver, } from "./acp/session-manager.js";
|
|
6
|
+
import { SqliteAcpSessionStore } from "./acp/session-store.js";
|
|
7
|
+
import { LocalAuthorizationError } from "./authorization/authorize-binding.js";
|
|
8
|
+
import { WorkspaceDirectoryService } from "./authorization/workspace-directory.js";
|
|
6
9
|
import { findLocalAgent } from "./config.js";
|
|
7
10
|
import { connectRelay } from "./relay-connection/connect.js";
|
|
11
|
+
import { RelayConnectionLiveness } from "./relay-connection/liveness.js";
|
|
12
|
+
import { systemDaemonTimer, } from "./relay-connection/timer.js";
|
|
8
13
|
const RELAY_RECONNECT_DELAY_MILLISECONDS = 500;
|
|
14
|
+
const RELAY_CONNECTION_REFRESH_MILLISECONDS = 20 * 60 * 1_000;
|
|
15
|
+
class RelayConnectionRefreshState {
|
|
16
|
+
}
|
|
17
|
+
class ScheduledRelayConnectionRefreshState extends RelayConnectionRefreshState {
|
|
18
|
+
}
|
|
19
|
+
class DueRelayConnectionRefreshState extends RelayConnectionRefreshState {
|
|
20
|
+
}
|
|
21
|
+
class FinishedRelayConnectionRefreshState extends RelayConnectionRefreshState {
|
|
22
|
+
}
|
|
23
|
+
class RelayConnectionRefresh {
|
|
24
|
+
#isSafe;
|
|
25
|
+
#refresh;
|
|
26
|
+
#scheduledTask;
|
|
27
|
+
#state = new ScheduledRelayConnectionRefreshState();
|
|
28
|
+
constructor(timer, isSafe, refresh) {
|
|
29
|
+
this.#isSafe = isSafe;
|
|
30
|
+
this.#refresh = refresh;
|
|
31
|
+
this.#scheduledTask = timer.schedule(this.#request, RELAY_CONNECTION_REFRESH_MILLISECONDS);
|
|
32
|
+
}
|
|
33
|
+
afterTurnFinished = () => {
|
|
34
|
+
if (this.#state instanceof DueRelayConnectionRefreshState) {
|
|
35
|
+
this.#request();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (this.#state instanceof ScheduledRelayConnectionRefreshState ||
|
|
39
|
+
this.#state instanceof FinishedRelayConnectionRefreshState) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
throw new Error("Unknown relay connection refresh state");
|
|
43
|
+
};
|
|
44
|
+
close = () => {
|
|
45
|
+
this.#scheduledTask.cancel();
|
|
46
|
+
this.#state = new FinishedRelayConnectionRefreshState();
|
|
47
|
+
};
|
|
48
|
+
#request = () => {
|
|
49
|
+
if (this.#state instanceof FinishedRelayConnectionRefreshState) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!(this.#state instanceof ScheduledRelayConnectionRefreshState) &&
|
|
53
|
+
!(this.#state instanceof DueRelayConnectionRefreshState)) {
|
|
54
|
+
throw new Error("Unknown relay connection refresh state");
|
|
55
|
+
}
|
|
56
|
+
if (!this.#isSafe()) {
|
|
57
|
+
this.#state = new DueRelayConnectionRefreshState();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
this.#state = new FinishedRelayConnectionRefreshState();
|
|
61
|
+
this.#scheduledTask.cancel();
|
|
62
|
+
this.#refresh();
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
class DaemonAcpObserver {
|
|
66
|
+
#socket;
|
|
67
|
+
constructor(socket) {
|
|
68
|
+
this.#socket = socket;
|
|
69
|
+
}
|
|
70
|
+
onCommands = (event) => {
|
|
71
|
+
send(this.#socket, encodeSessionCommandsUpdatedEvent(event));
|
|
72
|
+
return Promise.resolve();
|
|
73
|
+
};
|
|
74
|
+
onConfig = (event) => {
|
|
75
|
+
send(this.#socket, encodeSessionConfigUpdatedEvent(event));
|
|
76
|
+
return Promise.resolve();
|
|
77
|
+
};
|
|
78
|
+
onContent = (event) => {
|
|
79
|
+
send(this.#socket, encodeAgentContentEvent(event));
|
|
80
|
+
return Promise.resolve();
|
|
81
|
+
};
|
|
82
|
+
onElicitation = (event) => {
|
|
83
|
+
send(this.#socket, encodeSessionElicitationRequestedEvent(event));
|
|
84
|
+
return Promise.resolve();
|
|
85
|
+
};
|
|
86
|
+
onElicitationResolved = (event) => {
|
|
87
|
+
send(this.#socket, encodeSessionElicitationResolvedEvent(event));
|
|
88
|
+
return Promise.resolve();
|
|
89
|
+
};
|
|
90
|
+
onInfo = (event) => {
|
|
91
|
+
send(this.#socket, encodeSessionInfoUpdatedEvent(event));
|
|
92
|
+
return Promise.resolve();
|
|
93
|
+
};
|
|
94
|
+
onPermission = (event) => {
|
|
95
|
+
send(this.#socket, encodeSessionPermissionRequestedEvent(event));
|
|
96
|
+
return Promise.resolve();
|
|
97
|
+
};
|
|
98
|
+
onPermissionResolved = (event) => {
|
|
99
|
+
send(this.#socket, encodeSessionPermissionResolvedEvent(event));
|
|
100
|
+
return Promise.resolve();
|
|
101
|
+
};
|
|
102
|
+
onUsage = (event) => {
|
|
103
|
+
send(this.#socket, encodeSessionUsageUpdatedEvent(event));
|
|
104
|
+
return Promise.resolve();
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
class ResolvedSessionDirectories {
|
|
108
|
+
additionalDirectories;
|
|
109
|
+
additionalDirectoryEntries;
|
|
110
|
+
cwd;
|
|
111
|
+
constructor(cwd, additionalDirectories, additionalDirectoryEntries) {
|
|
112
|
+
this.additionalDirectories = additionalDirectories;
|
|
113
|
+
this.additionalDirectoryEntries = additionalDirectoryEntries;
|
|
114
|
+
this.cwd = cwd;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
9
117
|
class ActiveDaemonTurn {
|
|
10
118
|
command;
|
|
11
119
|
#connectionController;
|
|
@@ -49,9 +157,148 @@ function reportFailure(socket, command, code, message) {
|
|
|
49
157
|
sessionId: command.sessionId,
|
|
50
158
|
}));
|
|
51
159
|
}
|
|
52
|
-
|
|
160
|
+
function operationErrorCode(error) {
|
|
161
|
+
if (error instanceof AcpExecutionError ||
|
|
162
|
+
error instanceof LocalAuthorizationError) {
|
|
163
|
+
return error.code;
|
|
164
|
+
}
|
|
165
|
+
return "session_operation_failed";
|
|
166
|
+
}
|
|
167
|
+
function operationErrorMessage(error) {
|
|
168
|
+
if (error instanceof AcpExecutionError ||
|
|
169
|
+
error instanceof LocalAuthorizationError) {
|
|
170
|
+
return error.message;
|
|
171
|
+
}
|
|
172
|
+
return "The local agent operation could not be completed";
|
|
173
|
+
}
|
|
174
|
+
function reportOperationFailure(socket, requestId, sessionId, error) {
|
|
175
|
+
if (socket.readyState !== WebSocket.OPEN) {
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
send(socket, encodeSessionOperationFailedEvent({
|
|
179
|
+
code: operationErrorCode(error),
|
|
180
|
+
kind: "session.operation.failed",
|
|
181
|
+
message: operationErrorMessage(error),
|
|
182
|
+
protocolVersion: MOBIUS_PROTOCOL_VERSION,
|
|
183
|
+
requestId,
|
|
184
|
+
sessionId,
|
|
185
|
+
}));
|
|
186
|
+
}
|
|
187
|
+
async function resolveSessionDirectories(directories, bindingId, directoryId, additionalDirectoryIds) {
|
|
188
|
+
const cwd = await directories.resolve(bindingId, directoryId);
|
|
189
|
+
const additionalDirectories = [];
|
|
190
|
+
const additionalDirectoryEntries = [];
|
|
191
|
+
const unique = new Set([cwd]);
|
|
192
|
+
for (const additionalDirectoryId of additionalDirectoryIds) {
|
|
193
|
+
const directory = await directories.resolve(bindingId, additionalDirectoryId);
|
|
194
|
+
if (unique.has(directory)) {
|
|
195
|
+
throw new LocalAuthorizationError("Workspace directories must resolve to distinct locations");
|
|
196
|
+
}
|
|
197
|
+
unique.add(directory);
|
|
198
|
+
additionalDirectories.push(directory);
|
|
199
|
+
additionalDirectoryEntries.push({
|
|
200
|
+
directoryId: additionalDirectoryId,
|
|
201
|
+
name: basename(directory),
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return new ResolvedSessionDirectories(cwd, additionalDirectories, additionalDirectoryEntries);
|
|
205
|
+
}
|
|
206
|
+
async function executeWorkspaceList(socket, command, directories, audit) {
|
|
207
|
+
try {
|
|
208
|
+
const listing = await directories.list(command.bindingId, command.directoryId);
|
|
209
|
+
audit.record({
|
|
210
|
+
commandId: command.requestId,
|
|
211
|
+
event: "workspace_list_authorized",
|
|
212
|
+
sessionId: "workspace-browser",
|
|
213
|
+
});
|
|
214
|
+
send(socket, encodeWorkspaceEntriesEvent({
|
|
215
|
+
bindingId: command.bindingId,
|
|
216
|
+
directoryId: listing.directoryId,
|
|
217
|
+
directoryLabel: listing.directoryLabel,
|
|
218
|
+
entries: listing.entries,
|
|
219
|
+
kind: "workspace.entries",
|
|
220
|
+
parentDirectoryId: listing.parentDirectoryId,
|
|
221
|
+
protocolVersion: MOBIUS_PROTOCOL_VERSION,
|
|
222
|
+
requestId: command.requestId,
|
|
223
|
+
}));
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
reportOperationFailure(socket, command.requestId, "", error);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async function executeSessionPrepare(socket, command, config, directories, audit, sessions, signal) {
|
|
230
|
+
try {
|
|
231
|
+
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
|
|
232
|
+
const agent = findLocalAgent(config.agents, command.agentId);
|
|
233
|
+
const prepared = await sessions.prepareSession({
|
|
234
|
+
additionalDirectories: resolved.additionalDirectories,
|
|
235
|
+
agent,
|
|
236
|
+
cwd: resolved.cwd,
|
|
237
|
+
sessionId: command.sessionId,
|
|
238
|
+
signal,
|
|
239
|
+
});
|
|
240
|
+
const listing = await directories.list(command.bindingId, command.directoryId);
|
|
241
|
+
audit.record({
|
|
242
|
+
commandId: command.requestId,
|
|
243
|
+
event: "session_prepared",
|
|
244
|
+
sessionId: command.sessionId,
|
|
245
|
+
});
|
|
246
|
+
send(socket, encodeSessionReadyEvent({
|
|
247
|
+
additionalDirectories: resolved.additionalDirectoryEntries,
|
|
248
|
+
agentId: command.agentId,
|
|
249
|
+
agentName: prepared.agentName,
|
|
250
|
+
agentVersion: prepared.agentVersion,
|
|
251
|
+
capabilities: prepared.capabilities,
|
|
252
|
+
commands: prepared.commands,
|
|
253
|
+
configOptions: prepared.configOptions,
|
|
254
|
+
directoryId: command.directoryId,
|
|
255
|
+
directoryLabel: listing.directoryLabel,
|
|
256
|
+
kind: "session.ready",
|
|
257
|
+
protocolVersion: MOBIUS_PROTOCOL_VERSION,
|
|
258
|
+
requestId: command.requestId,
|
|
259
|
+
restored: prepared.restored,
|
|
260
|
+
sessionId: command.sessionId,
|
|
261
|
+
}));
|
|
262
|
+
}
|
|
263
|
+
catch (error) {
|
|
264
|
+
reportOperationFailure(socket, command.requestId, command.sessionId, error);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
async function executeSessionConfigure(socket, command, config, directories, audit, sessions, signal) {
|
|
268
|
+
try {
|
|
269
|
+
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
|
|
270
|
+
const configOptions = await sessions.configureSession({
|
|
271
|
+
additionalDirectories: resolved.additionalDirectories,
|
|
272
|
+
agent: findLocalAgent(config.agents, command.agentId),
|
|
273
|
+
booleanValue: command.booleanValue,
|
|
274
|
+
configId: command.configId,
|
|
275
|
+
cwd: resolved.cwd,
|
|
276
|
+
requestId: command.requestId,
|
|
277
|
+
sessionId: command.sessionId,
|
|
278
|
+
signal,
|
|
279
|
+
value: command.value,
|
|
280
|
+
valueType: command.valueType,
|
|
281
|
+
});
|
|
282
|
+
audit.record({
|
|
283
|
+
commandId: command.requestId,
|
|
284
|
+
event: "session_configured",
|
|
285
|
+
sessionId: command.sessionId,
|
|
286
|
+
});
|
|
287
|
+
send(socket, encodeSessionConfigUpdatedEvent({
|
|
288
|
+
configOptions,
|
|
289
|
+
kind: "session.config.updated",
|
|
290
|
+
protocolVersion: MOBIUS_PROTOCOL_VERSION,
|
|
291
|
+
requestId: command.requestId,
|
|
292
|
+
sessionId: command.sessionId,
|
|
293
|
+
}));
|
|
294
|
+
}
|
|
295
|
+
catch (error) {
|
|
296
|
+
reportOperationFailure(socket, command.requestId, command.sessionId, error);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
async function executePrompt(socket, command, config, directories, audit, sessions, signal) {
|
|
53
300
|
try {
|
|
54
|
-
const
|
|
301
|
+
const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
|
|
55
302
|
const agent = findLocalAgent(config.agents, command.agentId);
|
|
56
303
|
const processAttemptId = randomUUID();
|
|
57
304
|
audit.record({
|
|
@@ -67,13 +314,17 @@ async function executePrompt(socket, command, config, audit, sessions, signal) {
|
|
|
67
314
|
sessionId: command.sessionId,
|
|
68
315
|
}));
|
|
69
316
|
const result = await sessions.runPrompt({
|
|
317
|
+
additionalDirectories: resolved.additionalDirectories,
|
|
70
318
|
agent,
|
|
71
|
-
|
|
319
|
+
commandId: command.commandId,
|
|
320
|
+
cwd: resolved.cwd,
|
|
321
|
+
images: command.images,
|
|
72
322
|
onActivity: (activity) => {
|
|
73
323
|
send(socket, encodeAgentActivityEvent({
|
|
74
324
|
activityId: activity.activityId,
|
|
75
325
|
activityType: activity.activityType,
|
|
76
326
|
commandId: command.commandId,
|
|
327
|
+
content: activity.content,
|
|
77
328
|
input: activity.input,
|
|
78
329
|
kind: AGENT_ACTIVITY_KIND,
|
|
79
330
|
locations: activity.locations,
|
|
@@ -99,6 +350,7 @@ async function executePrompt(socket, command, config, audit, sessions, signal) {
|
|
|
99
350
|
return Promise.resolve();
|
|
100
351
|
},
|
|
101
352
|
prompt: command.prompt,
|
|
353
|
+
resources: command.resources,
|
|
102
354
|
sessionId: command.sessionId,
|
|
103
355
|
signal,
|
|
104
356
|
});
|
|
@@ -127,30 +379,37 @@ async function executePrompt(socket, command, config, audit, sessions, signal) {
|
|
|
127
379
|
console.error("Mobius daemon command failed", error);
|
|
128
380
|
}
|
|
129
381
|
}
|
|
130
|
-
function waitForRelayRetry(signal) {
|
|
382
|
+
function waitForRelayRetry(signal, timer) {
|
|
131
383
|
if (signal.aborted) {
|
|
132
384
|
return Promise.resolve();
|
|
133
385
|
}
|
|
134
386
|
return new Promise((resolve) => {
|
|
135
|
-
const
|
|
387
|
+
const retry = timer.schedule(finish, RELAY_RECONNECT_DELAY_MILLISECONDS);
|
|
136
388
|
function finish() {
|
|
137
|
-
|
|
389
|
+
retry.cancel();
|
|
138
390
|
signal.removeEventListener("abort", finish);
|
|
139
391
|
resolve();
|
|
140
392
|
}
|
|
141
393
|
signal.addEventListener("abort", finish, { once: true });
|
|
142
394
|
});
|
|
143
395
|
}
|
|
144
|
-
async function runRelayConnection(config, audit, sessions, signal) {
|
|
145
|
-
const
|
|
396
|
+
async function runRelayConnection(config, directories, audit, sessions, signal, timer) {
|
|
397
|
+
const relayUrl = new URL(config.relayUrl);
|
|
398
|
+
relayUrl.searchParams.set("liveness", "1");
|
|
399
|
+
const socket = await connectRelay(relayUrl, signal);
|
|
146
400
|
const activeTurns = new Map();
|
|
401
|
+
const connectionController = new AbortController();
|
|
402
|
+
sessions.setObserver(new DaemonAcpObserver(socket));
|
|
147
403
|
await new Promise((resolve, reject) => {
|
|
148
404
|
let finished = false;
|
|
149
405
|
const cleanup = () => {
|
|
406
|
+
connectionRefresh.close();
|
|
407
|
+
connectionLiveness.close();
|
|
150
408
|
signal.removeEventListener("abort", stop);
|
|
151
409
|
socket.removeEventListener("close", handleClose);
|
|
152
410
|
socket.removeEventListener("error", handleError);
|
|
153
411
|
socket.removeEventListener("message", handleMessage);
|
|
412
|
+
connectionController.abort();
|
|
154
413
|
for (const turn of activeTurns.values()) {
|
|
155
414
|
turn.abortConnection();
|
|
156
415
|
}
|
|
@@ -176,6 +435,18 @@ async function runRelayConnection(config, audit, sessions, signal) {
|
|
|
176
435
|
socket.close(1000, "Daemon stopped");
|
|
177
436
|
resolveConnection();
|
|
178
437
|
};
|
|
438
|
+
const refreshConnection = () => {
|
|
439
|
+
socket.close(1000, "Refreshing idle relay connection");
|
|
440
|
+
resolveConnection();
|
|
441
|
+
};
|
|
442
|
+
const connectionLiveness = new RelayConnectionLiveness(timer, {
|
|
443
|
+
send: (message) => send(socket, message),
|
|
444
|
+
unresponsive: () => {
|
|
445
|
+
socket.close(4001, "Relay liveness check timed out");
|
|
446
|
+
resolveConnection();
|
|
447
|
+
},
|
|
448
|
+
});
|
|
449
|
+
const connectionRefresh = new RelayConnectionRefresh(timer, () => activeTurns.size === 0, refreshConnection);
|
|
179
450
|
signal.addEventListener("abort", stop, { once: true });
|
|
180
451
|
const handleClose = () => {
|
|
181
452
|
resolveConnection();
|
|
@@ -190,11 +461,21 @@ async function runRelayConnection(config, audit, sessions, signal) {
|
|
|
190
461
|
const handleMessage = (event) => {
|
|
191
462
|
const encoded = event.data;
|
|
192
463
|
if (typeof encoded !== "string") {
|
|
193
|
-
socket.close(
|
|
464
|
+
socket.close(4003, "Text message required");
|
|
465
|
+
rejectConnection();
|
|
194
466
|
return;
|
|
195
467
|
}
|
|
196
468
|
try {
|
|
197
469
|
const kind = decodeMessageKind(encoded);
|
|
470
|
+
if (kind === RUNTIME_LIVENESS_READY_KIND) {
|
|
471
|
+
decodeRuntimeLivenessReadyEvent(encoded);
|
|
472
|
+
connectionLiveness.start();
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if (kind === RUNTIME_HEARTBEAT_ACK_KIND) {
|
|
476
|
+
connectionLiveness.acknowledge(decodeRuntimeHeartbeatAckEvent(encoded));
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
198
479
|
if (kind === SESSION_CANCEL_KIND) {
|
|
199
480
|
const cancellation = decodeSessionCancelCommand(encoded);
|
|
200
481
|
const turn = activeTurns.get(cancellation.commandId);
|
|
@@ -215,10 +496,56 @@ async function runRelayConnection(config, audit, sessions, signal) {
|
|
|
215
496
|
});
|
|
216
497
|
return;
|
|
217
498
|
}
|
|
218
|
-
if (kind
|
|
219
|
-
|
|
499
|
+
if (kind === WORKSPACE_LIST_KIND) {
|
|
500
|
+
const command = decodeWorkspaceListCommand(encoded);
|
|
501
|
+
void executeWorkspaceList(socket, command, directories, audit);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
if (kind === SESSION_PREPARE_KIND) {
|
|
505
|
+
const command = decodeSessionPrepareCommand(encoded);
|
|
506
|
+
void executeSessionPrepare(socket, command, config, directories, audit, sessions, connectionController.signal);
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
if (kind === SESSION_CONFIGURE_KIND) {
|
|
510
|
+
const command = decodeSessionConfigureCommand(encoded);
|
|
511
|
+
void executeSessionConfigure(socket, command, config, directories, audit, sessions, connectionController.signal);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
if (kind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
515
|
+
const command = decodeSessionPermissionRespondCommand(encoded);
|
|
516
|
+
void sessions
|
|
517
|
+
.respondPermission(command)
|
|
518
|
+
.then(() => {
|
|
519
|
+
audit.record({
|
|
520
|
+
commandId: command.interactionId,
|
|
521
|
+
event: "session_permission_resolved",
|
|
522
|
+
sessionId: command.sessionId,
|
|
523
|
+
});
|
|
524
|
+
})
|
|
525
|
+
.catch((error) => {
|
|
526
|
+
reportOperationFailure(socket, command.interactionId, command.sessionId, error);
|
|
527
|
+
});
|
|
220
528
|
return;
|
|
221
529
|
}
|
|
530
|
+
if (kind === SESSION_ELICITATION_RESPOND_KIND) {
|
|
531
|
+
const command = decodeSessionElicitationRespondCommand(encoded);
|
|
532
|
+
void sessions
|
|
533
|
+
.respondElicitation(command)
|
|
534
|
+
.then(() => {
|
|
535
|
+
audit.record({
|
|
536
|
+
commandId: command.interactionId,
|
|
537
|
+
event: "session_elicitation_resolved",
|
|
538
|
+
sessionId: command.sessionId,
|
|
539
|
+
});
|
|
540
|
+
})
|
|
541
|
+
.catch((error) => {
|
|
542
|
+
reportOperationFailure(socket, command.interactionId, command.sessionId, error);
|
|
543
|
+
});
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
if (kind !== SESSION_PROMPT_KIND) {
|
|
547
|
+
throw new Error("Unsupported protocol message");
|
|
548
|
+
}
|
|
222
549
|
const command = decodeSessionPromptCommand(encoded);
|
|
223
550
|
if (activeTurns.has(command.commandId)) {
|
|
224
551
|
reportFailure(socket, command, "duplicate_command", "The command is already running");
|
|
@@ -226,17 +553,19 @@ async function runRelayConnection(config, audit, sessions, signal) {
|
|
|
226
553
|
}
|
|
227
554
|
const turn = new ActiveDaemonTurn(command, new AbortController());
|
|
228
555
|
activeTurns.set(command.commandId, turn);
|
|
229
|
-
void executePrompt(socket, command, config, audit, sessions, turn.signal)
|
|
556
|
+
void executePrompt(socket, command, config, directories, audit, sessions, turn.signal)
|
|
230
557
|
.catch((error) => {
|
|
231
558
|
console.error("Mobius daemon turn failed", error);
|
|
232
559
|
})
|
|
233
560
|
.finally(() => {
|
|
234
561
|
activeTurns.delete(command.commandId);
|
|
562
|
+
connectionRefresh.afterTurnFinished();
|
|
235
563
|
});
|
|
236
564
|
}
|
|
237
565
|
catch (error) {
|
|
238
566
|
console.error("Mobius daemon rejected a message", error);
|
|
239
|
-
socket.close(
|
|
567
|
+
socket.close(4008, "Invalid protocol message");
|
|
568
|
+
rejectConnection();
|
|
240
569
|
}
|
|
241
570
|
};
|
|
242
571
|
socket.addEventListener("close", handleClose);
|
|
@@ -247,13 +576,15 @@ async function runRelayConnection(config, audit, sessions, signal) {
|
|
|
247
576
|
}
|
|
248
577
|
});
|
|
249
578
|
}
|
|
250
|
-
export async function runDaemon(config, audit, signal) {
|
|
251
|
-
const
|
|
579
|
+
export async function runDaemon(config, audit, signal, timer = systemDaemonTimer) {
|
|
580
|
+
const sessionStore = new SqliteAcpSessionStore(config.auditDatabasePath);
|
|
581
|
+
const sessions = new AcpSessionManager(new NullAcpSessionObserver(), sessionStore);
|
|
582
|
+
const directories = new WorkspaceDirectoryService(config.bindingId, config.approvedRoot);
|
|
252
583
|
let retryAnnounced = false;
|
|
253
584
|
try {
|
|
254
585
|
while (!signal.aborted) {
|
|
255
586
|
try {
|
|
256
|
-
await runRelayConnection(config, audit, sessions, signal);
|
|
587
|
+
await runRelayConnection(config, directories, audit, sessions, signal, timer);
|
|
257
588
|
retryAnnounced = false;
|
|
258
589
|
}
|
|
259
590
|
catch {
|
|
@@ -265,11 +596,12 @@ export async function runDaemon(config, audit, signal) {
|
|
|
265
596
|
retryAnnounced = true;
|
|
266
597
|
}
|
|
267
598
|
}
|
|
268
|
-
await waitForRelayRetry(signal);
|
|
599
|
+
await waitForRelayRetry(signal, timer);
|
|
269
600
|
}
|
|
270
601
|
}
|
|
271
602
|
finally {
|
|
272
603
|
await sessions.close();
|
|
604
|
+
sessionStore.close();
|
|
273
605
|
}
|
|
274
606
|
}
|
|
275
607
|
//# sourceMappingURL=run-daemon.js.map
|