@moxt-ai/mobius 0.0.3 → 0.0.5

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.
Files changed (88) hide show
  1. package/dist/acp/session-manager.d.ts.map +1 -1
  2. package/dist/acp/session-manager.js +15 -29
  3. package/dist/acp/session-manager.js.map +1 -1
  4. package/dist/agent-catalog/agent-diagnostics.d.ts +3 -0
  5. package/dist/agent-catalog/agent-diagnostics.d.ts.map +1 -0
  6. package/dist/agent-catalog/agent-diagnostics.js +62 -0
  7. package/dist/agent-catalog/agent-diagnostics.js.map +1 -0
  8. package/dist/agent-catalog/discover-local-agents.d.ts +77 -0
  9. package/dist/agent-catalog/discover-local-agents.d.ts.map +1 -0
  10. package/dist/agent-catalog/discover-local-agents.js +478 -0
  11. package/dist/agent-catalog/discover-local-agents.js.map +1 -0
  12. package/dist/agent-catalog/local-agent.d.ts +38 -0
  13. package/dist/agent-catalog/local-agent.d.ts.map +1 -0
  14. package/dist/agent-catalog/local-agent.js +93 -0
  15. package/dist/agent-catalog/local-agent.js.map +1 -0
  16. package/dist/agent-catalog/publish-agent-catalog.d.ts +3 -0
  17. package/dist/agent-catalog/publish-agent-catalog.d.ts.map +1 -0
  18. package/dist/agent-catalog/publish-agent-catalog.js +58 -0
  19. package/dist/agent-catalog/publish-agent-catalog.js.map +1 -0
  20. package/dist/agent-installation/agent-installation-manager.d.ts +60 -0
  21. package/dist/agent-installation/agent-installation-manager.d.ts.map +1 -0
  22. package/dist/agent-installation/agent-installation-manager.js +242 -0
  23. package/dist/agent-installation/agent-installation-manager.js.map +1 -0
  24. package/dist/agent-installation/automatic-adapter-provisioner.d.ts +18 -0
  25. package/dist/agent-installation/automatic-adapter-provisioner.d.ts.map +1 -0
  26. package/dist/agent-installation/automatic-adapter-provisioner.js +116 -0
  27. package/dist/agent-installation/automatic-adapter-provisioner.js.map +1 -0
  28. package/dist/agent-installation/fixed-agent-install-recipes.d.ts +20 -0
  29. package/dist/agent-installation/fixed-agent-install-recipes.d.ts.map +1 -0
  30. package/dist/agent-installation/fixed-agent-install-recipes.js +163 -0
  31. package/dist/agent-installation/fixed-agent-install-recipes.js.map +1 -0
  32. package/dist/agent-installation/fixed-installation-executor.d.ts +20 -0
  33. package/dist/agent-installation/fixed-installation-executor.d.ts.map +1 -0
  34. package/dist/agent-installation/fixed-installation-executor.js +213 -0
  35. package/dist/agent-installation/fixed-installation-executor.js.map +1 -0
  36. package/dist/agent-installation/installation-grants.d.ts +2 -0
  37. package/dist/agent-installation/installation-grants.d.ts.map +1 -0
  38. package/dist/agent-installation/installation-grants.js +52 -0
  39. package/dist/agent-installation/installation-grants.js.map +1 -0
  40. package/dist/agent-installation/local-installation-authorizer.d.ts +7 -0
  41. package/dist/agent-installation/local-installation-authorizer.d.ts.map +1 -0
  42. package/dist/agent-installation/local-installation-authorizer.js +32 -0
  43. package/dist/agent-installation/local-installation-authorizer.js.map +1 -0
  44. package/dist/agent-installation/managed-adapter-installer.d.ts +21 -0
  45. package/dist/agent-installation/managed-adapter-installer.d.ts.map +1 -0
  46. package/dist/agent-installation/managed-adapter-installer.js +161 -0
  47. package/dist/agent-installation/managed-adapter-installer.js.map +1 -0
  48. package/dist/agent-installation/verified-download.d.ts +24 -0
  49. package/dist/agent-installation/verified-download.d.ts.map +1 -0
  50. package/dist/agent-installation/verified-download.js +158 -0
  51. package/dist/agent-installation/verified-download.js.map +1 -0
  52. package/dist/audit/audit-store.d.ts +1 -0
  53. package/dist/audit/audit-store.d.ts.map +1 -1
  54. package/dist/audit/audit-store.js +12 -4
  55. package/dist/audit/audit-store.js.map +1 -1
  56. package/dist/cli.js +4284 -1178
  57. package/dist/cli.js.map +4 -4
  58. package/dist/config.d.ts +6 -10
  59. package/dist/config.d.ts.map +1 -1
  60. package/dist/config.js +10 -53
  61. package/dist/config.js.map +1 -1
  62. package/dist/index.js +3476 -458
  63. package/dist/index.js.map +4 -4
  64. package/dist/pairing/pair-local-machine.d.ts +2 -2
  65. package/dist/pairing/pair-local-machine.d.ts.map +1 -1
  66. package/dist/pairing/pair-local-machine.js +41 -20
  67. package/dist/pairing/pair-local-machine.js.map +1 -1
  68. package/dist/process/executable.d.ts +40 -0
  69. package/dist/process/executable.d.ts.map +1 -0
  70. package/dist/process/executable.js +313 -0
  71. package/dist/process/executable.js.map +1 -0
  72. package/dist/relay-connection/liveness.d.ts +14 -0
  73. package/dist/relay-connection/liveness.d.ts.map +1 -0
  74. package/dist/relay-connection/liveness.js +102 -0
  75. package/dist/relay-connection/liveness.js.map +1 -0
  76. package/dist/relay-connection/timer.d.ts +8 -0
  77. package/dist/relay-connection/timer.d.ts.map +1 -0
  78. package/dist/relay-connection/timer.js +8 -0
  79. package/dist/relay-connection/timer.js.map +1 -0
  80. package/dist/run-daemon.d.ts +3 -2
  81. package/dist/run-daemon.d.ts.map +1 -1
  82. package/dist/run-daemon.js +272 -22
  83. package/dist/run-daemon.js.map +1 -1
  84. package/dist/storage/state-directory.d.ts +2 -0
  85. package/dist/storage/state-directory.d.ts.map +1 -0
  86. package/dist/storage/state-directory.js +10 -0
  87. package/dist/storage/state-directory.js.map +1 -0
  88. package/package.json +3 -5
