@moxt-ai/mobius-sdk 0.0.64 → 0.0.66
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/channel.d.ts +1 -1
- package/dist/channel.d.ts.map +1 -1
- package/dist/channel.js.map +1 -1
- package/dist/client.d.ts +1 -89
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +5 -558
- package/dist/client.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/resources.d.ts +1 -97
- package/dist/resources.d.ts.map +1 -1
- package/dist/resources.js +5 -194
- package/dist/resources.js.map +1 -1
- package/dist/service-client.d.ts +4 -56
- package/dist/service-client.d.ts.map +1 -1
- package/dist/service-client.js +4 -569
- package/dist/service-client.js.map +1 -1
- package/package.json +2 -2
package/dist/client.js
CHANGED
|
@@ -1,26 +1,10 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { 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, decodeAgentInstallCancelledEvent, decodeAgentInstallCompletedEvent, decodeAgentInstallFailedEvent, decodeAgentInstallProgressEvent, decodeAgentInstallStartedEvent, decodeDaemonToServiceMessage, decodeMessageKind, decodeRuntimeStatusEvent, decodeSessionOperationFailedEvent, encodeAgentInstallCancelCommand, encodeAgentInstallRequestCommand, RUNTIME_STATUS_KIND, SESSION_OPERATION_FAILED_KIND, } from "@moxt-ai/mobius-protocol";
|
|
2
2
|
import ReconnectingWebSocket, {} from "partysocket/ws";
|
|
3
3
|
const CONNECTION_TIMEOUT_MILLISECONDS = 15_000;
|
|
4
4
|
const MAX_RECONNECTION_DELAY_MILLISECONDS = 10_000;
|
|
5
5
|
const MIN_RECONNECTION_DELAY_MILLISECONDS = 500;
|
|
6
|
-
const OPERATION_TIMEOUT_MILLISECONDS = 120_000;
|
|
7
6
|
const INSTALLATION_TIMEOUT_MILLISECONDS = 11 * 60 * 1_000;
|
|
8
7
|
const TERMINAL_RELAY_CLOSE_CODES = new Set([1002, 1003, 1007, 1008, 4003, 4008]);
|
|
9
|
-
const HISTORY_CHANGING_MESSAGE_KINDS = new Set([
|
|
10
|
-
AGENT_ACTIVITY_KIND,
|
|
11
|
-
AGENT_ACTIVITY_NARRATION_KIND,
|
|
12
|
-
AGENT_CONTENT_KIND,
|
|
13
|
-
AGENT_MESSAGE_DELTA_KIND,
|
|
14
|
-
NATIVE_SESSION_RESULT_KIND,
|
|
15
|
-
SESSION_COMPLETED_KIND,
|
|
16
|
-
SESSION_ELICITATION_REQUESTED_KIND,
|
|
17
|
-
SESSION_ELICITATION_RESOLVED_KIND,
|
|
18
|
-
SESSION_FAILED_KIND,
|
|
19
|
-
SESSION_FORKED_KIND,
|
|
20
|
-
SESSION_PERMISSION_REQUESTED_KIND,
|
|
21
|
-
SESSION_PERMISSION_RESOLVED_KIND,
|
|
22
|
-
SESSION_STARTED_KIND,
|
|
23
|
-
]);
|
|
24
8
|
export class MobiusClientError extends Error {
|
|
25
9
|
code;
|
|
26
10
|
constructor(code, message, options = {}) {
|
|
@@ -35,15 +19,6 @@ function readMessageText(event) {
|
|
|
35
19
|
}
|
|
36
20
|
return event.data;
|
|
37
21
|
}
|
|
38
|
-
function readInboundMessage(event) {
|
|
39
|
-
const encoded = readMessageText(event);
|
|
40
|
-
if (decodeMessageKind(encoded) !== SESSION_EVENT_KIND) {
|
|
41
|
-
return encoded;
|
|
42
|
-
}
|
|
43
|
-
const envelope = decodeSessionEventEnvelope(encoded);
|
|
44
|
-
decodeRecordedSessionEvent(envelope.payload);
|
|
45
|
-
return envelope.payload;
|
|
46
|
-
}
|
|
47
22
|
function encodeOutboundMessage(encode) {
|
|
48
23
|
try {
|
|
49
24
|
return encode();
|
|
@@ -93,8 +68,6 @@ function waitForOpen(socket) {
|
|
|
93
68
|
}
|
|
94
69
|
export class MobiusClient {
|
|
95
70
|
#connectionController;
|
|
96
|
-
#onActivity;
|
|
97
|
-
#onHistoryChanged;
|
|
98
71
|
#onProtocolError;
|
|
99
72
|
#onStatus;
|
|
100
73
|
#options;
|
|
@@ -102,8 +75,6 @@ export class MobiusClient {
|
|
|
102
75
|
constructor(socket, options, connectionController = null) {
|
|
103
76
|
this.#connectionController = connectionController;
|
|
104
77
|
this.#options = options;
|
|
105
|
-
this.#onActivity = options.onActivity ?? (() => undefined);
|
|
106
|
-
this.#onHistoryChanged = options.onHistoryChanged ?? (() => undefined);
|
|
107
78
|
this.#onProtocolError = options.onProtocolError ?? (() => undefined);
|
|
108
79
|
this.#onStatus = options.onStatus ?? (() => undefined);
|
|
109
80
|
this.#socket = socket;
|
|
@@ -113,203 +84,6 @@ export class MobiusClient {
|
|
|
113
84
|
ready = async () => {
|
|
114
85
|
await waitForOpen(this.#socket);
|
|
115
86
|
};
|
|
116
|
-
listWorkspace = async (request, signal) => {
|
|
117
|
-
const requestId = request.requestId ?? crypto.randomUUID();
|
|
118
|
-
return await this.#requestReply(requestId, encodeOutboundMessage(() => encodeWorkspaceListCommand({
|
|
119
|
-
bindingId: request.bindingId,
|
|
120
|
-
directoryId: request.directoryId,
|
|
121
|
-
kind: WORKSPACE_LIST_KIND,
|
|
122
|
-
requestId,
|
|
123
|
-
})), WORKSPACE_ENTRIES_KIND, decodeWorkspaceEntriesEvent, (event) => event.requestId, signal);
|
|
124
|
-
};
|
|
125
|
-
readFile = async (sessionId, path, signal, maxBytes = 32 * 1024 * 1024) => {
|
|
126
|
-
signal.throwIfAborted();
|
|
127
|
-
if (this.#socket.readyState !== WebSocket.OPEN) {
|
|
128
|
-
throw new MobiusClientError("runtime_offline", "The local runtime is offline");
|
|
129
|
-
}
|
|
130
|
-
const requestId = crypto.randomUUID();
|
|
131
|
-
let cleanup = () => undefined;
|
|
132
|
-
const result = new Promise((resolve, reject) => {
|
|
133
|
-
let metadata = null;
|
|
134
|
-
const chunks = [];
|
|
135
|
-
let receivedBytes = 0;
|
|
136
|
-
let nextSequence = 0;
|
|
137
|
-
const finish = () => {
|
|
138
|
-
this.#socket.removeEventListener("message", handleMessage);
|
|
139
|
-
this.#socket.removeEventListener("close", handleClose);
|
|
140
|
-
signal.removeEventListener("abort", handleAbort);
|
|
141
|
-
};
|
|
142
|
-
cleanup = finish;
|
|
143
|
-
const fail = (error) => {
|
|
144
|
-
finish();
|
|
145
|
-
reject(error);
|
|
146
|
-
};
|
|
147
|
-
const handleAbort = () => fail(signal.reason);
|
|
148
|
-
const handleClose = () => fail(new MobiusClientError("connection_closed", "The connection closed while reading the file"));
|
|
149
|
-
const handleMessage = (event) => {
|
|
150
|
-
try {
|
|
151
|
-
const encoded = readInboundMessage(event);
|
|
152
|
-
const kind = decodeMessageKind(encoded);
|
|
153
|
-
if (kind === FILE_BEGIN_KIND) {
|
|
154
|
-
const begin = decodeFileBeginEvent(encoded);
|
|
155
|
-
if (begin.requestId !== requestId) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
if (begin.byteLength > maxBytes) {
|
|
159
|
-
fail(new MobiusClientError("file_too_large", "The local file is too large"));
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
metadata = begin;
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
if (kind === FILE_CHUNK_KIND) {
|
|
166
|
-
const chunk = decodeFileChunkEvent(encoded);
|
|
167
|
-
if (chunk.requestId !== requestId) {
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
if (metadata === null || chunk.sequence !== nextSequence) {
|
|
171
|
-
throw new MobiusClientError("invalid_protocol_message", "File chunks are out of order");
|
|
172
|
-
}
|
|
173
|
-
const binary = atob(chunk.data);
|
|
174
|
-
const bytes = Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
175
|
-
receivedBytes += bytes.byteLength;
|
|
176
|
-
if (receivedBytes > metadata.byteLength || receivedBytes > maxBytes) {
|
|
177
|
-
throw new MobiusClientError("invalid_protocol_message", "File exceeded its advertised size");
|
|
178
|
-
}
|
|
179
|
-
chunks.push(bytes);
|
|
180
|
-
nextSequence += 1;
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
if (kind === FILE_END_KIND) {
|
|
184
|
-
const end = decodeFileEndEvent(encoded);
|
|
185
|
-
if (end.requestId !== requestId) {
|
|
186
|
-
return;
|
|
187
|
-
}
|
|
188
|
-
if (metadata === null || receivedBytes !== metadata.byteLength) {
|
|
189
|
-
throw new MobiusClientError("invalid_protocol_message", "File transfer was incomplete");
|
|
190
|
-
}
|
|
191
|
-
const bytes = new Uint8Array(receivedBytes);
|
|
192
|
-
let offset = 0;
|
|
193
|
-
for (const chunk of chunks) {
|
|
194
|
-
bytes.set(chunk, offset);
|
|
195
|
-
offset += chunk.byteLength;
|
|
196
|
-
}
|
|
197
|
-
finish();
|
|
198
|
-
resolve({
|
|
199
|
-
bytes,
|
|
200
|
-
mediaType: metadata.mediaType,
|
|
201
|
-
modifiedAt: metadata.modifiedAt,
|
|
202
|
-
name: metadata.name,
|
|
203
|
-
});
|
|
204
|
-
return;
|
|
205
|
-
}
|
|
206
|
-
if (kind === FILE_FAILED_KIND) {
|
|
207
|
-
const failed = decodeFileFailedEvent(encoded);
|
|
208
|
-
if (failed.requestId === requestId) {
|
|
209
|
-
fail(new MobiusClientError(failed.code, failed.message));
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
catch (error) {
|
|
214
|
-
fail(error);
|
|
215
|
-
}
|
|
216
|
-
};
|
|
217
|
-
this.#socket.addEventListener("message", handleMessage);
|
|
218
|
-
this.#socket.addEventListener("close", handleClose, { once: true });
|
|
219
|
-
signal.addEventListener("abort", handleAbort, { once: true });
|
|
220
|
-
});
|
|
221
|
-
try {
|
|
222
|
-
sendImmediately(this.#socket, encodeOutboundMessage(() => encodeFileReadCommand({
|
|
223
|
-
kind: FILE_READ_KIND,
|
|
224
|
-
maxBytes,
|
|
225
|
-
path,
|
|
226
|
-
requestId,
|
|
227
|
-
sessionId,
|
|
228
|
-
})));
|
|
229
|
-
}
|
|
230
|
-
catch (error) {
|
|
231
|
-
cleanup();
|
|
232
|
-
throw error;
|
|
233
|
-
}
|
|
234
|
-
return await result;
|
|
235
|
-
};
|
|
236
|
-
prepareSession = async (request, signal) => {
|
|
237
|
-
const requestId = request.requestId ?? crypto.randomUUID();
|
|
238
|
-
return await this.#requestReply(requestId, encodeOutboundMessage(() => encodeSessionPrepareCommand({
|
|
239
|
-
additionalDirectoryIds: request.additionalDirectoryIds,
|
|
240
|
-
agentId: request.agentId,
|
|
241
|
-
bindingId: request.bindingId,
|
|
242
|
-
directoryId: request.directoryId,
|
|
243
|
-
kind: SESSION_PREPARE_KIND,
|
|
244
|
-
requestId,
|
|
245
|
-
sessionId: request.sessionId,
|
|
246
|
-
...(request.transient === true ? { transient: true } : {}),
|
|
247
|
-
})), SESSION_READY_KIND, decodeSessionReadyEvent, (event) => event.requestId, signal);
|
|
248
|
-
};
|
|
249
|
-
manageNativeSession = async (command, signal) => {
|
|
250
|
-
signal.throwIfAborted();
|
|
251
|
-
return await this.#requestReply(command.requestId, encodeOutboundMessage(() => encodeNativeSessionRequest(command)), NATIVE_SESSION_RESULT_KIND, decodeNativeSessionResult, (event) => event.requestId, signal, () => {
|
|
252
|
-
try {
|
|
253
|
-
if (this.#socket.readyState === WebSocket.OPEN) {
|
|
254
|
-
sendImmediately(this.#socket, encodeNativeSessionCancel({
|
|
255
|
-
kind: NATIVE_SESSION_CANCEL_KIND,
|
|
256
|
-
requestId: command.requestId,
|
|
257
|
-
sessionId: command.sessionId,
|
|
258
|
-
}));
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
catch {
|
|
262
|
-
// The local runtime also cancels requests when the connection closes.
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
};
|
|
266
|
-
configureSession = async (request, signal) => {
|
|
267
|
-
const requestId = request.requestId ?? crypto.randomUUID();
|
|
268
|
-
return await this.#requestReply(requestId, encodeOutboundMessage(() => encodeSessionConfigureCommand({
|
|
269
|
-
additionalDirectoryIds: request.additionalDirectoryIds,
|
|
270
|
-
agentId: request.agentId,
|
|
271
|
-
bindingId: request.bindingId,
|
|
272
|
-
booleanValue: request.booleanValue,
|
|
273
|
-
configId: request.configId,
|
|
274
|
-
directoryId: request.directoryId,
|
|
275
|
-
kind: SESSION_CONFIGURE_KIND,
|
|
276
|
-
requestId,
|
|
277
|
-
sessionId: request.sessionId,
|
|
278
|
-
value: request.value,
|
|
279
|
-
valueType: request.valueType,
|
|
280
|
-
})), SESSION_CONFIG_UPDATED_KIND, decodeSessionConfigUpdatedEvent, (event) => event.requestId, signal);
|
|
281
|
-
};
|
|
282
|
-
respondPermission = async (request) => await this.#requestReply(request.interactionId, encodeOutboundMessage(() => encodeSessionPermissionRespondCommand({
|
|
283
|
-
interactionId: request.interactionId,
|
|
284
|
-
kind: SESSION_PERMISSION_RESPOND_KIND,
|
|
285
|
-
optionId: request.optionId,
|
|
286
|
-
outcome: request.outcome,
|
|
287
|
-
sessionId: request.sessionId,
|
|
288
|
-
})), SESSION_PERMISSION_RESOLVED_KIND, decodeSessionPermissionResolvedEvent, (event) => event.interactionId);
|
|
289
|
-
respondElicitation = async (request) => await this.#requestReply(request.interactionId, encodeOutboundMessage(() => encodeSessionElicitationRespondCommand({
|
|
290
|
-
action: request.action,
|
|
291
|
-
content: request.content,
|
|
292
|
-
interactionId: request.interactionId,
|
|
293
|
-
kind: SESSION_ELICITATION_RESPOND_KIND,
|
|
294
|
-
sessionId: request.sessionId,
|
|
295
|
-
})), SESSION_ELICITATION_RESOLVED_KIND, decodeSessionElicitationResolvedEvent, (event) => event.interactionId);
|
|
296
|
-
cancel = (request) => {
|
|
297
|
-
if (this.#socket.readyState !== WebSocket.OPEN) {
|
|
298
|
-
throw new MobiusClientError("runtime_offline", "The relay connection is not open");
|
|
299
|
-
}
|
|
300
|
-
try {
|
|
301
|
-
sendImmediately(this.#socket, encodeOutboundMessage(() => encodeSessionCancelCommand({
|
|
302
|
-
commandId: request.commandId,
|
|
303
|
-
kind: SESSION_CANCEL_KIND,
|
|
304
|
-
sessionId: request.sessionId,
|
|
305
|
-
})));
|
|
306
|
-
}
|
|
307
|
-
catch (error) {
|
|
308
|
-
throw new MobiusClientError("connection_closed", "Could not send cancellation to the relay", {
|
|
309
|
-
cause: error,
|
|
310
|
-
});
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
87
|
cancelAgentInstallation = (request) => {
|
|
314
88
|
if (this.#socket.readyState !== WebSocket.OPEN) {
|
|
315
89
|
throw new MobiusClientError("runtime_offline", "The relay connection is not open");
|
|
@@ -347,7 +121,7 @@ export class MobiusClient {
|
|
|
347
121
|
};
|
|
348
122
|
const handleMessage = (message) => {
|
|
349
123
|
try {
|
|
350
|
-
const encoded =
|
|
124
|
+
const encoded = readMessageText(message);
|
|
351
125
|
const kind = decodeMessageKind(encoded);
|
|
352
126
|
if (kind === AGENT_INSTALL_COMPLETED_KIND) {
|
|
353
127
|
const completed = decodeAgentInstallCompletedEvent(encoded);
|
|
@@ -402,195 +176,6 @@ export class MobiusClient {
|
|
|
402
176
|
}
|
|
403
177
|
return await result;
|
|
404
178
|
};
|
|
405
|
-
prompt = async (request) => {
|
|
406
|
-
if (this.#socket.readyState !== WebSocket.OPEN) {
|
|
407
|
-
throw new MobiusClientError("runtime_offline", "The relay connection is not open");
|
|
408
|
-
}
|
|
409
|
-
const commandId = request.commandId ?? crypto.randomUUID();
|
|
410
|
-
const sessionId = request.sessionId ?? crypto.randomUUID();
|
|
411
|
-
const outbound = encodeOutboundMessage(() => encodeSessionPromptCommand({
|
|
412
|
-
additionalDirectoryIds: request.additionalDirectoryIds ?? [],
|
|
413
|
-
agentId: request.agentId,
|
|
414
|
-
bindingId: request.bindingId,
|
|
415
|
-
commandId,
|
|
416
|
-
directoryId: request.directoryId ?? "default",
|
|
417
|
-
disposition: request.disposition ?? SessionPromptDisposition.Reject,
|
|
418
|
-
images: request.images ?? [],
|
|
419
|
-
kind: SESSION_PROMPT_KIND,
|
|
420
|
-
prompt: request.prompt,
|
|
421
|
-
resources: request.resources ?? [],
|
|
422
|
-
...(request.sessionConfigurations === undefined
|
|
423
|
-
? {}
|
|
424
|
-
: { sessionConfigurations: request.sessionConfigurations }),
|
|
425
|
-
sessionId,
|
|
426
|
-
targetCommandId: request.targetCommandId ?? "",
|
|
427
|
-
}));
|
|
428
|
-
let cleanupPrompt = () => undefined;
|
|
429
|
-
const result = new Promise((resolve, reject) => {
|
|
430
|
-
const cleanup = () => {
|
|
431
|
-
this.#socket.removeEventListener("message", handleMessage);
|
|
432
|
-
this.#socket.removeEventListener("close", handleSocketClose);
|
|
433
|
-
};
|
|
434
|
-
cleanupPrompt = cleanup;
|
|
435
|
-
const handleSocketClose = () => {
|
|
436
|
-
cleanup();
|
|
437
|
-
reject(new MobiusClientError("connection_closed", "Relay connection closed while the request was running"));
|
|
438
|
-
};
|
|
439
|
-
const handleMessage = (event) => {
|
|
440
|
-
try {
|
|
441
|
-
const encoded = readInboundMessage(event);
|
|
442
|
-
const kind = decodeMessageKind(encoded);
|
|
443
|
-
if (kind === SESSION_STARTED_KIND) {
|
|
444
|
-
const message = decodeSessionStartedEvent(encoded);
|
|
445
|
-
if (message.commandId === commandId) {
|
|
446
|
-
request.observer?.onStarted?.(message.attemptId);
|
|
447
|
-
}
|
|
448
|
-
return;
|
|
449
|
-
}
|
|
450
|
-
if (kind === AGENT_MESSAGE_DELTA_KIND) {
|
|
451
|
-
const message = decodeAgentMessageDeltaEvent(encoded);
|
|
452
|
-
if (message.commandId === commandId) {
|
|
453
|
-
request.observer?.onDelta?.(message.text, String(message.sequence));
|
|
454
|
-
}
|
|
455
|
-
return;
|
|
456
|
-
}
|
|
457
|
-
if (kind === AGENT_ACTIVITY_KIND) {
|
|
458
|
-
const message = decodeAgentActivityEvent(encoded);
|
|
459
|
-
if (message.commandId === commandId) {
|
|
460
|
-
request.observer?.onActivity?.(message);
|
|
461
|
-
}
|
|
462
|
-
return;
|
|
463
|
-
}
|
|
464
|
-
if (kind === AGENT_ACTIVITY_NARRATION_KIND) {
|
|
465
|
-
const message = decodeAgentActivityNarrationEvent(encoded);
|
|
466
|
-
if (message.commandId === commandId) {
|
|
467
|
-
request.observer?.onNarration?.(message);
|
|
468
|
-
}
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
if (kind === AGENT_CONTENT_KIND) {
|
|
472
|
-
const message = decodeAgentContentEvent(encoded);
|
|
473
|
-
if (message.commandId === commandId) {
|
|
474
|
-
request.observer?.onContent?.(message);
|
|
475
|
-
}
|
|
476
|
-
return;
|
|
477
|
-
}
|
|
478
|
-
if (kind === SESSION_COMPLETED_KIND) {
|
|
479
|
-
const message = decodeSessionCompletedEvent(encoded);
|
|
480
|
-
if (message.commandId === commandId) {
|
|
481
|
-
cleanup();
|
|
482
|
-
resolve({
|
|
483
|
-
sessionId: message.sessionId,
|
|
484
|
-
stopReason: message.stopReason,
|
|
485
|
-
});
|
|
486
|
-
}
|
|
487
|
-
return;
|
|
488
|
-
}
|
|
489
|
-
if (kind === SESSION_OPERATION_FAILED_KIND) {
|
|
490
|
-
const failure = decodeSessionOperationFailedEvent(encoded);
|
|
491
|
-
if (failure.requestId === commandId) {
|
|
492
|
-
cleanup();
|
|
493
|
-
reject(new MobiusClientError(failure.code, failure.message));
|
|
494
|
-
}
|
|
495
|
-
return;
|
|
496
|
-
}
|
|
497
|
-
if (kind === SESSION_FAILED_KIND) {
|
|
498
|
-
const message = decodeSessionFailedEvent(encoded);
|
|
499
|
-
if (message.commandId === commandId) {
|
|
500
|
-
cleanup();
|
|
501
|
-
reject(new MobiusClientError(message.code, message.message));
|
|
502
|
-
}
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
catch (error) {
|
|
506
|
-
cleanup();
|
|
507
|
-
reject(new MobiusClientError("invalid_protocol_message", "Received an invalid message from the relay", { cause: error }));
|
|
508
|
-
}
|
|
509
|
-
};
|
|
510
|
-
this.#socket.addEventListener("message", handleMessage);
|
|
511
|
-
this.#socket.addEventListener("close", handleSocketClose, { once: true });
|
|
512
|
-
});
|
|
513
|
-
try {
|
|
514
|
-
sendImmediately(this.#socket, outbound);
|
|
515
|
-
}
|
|
516
|
-
catch (error) {
|
|
517
|
-
cleanupPrompt();
|
|
518
|
-
throw new MobiusClientError("connection_closed", "Could not send the request to the relay", {
|
|
519
|
-
cause: error,
|
|
520
|
-
});
|
|
521
|
-
}
|
|
522
|
-
return result;
|
|
523
|
-
};
|
|
524
|
-
#requestReply = async (requestId, outbound, expectedKind, decode, identify, signal, cancel) => {
|
|
525
|
-
signal?.throwIfAborted();
|
|
526
|
-
if (this.#socket.readyState !== WebSocket.OPEN) {
|
|
527
|
-
throw new MobiusClientError("runtime_offline", "The relay connection is not open");
|
|
528
|
-
}
|
|
529
|
-
let cleanupRequest = () => undefined;
|
|
530
|
-
const result = new Promise((resolve, reject) => {
|
|
531
|
-
const timeout = window.setTimeout(() => {
|
|
532
|
-
cleanup();
|
|
533
|
-
cancel?.();
|
|
534
|
-
reject(new MobiusClientError("operation_timeout", "Timed out while waiting for the local runtime"));
|
|
535
|
-
}, OPERATION_TIMEOUT_MILLISECONDS);
|
|
536
|
-
const cleanup = () => {
|
|
537
|
-
window.clearTimeout(timeout);
|
|
538
|
-
this.#socket.removeEventListener("message", handleMessage);
|
|
539
|
-
this.#socket.removeEventListener("close", handleClose);
|
|
540
|
-
signal?.removeEventListener("abort", handleAbort);
|
|
541
|
-
};
|
|
542
|
-
const handleAbort = () => {
|
|
543
|
-
cleanup();
|
|
544
|
-
cancel?.();
|
|
545
|
-
reject(signal?.reason ??
|
|
546
|
-
new MobiusClientError("operation_cancelled", "Native session operation cancelled"));
|
|
547
|
-
};
|
|
548
|
-
cleanupRequest = cleanup;
|
|
549
|
-
const handleClose = () => {
|
|
550
|
-
cleanup();
|
|
551
|
-
reject(new MobiusClientError("connection_closed", "Relay connection closed while the operation was running"));
|
|
552
|
-
};
|
|
553
|
-
const handleMessage = (message) => {
|
|
554
|
-
try {
|
|
555
|
-
const encoded = readInboundMessage(message);
|
|
556
|
-
const kind = decodeMessageKind(encoded);
|
|
557
|
-
if (kind === SESSION_OPERATION_FAILED_KIND) {
|
|
558
|
-
const failure = decodeSessionOperationFailedEvent(encoded);
|
|
559
|
-
if (failure.requestId === requestId) {
|
|
560
|
-
cleanup();
|
|
561
|
-
reject(new MobiusClientError(failure.code, failure.message));
|
|
562
|
-
}
|
|
563
|
-
return;
|
|
564
|
-
}
|
|
565
|
-
if (kind !== expectedKind) {
|
|
566
|
-
return;
|
|
567
|
-
}
|
|
568
|
-
const event = decode(encoded);
|
|
569
|
-
if (identify(event) === requestId) {
|
|
570
|
-
cleanup();
|
|
571
|
-
resolve(event);
|
|
572
|
-
}
|
|
573
|
-
}
|
|
574
|
-
catch (error) {
|
|
575
|
-
cleanup();
|
|
576
|
-
reject(new MobiusClientError("invalid_protocol_message", "Received an invalid message from the relay", { cause: error }));
|
|
577
|
-
}
|
|
578
|
-
};
|
|
579
|
-
this.#socket.addEventListener("message", handleMessage);
|
|
580
|
-
this.#socket.addEventListener("close", handleClose, { once: true });
|
|
581
|
-
signal?.addEventListener("abort", handleAbort, { once: true });
|
|
582
|
-
});
|
|
583
|
-
try {
|
|
584
|
-
sendImmediately(this.#socket, outbound);
|
|
585
|
-
}
|
|
586
|
-
catch (error) {
|
|
587
|
-
cleanupRequest();
|
|
588
|
-
throw new MobiusClientError("connection_closed", "Could not send the operation to the relay", {
|
|
589
|
-
cause: error,
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
return await result;
|
|
593
|
-
};
|
|
594
179
|
close = () => {
|
|
595
180
|
this.#connectionController?.abort(new MobiusClientError("connection_closed", "The browser relay connection was closed"));
|
|
596
181
|
this.#socket.removeEventListener("message", this.#handleChannelMessage);
|
|
@@ -606,112 +191,13 @@ export class MobiusClient {
|
|
|
606
191
|
};
|
|
607
192
|
#handleChannelMessage = (event) => {
|
|
608
193
|
try {
|
|
609
|
-
const encoded =
|
|
194
|
+
const encoded = readMessageText(event);
|
|
610
195
|
const kind = decodeMessageKind(encoded);
|
|
611
196
|
if (kind === RUNTIME_STATUS_KIND) {
|
|
612
197
|
this.#onStatus(decodeRuntimeStatusEvent(encoded).online);
|
|
613
198
|
return;
|
|
614
199
|
}
|
|
615
|
-
if (kind ===
|
|
616
|
-
decodeSessionPromptCommand(encoded);
|
|
617
|
-
}
|
|
618
|
-
else if (kind === NATIVE_SESSION_REQUEST_KIND) {
|
|
619
|
-
decodeNativeSessionCommand(encoded);
|
|
620
|
-
}
|
|
621
|
-
else if (kind === NATIVE_SESSION_CANCEL_KIND) {
|
|
622
|
-
decodeNativeSessionCancel(encoded);
|
|
623
|
-
}
|
|
624
|
-
else if (kind === NATIVE_SESSION_RESULT_KIND) {
|
|
625
|
-
this.#options.onNativeSessionResult?.(decodeNativeSessionResult(encoded));
|
|
626
|
-
}
|
|
627
|
-
else if (kind === SESSION_CANCEL_KIND) {
|
|
628
|
-
decodeSessionCancelCommand(encoded);
|
|
629
|
-
}
|
|
630
|
-
else if (kind === WORKSPACE_LIST_KIND) {
|
|
631
|
-
decodeWorkspaceListCommand(encoded);
|
|
632
|
-
}
|
|
633
|
-
else if (kind === WORKSPACE_ENTRIES_KIND) {
|
|
634
|
-
this.#options.onWorkspace?.(decodeWorkspaceEntriesEvent(encoded));
|
|
635
|
-
}
|
|
636
|
-
else if (kind === FILE_READ_KIND) {
|
|
637
|
-
// The Relay echoes host control requests to sibling tabs.
|
|
638
|
-
}
|
|
639
|
-
else if (kind === FILE_BEGIN_KIND) {
|
|
640
|
-
decodeFileBeginEvent(encoded);
|
|
641
|
-
}
|
|
642
|
-
else if (kind === FILE_CHUNK_KIND) {
|
|
643
|
-
decodeFileChunkEvent(encoded);
|
|
644
|
-
}
|
|
645
|
-
else if (kind === FILE_END_KIND) {
|
|
646
|
-
decodeFileEndEvent(encoded);
|
|
647
|
-
}
|
|
648
|
-
else if (kind === FILE_FAILED_KIND) {
|
|
649
|
-
decodeFileFailedEvent(encoded);
|
|
650
|
-
}
|
|
651
|
-
else if (kind === SESSION_PREPARE_KIND) {
|
|
652
|
-
decodeSessionPrepareCommand(encoded);
|
|
653
|
-
}
|
|
654
|
-
else if (kind === SESSION_READY_KIND) {
|
|
655
|
-
this.#options.onReady?.(decodeSessionReadyEvent(encoded));
|
|
656
|
-
}
|
|
657
|
-
else if (kind === SESSION_STATUS_CHANGED_KIND) {
|
|
658
|
-
this.#options.onSessionStatusChanged?.(decodeSessionStatusChangedEvent(encoded));
|
|
659
|
-
}
|
|
660
|
-
else if (kind === SESSION_CONFIGURE_KIND) {
|
|
661
|
-
decodeSessionConfigureCommand(encoded);
|
|
662
|
-
}
|
|
663
|
-
else if (kind === SESSION_CONFIG_UPDATED_KIND) {
|
|
664
|
-
this.#options.onConfig?.(decodeSessionConfigUpdatedEvent(encoded));
|
|
665
|
-
}
|
|
666
|
-
else if (kind === SESSION_COMMANDS_UPDATED_KIND) {
|
|
667
|
-
this.#options.onCommands?.(decodeSessionCommandsUpdatedEvent(encoded));
|
|
668
|
-
}
|
|
669
|
-
else if (kind === SESSION_INFO_UPDATED_KIND) {
|
|
670
|
-
this.#options.onInfo?.(decodeSessionInfoUpdatedEvent(encoded));
|
|
671
|
-
}
|
|
672
|
-
else if (kind === SESSION_USAGE_UPDATED_KIND) {
|
|
673
|
-
this.#options.onUsage?.(decodeSessionUsageUpdatedEvent(encoded));
|
|
674
|
-
}
|
|
675
|
-
else if (kind === SESSION_PERMISSION_REQUESTED_KIND) {
|
|
676
|
-
this.#options.onPermission?.(decodeSessionPermissionRequestedEvent(encoded));
|
|
677
|
-
}
|
|
678
|
-
else if (kind === SESSION_PERMISSION_RESPOND_KIND) {
|
|
679
|
-
decodeSessionPermissionRespondCommand(encoded);
|
|
680
|
-
}
|
|
681
|
-
else if (kind === SESSION_PERMISSION_RESOLVED_KIND) {
|
|
682
|
-
this.#options.onPermissionResolved?.(decodeSessionPermissionResolvedEvent(encoded));
|
|
683
|
-
}
|
|
684
|
-
else if (kind === SESSION_ELICITATION_REQUESTED_KIND) {
|
|
685
|
-
this.#options.onElicitation?.(decodeSessionElicitationRequestedEvent(encoded));
|
|
686
|
-
}
|
|
687
|
-
else if (kind === SESSION_ELICITATION_RESPOND_KIND) {
|
|
688
|
-
decodeSessionElicitationRespondCommand(encoded);
|
|
689
|
-
}
|
|
690
|
-
else if (kind === SESSION_ELICITATION_RESOLVED_KIND) {
|
|
691
|
-
this.#options.onElicitationResolved?.(decodeSessionElicitationResolvedEvent(encoded));
|
|
692
|
-
}
|
|
693
|
-
else if (kind === SESSION_STARTED_KIND) {
|
|
694
|
-
this.#options.onLiveMessage?.(decodeSessionStartedEvent(encoded));
|
|
695
|
-
}
|
|
696
|
-
else if (kind === AGENT_MESSAGE_DELTA_KIND) {
|
|
697
|
-
this.#options.onLiveMessage?.(decodeAgentMessageDeltaEvent(encoded));
|
|
698
|
-
}
|
|
699
|
-
else if (kind === AGENT_ACTIVITY_KIND) {
|
|
700
|
-
decodeAgentActivityEvent(encoded);
|
|
701
|
-
}
|
|
702
|
-
else if (kind === AGENT_ACTIVITY_NARRATION_KIND) {
|
|
703
|
-
decodeAgentActivityNarrationEvent(encoded);
|
|
704
|
-
}
|
|
705
|
-
else if (kind === AGENT_CONTENT_KIND) {
|
|
706
|
-
this.#options.onContent?.(decodeAgentContentEvent(encoded));
|
|
707
|
-
}
|
|
708
|
-
else if (kind === AGENT_INSTALL_REQUEST_KIND) {
|
|
709
|
-
decodeAgentInstallRequestCommand(encoded);
|
|
710
|
-
}
|
|
711
|
-
else if (kind === AGENT_INSTALL_CANCEL_KIND) {
|
|
712
|
-
decodeAgentInstallCancelCommand(encoded);
|
|
713
|
-
}
|
|
714
|
-
else if (kind === AGENT_INSTALL_STARTED_KIND) {
|
|
200
|
+
if (kind === AGENT_INSTALL_STARTED_KIND) {
|
|
715
201
|
this.#options.onInstallStarted?.(decodeAgentInstallStartedEvent(encoded));
|
|
716
202
|
}
|
|
717
203
|
else if (kind === AGENT_INSTALL_PROGRESS_KIND) {
|
|
@@ -726,47 +212,8 @@ export class MobiusClient {
|
|
|
726
212
|
else if (kind === AGENT_INSTALL_CANCELLED_KIND) {
|
|
727
213
|
this.#options.onInstallCancelled?.(decodeAgentInstallCancelledEvent(encoded));
|
|
728
214
|
}
|
|
729
|
-
else if (kind === SESSION_COMPLETED_KIND) {
|
|
730
|
-
this.#options.onLiveMessage?.(decodeSessionCompletedEvent(encoded));
|
|
731
|
-
}
|
|
732
|
-
else if (kind === SESSION_FAILED_KIND) {
|
|
733
|
-
this.#options.onLiveMessage?.(decodeSessionFailedEvent(encoded));
|
|
734
|
-
}
|
|
735
|
-
else if (kind === SESSION_FORKED_KIND) {
|
|
736
|
-
decodeRecordedSessionEvent(encoded);
|
|
737
|
-
}
|
|
738
|
-
else if (kind === SESSION_OPERATION_FAILED_KIND) {
|
|
739
|
-
decodeSessionOperationFailedEvent(encoded);
|
|
740
|
-
}
|
|
741
215
|
else {
|
|
742
|
-
|
|
743
|
-
decodeServiceToDaemonMessage(encoded);
|
|
744
|
-
}
|
|
745
|
-
catch (serviceMessageError) {
|
|
746
|
-
if (!(serviceMessageError instanceof UnsupportedProtocolMessageKindError)) {
|
|
747
|
-
throw serviceMessageError;
|
|
748
|
-
}
|
|
749
|
-
try {
|
|
750
|
-
decodeDaemonToServiceMessage(encoded);
|
|
751
|
-
}
|
|
752
|
-
catch (daemonMessageError) {
|
|
753
|
-
if (!(daemonMessageError instanceof UnsupportedProtocolMessageKindError)) {
|
|
754
|
-
throw daemonMessageError;
|
|
755
|
-
}
|
|
756
|
-
console.warn("Mobius client skipped an unsupported Relay message", {
|
|
757
|
-
code: daemonMessageError.code,
|
|
758
|
-
messageKind: daemonMessageError.messageKind,
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
// Runtime control requests use short-lived sibling host sockets. The Relay mirrors those
|
|
763
|
-
// messages to this host as well, so validate and ignore control traffic that belongs to
|
|
764
|
-
// another requester instead of surfacing a protocol failure or scheduling a history sync.
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
this.#onActivity();
|
|
768
|
-
if (HISTORY_CHANGING_MESSAGE_KINDS.has(kind)) {
|
|
769
|
-
this.#onHistoryChanged();
|
|
216
|
+
decodeDaemonToServiceMessage(encoded);
|
|
770
217
|
}
|
|
771
218
|
}
|
|
772
219
|
catch (error) {
|