@@ -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"}
@@ -1,4 +1,5 @@
1
1
  import type { AuditStore } from "./audit/audit-store.ts";
2
- import { type DaemonConfig } from "./config.ts";
3
- export declare function runDaemon(config: DaemonConfig, audit: AuditStore, signal: AbortSignal): Promise<void>;
2
+ import type { DaemonConfig } from "./config.ts";
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
@@ -1 +1 @@
1
- {"version":3,"file":"run-daemon.d.ts","sourceRoot":"","sources":["../src/run-daemon.ts"],"names":[],"mappings":"AAkEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,aAAa,CAAC;AA8sBhE,wBAAsB,SAAS,CAC7B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,IAAI,CAAC,CAiCf"}
1
+ {"version":3,"file":"run-daemon.d.ts","sourceRoot":"","sources":["../src/run-daemon.ts"],"names":[],"mappings":"AAyGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAGzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,6BAA6B,CAAC;AAu9BrC,wBAAsB,SAAS,CAC7B,MAAM,EAAE,YAAY,EACpB,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,WAAW,EACnB,KAAK,GAAE,WAA+B,GACrC,OAAO,CAAC,IAAI,CAAC,CAqFf"}
@@ -1,14 +1,75 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { basename } from "node:path";
3
- import { AGENT_ACTIVITY_KIND, AGENT_MESSAGE_DELTA_KIND, decodeMessageKind, 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, 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, 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";
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";
7
+ import { supportedLocalAgentSpecifications } from "./agent-catalog/discover-local-agents.js";
8
+ import { LocalAgentCatalogError } from "./agent-catalog/local-agent.js";
9
+ import { publishAgentCatalog } from "./agent-catalog/publish-agent-catalog.js";
10
+ import { AgentInstallationManager, AwaitingLocalApproval, CancelledInstallation, FailedInstallation, RequestedInstallation, RunningInstallation, SucceededInstallation, } from "./agent-installation/agent-installation-manager.js";
11
+ import { AutomaticAdapterProvisioner } from "./agent-installation/automatic-adapter-provisioner.js";
12
+ import { FixedInstallationExecutor } from "./agent-installation/fixed-installation-executor.js";
13
+ import { ConsoleInstallationAuthorizer } from "./agent-installation/local-installation-authorizer.js";
14
+ import { ManagedAdapterInstaller } from "./agent-installation/managed-adapter-installer.js";
7
15
  import { LocalAuthorizationError } from "./authorization/authorize-binding.js";
8
16
  import { WorkspaceDirectoryService } from "./authorization/workspace-directory.js";
9
- import { findLocalAgent } from "./config.js";
10
17
  import { connectRelay } from "./relay-connection/connect.js";
18
+ import { RelayConnectionLiveness } from "./relay-connection/liveness.js";
19
+ import { systemDaemonTimer, } from "./relay-connection/timer.js";
20
+ import { readStateDirectory } from "./storage/state-directory.js";
11
21
  const RELAY_RECONNECT_DELAY_MILLISECONDS = 500;
22
+ const RELAY_CONNECTION_REFRESH_MILLISECONDS = 20 * 60 * 1_000;
23
+ class RelayConnectionRefreshState {
24
+ }
25
+ class ScheduledRelayConnectionRefreshState extends RelayConnectionRefreshState {
26
+ }
27
+ class DueRelayConnectionRefreshState extends RelayConnectionRefreshState {
28
+ }
29
+ class FinishedRelayConnectionRefreshState extends RelayConnectionRefreshState {
30
+ }
31
+ class RelayConnectionRefresh {
32
+ #isSafe;
33
+ #refresh;
34
+ #scheduledTask;
35
+ #state = new ScheduledRelayConnectionRefreshState();
36
+ constructor(timer, isSafe, refresh) {
37
+ this.#isSafe = isSafe;
38
+ this.#refresh = refresh;
39
+ this.#scheduledTask = timer.schedule(this.#request, RELAY_CONNECTION_REFRESH_MILLISECONDS);
40
+ }
41
+ afterTurnFinished = () => {
42
+ if (this.#state instanceof DueRelayConnectionRefreshState) {
43
+ this.#request();
44
+ return;
45
+ }
46
+ if (this.#state instanceof ScheduledRelayConnectionRefreshState ||
47
+ this.#state instanceof FinishedRelayConnectionRefreshState) {
48
+ return;
49
+ }
50
+ throw new Error("Unknown relay connection refresh state");
51
+ };
52
+ close = () => {
53
+ this.#scheduledTask.cancel();
54
+ this.#state = new FinishedRelayConnectionRefreshState();
55
+ };
56
+ #request = () => {
57
+ if (this.#state instanceof FinishedRelayConnectionRefreshState) {
58
+ return;
59
+ }
60
+ if (!(this.#state instanceof ScheduledRelayConnectionRefreshState) &&
61
+ !(this.#state instanceof DueRelayConnectionRefreshState)) {
62
+ throw new Error("Unknown relay connection refresh state");
63
+ }
64
+ if (!this.#isSafe()) {
65
+ this.#state = new DueRelayConnectionRefreshState();
66
+ return;
67
+ }
68
+ this.#state = new FinishedRelayConnectionRefreshState();
69
+ this.#scheduledTask.cancel();
70
+ this.#refresh();
71
+ };
72
+ }
12
73
  class DaemonAcpObserver {
13
74
  #socket;
14
75
  constructor(socket) {
@@ -51,6 +112,110 @@ class DaemonAcpObserver {
51
112
  return Promise.resolve();
52
113
  };
53
114
  }
115
+ class AuditInstallationEvents {
116
+ #audit;
117
+ constructor(audit) {
118
+ this.#audit = audit;
119
+ }
120
+ record = (event, requestId, agentId, details = {}) => {
121
+ this.#audit.record({
122
+ commandId: requestId,
123
+ details,
124
+ event,
125
+ sessionId: `agent-installation:${agentId}`,
126
+ });
127
+ };
128
+ }
129
+ class DaemonInstallationObserver {
130
+ #started = new Set();
131
+ #socket = null;
132
+ connect = (socket) => {
133
+ this.#socket = socket;
134
+ };
135
+ disconnect = (socket) => {
136
+ if (this.#socket === socket) {
137
+ this.#socket = null;
138
+ }
139
+ };
140
+ #send = (socket, message) => {
141
+ try {
142
+ send(socket, message);
143
+ }
144
+ catch {
145
+ if (this.#socket === socket) {
146
+ this.#socket = null;
147
+ }
148
+ }
149
+ };
150
+ onState = (state) => {
151
+ const socket = this.#socket;
152
+ if (socket === null || socket.readyState !== WebSocket.OPEN) {
153
+ return Promise.resolve();
154
+ }
155
+ if (state instanceof RequestedInstallation) {
156
+ return Promise.resolve();
157
+ }
158
+ if (state instanceof AwaitingLocalApproval) {
159
+ this.#send(socket, encodeAgentInstallAwaitingApprovalEvent({
160
+ agentId: state.agentId,
161
+ kind: AGENT_INSTALL_AWAITING_APPROVAL_KIND,
162
+ protocolVersion: MOBIUS_PROTOCOL_VERSION,
163
+ requestId: state.requestId,
164
+ }));
165
+ return Promise.resolve();
166
+ }
167
+ if (state instanceof RunningInstallation) {
168
+ if (!this.#started.has(state.requestId)) {
169
+ this.#started.add(state.requestId);
170
+ this.#send(socket, encodeAgentInstallStartedEvent({
171
+ agentId: state.agentId,
172
+ kind: AGENT_INSTALL_STARTED_KIND,
173
+ protocolVersion: MOBIUS_PROTOCOL_VERSION,
174
+ requestId: state.requestId,
175
+ }));
176
+ }
177
+ this.#send(socket, encodeAgentInstallProgressEvent({
178
+ agentId: state.agentId,
179
+ kind: AGENT_INSTALL_PROGRESS_KIND,
180
+ protocolVersion: MOBIUS_PROTOCOL_VERSION,
181
+ requestId: state.requestId,
182
+ stage: state.stage,
183
+ }));
184
+ return Promise.resolve();
185
+ }
186
+ if (state instanceof SucceededInstallation) {
187
+ this.#send(socket, encodeAgentInstallCompletedEvent({
188
+ agentId: state.agentId,
189
+ authenticationMayBeRequired: state.authenticationMayBeRequired,
190
+ kind: AGENT_INSTALL_COMPLETED_KIND,
191
+ protocolVersion: MOBIUS_PROTOCOL_VERSION,
192
+ requestId: state.requestId,
193
+ }));
194
+ return Promise.resolve();
195
+ }
196
+ if (state instanceof FailedInstallation) {
197
+ this.#send(socket, encodeAgentInstallFailedEvent({
198
+ agentId: state.agentId,
199
+ code: state.code,
200
+ kind: AGENT_INSTALL_FAILED_KIND,
201
+ message: state.message,
202
+ protocolVersion: MOBIUS_PROTOCOL_VERSION,
203
+ requestId: state.requestId,
204
+ }));
205
+ return Promise.resolve();
206
+ }
207
+ if (state instanceof CancelledInstallation) {
208
+ this.#send(socket, encodeAgentInstallCancelledEvent({
209
+ agentId: state.agentId,
210
+ kind: AGENT_INSTALL_CANCELLED_KIND,
211
+ protocolVersion: MOBIUS_PROTOCOL_VERSION,
212
+ requestId: state.requestId,
213
+ }));
214
+ return Promise.resolve();
215
+ }
216
+ throw new Error("Unsupported Agent installation state");
217
+ };
218
+ }
54
219
  class ResolvedSessionDirectories {
55
220
  additionalDirectories;
56
221
  additionalDirectoryEntries;
@@ -106,14 +271,16 @@ function reportFailure(socket, command, code, message) {
106
271
  }
107
272
  function operationErrorCode(error) {
108
273
  if (error instanceof AcpExecutionError ||
109
- error instanceof LocalAuthorizationError) {
274
+ error instanceof LocalAuthorizationError ||
275
+ error instanceof LocalAgentCatalogError) {
110
276
  return error.code;
111
277
  }
112
278
  return "session_operation_failed";
113
279
  }
114
280
  function operationErrorMessage(error) {
115
281
  if (error instanceof AcpExecutionError ||
116
- error instanceof LocalAuthorizationError) {
282
+ error instanceof LocalAuthorizationError ||
283
+ error instanceof LocalAgentCatalogError) {
117
284
  return error.message;
118
285
  }
119
286
  return "The local agent operation could not be completed";
@@ -176,7 +343,7 @@ async function executeWorkspaceList(socket, command, directories, audit) {
176
343
  async function executeSessionPrepare(socket, command, config, directories, audit, sessions, signal) {
177
344
  try {
178
345
  const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
179
- const agent = findLocalAgent(config.agents, command.agentId);
346
+ const agent = config.catalog.find(command.agentId);
180
347
  const prepared = await sessions.prepareSession({
181
348
  additionalDirectories: resolved.additionalDirectories,
182
349
  agent,
@@ -216,7 +383,7 @@ async function executeSessionConfigure(socket, command, config, directories, aud
216
383
  const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
217
384
  const configOptions = await sessions.configureSession({
218
385
  additionalDirectories: resolved.additionalDirectories,
219
- agent: findLocalAgent(config.agents, command.agentId),
386
+ agent: config.catalog.find(command.agentId),
220
387
  booleanValue: command.booleanValue,
221
388
  configId: command.configId,
222
389
  cwd: resolved.cwd,
@@ -246,7 +413,7 @@ async function executeSessionConfigure(socket, command, config, directories, aud
246
413
  async function executePrompt(socket, command, config, directories, audit, sessions, signal) {
247
414
  try {
248
415
  const resolved = await resolveSessionDirectories(directories, command.bindingId, command.directoryId, command.additionalDirectoryIds);
249
- const agent = findLocalAgent(config.agents, command.agentId);
416
+ const agent = config.catalog.find(command.agentId);
250
417
  const processAttemptId = randomUUID();
251
418
  audit.record({
252
419
  commandId: command.commandId,
@@ -322,36 +489,45 @@ async function executePrompt(socket, command, config, directories, audit, sessio
322
489
  event: "session_failed",
323
490
  sessionId: command.sessionId,
324
491
  });
325
- reportFailure(socket, command, "agent_execution_failed", "The local agent could not complete the request");
492
+ const code = operationErrorCode(error);
493
+ reportFailure(socket, command, code === "session_operation_failed" ? "agent_execution_failed" : code, code === "session_operation_failed"
494
+ ? "The local agent could not complete the request"
495
+ : operationErrorMessage(error));
326
496
  console.error("Mobius daemon command failed", error);
327
497
  }
328
498
  }
329
- function waitForRelayRetry(signal) {
499
+ function waitForRelayRetry(signal, timer) {
330
500
  if (signal.aborted) {
331
501
  return Promise.resolve();
332
502
  }
333
503
  return new Promise((resolve) => {
334
- const timeout = setTimeout(finish, RELAY_RECONNECT_DELAY_MILLISECONDS);
504
+ const retry = timer.schedule(finish, RELAY_RECONNECT_DELAY_MILLISECONDS);
335
505
  function finish() {
336
- clearTimeout(timeout);
506
+ retry.cancel();
337
507
  signal.removeEventListener("abort", finish);
338
508
  resolve();
339
509
  }
340
510
  signal.addEventListener("abort", finish, { once: true });
341
511
  });
342
512
  }
343
- async function runRelayConnection(config, directories, audit, sessions, signal) {
344
- const socket = await connectRelay(config.relayUrl, signal);
513
+ async function runRelayConnection(config, directories, audit, sessions, installations, installationObserver, signal, timer) {
514
+ const relayUrl = new URL(config.relayUrl);
515
+ relayUrl.searchParams.set("liveness", "1");
516
+ const socket = await connectRelay(relayUrl, signal);
345
517
  const activeTurns = new Map();
346
518
  const connectionController = new AbortController();
347
519
  sessions.setObserver(new DaemonAcpObserver(socket));
520
+ installationObserver.connect(socket);
348
521
  await new Promise((resolve, reject) => {
349
522
  let finished = false;
350
523
  const cleanup = () => {
524
+ connectionRefresh.close();
525
+ connectionLiveness.close();
351
526
  signal.removeEventListener("abort", stop);
352
527
  socket.removeEventListener("close", handleClose);
353
528
  socket.removeEventListener("error", handleError);
354
529
  socket.removeEventListener("message", handleMessage);
530
+ installationObserver.disconnect(socket);
355
531
  connectionController.abort();
356
532
  for (const turn of activeTurns.values()) {
357
533
  turn.abortConnection();
@@ -378,6 +554,18 @@ async function runRelayConnection(config, directories, audit, sessions, signal)
378
554
  socket.close(1000, "Daemon stopped");
379
555
  resolveConnection();
380
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);
381
569
  signal.addEventListener("abort", stop, { once: true });
382
570
  const handleClose = () => {
383
571
  resolveConnection();
@@ -392,11 +580,21 @@ async function runRelayConnection(config, directories, audit, sessions, signal)
392
580
  const handleMessage = (event) => {
393
581
  const encoded = event.data;
394
582
  if (typeof encoded !== "string") {
395
- socket.close(1003, "Text message required");
583
+ socket.close(4003, "Text message required");
584
+ rejectConnection();
396
585
  return;
397
586
  }
398
587
  try {
399
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;
597
+ }
400
598
  if (kind === SESSION_CANCEL_KIND) {
401
599
  const cancellation = decodeSessionCancelCommand(encoded);
402
600
  const turn = activeTurns.get(cancellation.commandId);
@@ -464,10 +662,27 @@ async function runRelayConnection(config, directories, audit, sessions, signal)
464
662
  });
465
663
  return;
466
664
  }
467
- if (kind !== SESSION_PROMPT_KIND) {
468
- socket.close(1008, "Unsupported protocol message");
665
+ if (kind === AGENT_INSTALL_REQUEST_KIND) {
666
+ const command = decodeAgentInstallRequestCommand(encoded);
667
+ void installations
668
+ .request(command, signal)
669
+ .catch((error) => {
670
+ console.error("Mobius Agent installation failed", error);
671
+ });
672
+ return;
673
+ }
674
+ if (kind === AGENT_INSTALL_CANCEL_KIND) {
675
+ const command = decodeAgentInstallCancelCommand(encoded);
676
+ void installations
677
+ .cancel(command.requestId)
678
+ .catch((error) => {
679
+ reportOperationFailure(socket, command.requestId, "", error);
680
+ });
469
681
  return;
470
682
  }
683
+ if (kind !== SESSION_PROMPT_KIND) {
684
+ throw new Error("Unsupported protocol message");
685
+ }
471
686
  const command = decodeSessionPromptCommand(encoded);
472
687
  if (activeTurns.has(command.commandId)) {
473
688
  reportFailure(socket, command, "duplicate_command", "The command is already running");
@@ -481,11 +696,13 @@ async function runRelayConnection(config, directories, audit, sessions, signal)
481
696
  })
482
697
  .finally(() => {
483
698
  activeTurns.delete(command.commandId);
699
+ connectionRefresh.afterTurnFinished();
484
700
  });
485
701
  }
486
702
  catch (error) {
487
703
  console.error("Mobius daemon rejected a message", error);
488
- socket.close(1008, "Invalid protocol message");
704
+ socket.close(4008, "Invalid protocol message");
705
+ rejectConnection();
489
706
  }
490
707
  };
491
708
  socket.addEventListener("close", handleClose);
@@ -496,15 +713,39 @@ async function runRelayConnection(config, directories, audit, sessions, signal)
496
713
  }
497
714
  });
498
715
  }
499
- export async function runDaemon(config, audit, signal) {
716
+ export async function runDaemon(config, audit, signal, timer = systemDaemonTimer) {
717
+ const installationAudit = new AuditInstallationEvents(audit);
718
+ const stateDirectory = readStateDirectory();
719
+ const specifications = supportedLocalAgentSpecifications();
720
+ const installationExecutor = new FixedInstallationExecutor({
721
+ approvedRoot: config.approvedRoot,
722
+ audit: installationAudit,
723
+ catalog: config.catalog,
724
+ environment: process.env,
725
+ publishCatalog: async (installationSignal) => await publishAgentCatalog(config, installationSignal),
726
+ stateDirectory,
727
+ });
728
+ await new AutomaticAdapterProvisioner({
729
+ approvedRoot: config.approvedRoot,
730
+ audit: installationAudit,
731
+ catalog: config.catalog,
732
+ environment: process.env,
733
+ }, new ManagedAdapterInstaller({
734
+ approvedRoot: config.approvedRoot,
735
+ environment: process.env,
736
+ stateDirectory,
737
+ }), specifications).prepare(signal);
500
738
  const sessionStore = new SqliteAcpSessionStore(config.auditDatabasePath);
501
739
  const sessions = new AcpSessionManager(new NullAcpSessionObserver(), sessionStore);
502
740
  const directories = new WorkspaceDirectoryService(config.bindingId, config.approvedRoot);
741
+ const installationObserver = new DaemonInstallationObserver();
742
+ const installations = new AgentInstallationManager(specifications, new ConsoleInstallationAuthorizer(stateDirectory), installationExecutor, installationObserver, installationAudit);
503
743
  let retryAnnounced = false;
504
744
  try {
505
745
  while (!signal.aborted) {
506
746
  try {
507
- await runRelayConnection(config, directories, audit, sessions, signal);
747
+ await publishAgentCatalog(config, signal);
748
+ await runRelayConnection(config, directories, audit, sessions, installations, installationObserver, signal, timer);
508
749
  retryAnnounced = false;
509
750
  }
510
751
  catch {
@@ -516,12 +757,21 @@ export async function runDaemon(config, audit, signal) {
516
757
  retryAnnounced = true;
517
758
  }
518
759
  }
519
- await waitForRelayRetry(signal);
760
+ await waitForRelayRetry(signal, timer);
520
761
  }
521
762
  }
522
763
  finally {
523
- await sessions.close();
524
- sessionStore.close();
764
+ try {
765
+ await installations.close();
766
+ }
767
+ finally {
768
+ try {
769
+ await sessions.close();
770
+ }
771
+ finally {
772
+ sessionStore.close();
773
+ }
774
+ }
525
775
  }
526
776
  }
527
777
  //# sourceMappingURL=run-daemon.js.map