@letta-ai/letta-agent-sdk 0.3.2 → 0.5.0
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/AGENTS.md +44 -0
- package/README.md +29 -0
- package/dist/app-server-management.d.ts +3 -48
- package/dist/app-server-management.d.ts.map +1 -1
- package/dist/app-server-session.d.ts +0 -6
- package/dist/app-server-session.d.ts.map +1 -1
- package/dist/client-base.d.ts +7 -15
- package/dist/client-base.d.ts.map +1 -1
- package/dist/client-entry.js +979 -1010
- package/dist/client-entry.js.map +15 -11
- package/dist/client.d.ts +3 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/cloud-sandbox.d.ts +33 -0
- package/dist/cloud-sandbox.d.ts.map +1 -0
- package/dist/cloud-session.d.ts.map +1 -1
- package/dist/cloud-status-transport.d.ts +53 -0
- package/dist/cloud-status-transport.d.ts.map +1 -0
- package/dist/index.d.ts +7 -16
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3242 -4298
- package/dist/index.js.map +20 -17
- package/dist/local-app-server-session.d.ts +1 -1
- package/dist/local-app-server-session.d.ts.map +1 -1
- package/dist/local-app-server.d.ts +11 -0
- package/dist/local-app-server.d.ts.map +1 -1
- package/dist/management.d.ts +0 -7
- package/dist/management.d.ts.map +1 -1
- package/dist/remote-client-session-core.d.ts +6 -93
- package/dist/remote-client-session-core.d.ts.map +1 -1
- package/dist/remote-session-protocol.d.ts +132 -0
- package/dist/remote-session-protocol.d.ts.map +1 -0
- package/dist/remote-turn-coordinator.d.ts +49 -0
- package/dist/remote-turn-coordinator.d.ts.map +1 -0
- package/dist/types.d.ts +59 -86
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/package.json +6 -3
- package/src/app-server-management.ts +405 -0
- package/src/app-server-session.ts +916 -0
- package/src/cli-resolver.ts +46 -0
- package/src/client-base.ts +456 -0
- package/src/client-entry.ts +31 -0
- package/src/client.ts +99 -0
- package/src/cloud-management.ts +360 -0
- package/src/cloud-sandbox.ts +117 -0
- package/src/cloud-session.ts +1055 -0
- package/src/cloud-status-transport.ts +305 -0
- package/src/index.ts +422 -0
- package/src/interactiveToolPolicy.ts +62 -0
- package/src/local-app-server-session.ts +49 -0
- package/src/local-app-server.ts +231 -0
- package/src/management-types.ts +133 -0
- package/src/management.ts +199 -0
- package/src/remote-client-session-core.ts +786 -0
- package/src/remote-session-protocol.ts +674 -0
- package/src/remote-turn-coordinator.ts +523 -0
- package/src/remote.ts +177 -0
- package/src/repositories.ts +340 -0
- package/src/request-ids.ts +33 -0
- package/src/stream-events.ts +88 -0
- package/src/tool-helpers.ts +147 -0
- package/src/types.ts +1281 -0
- package/src/validation.ts +238 -0
- package/src/websocket.ts +22 -0
- package/dist/protocol.d.ts +0 -205
- package/dist/protocol.d.ts.map +0 -1
- package/dist/session.d.ts +0 -155
- package/dist/session.d.ts.map +0 -1
- package/dist/transport.d.ts +0 -53
- package/dist/transport.d.ts.map +0 -1
package/dist/client-entry.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// Generated bundle. Canonical modules are published under src/ and maintained in the letta-agent-sdk repository.
|
|
2
|
+
|
|
1
3
|
// src/repositories.ts
|
|
2
4
|
var DEFAULT_CLOUD_API_BASE_URL = "https://api.letta.com";
|
|
3
5
|
function getDefaultApiKey() {
|
|
@@ -242,6 +244,18 @@ class RepositoriesClient {
|
|
|
242
244
|
}
|
|
243
245
|
|
|
244
246
|
// node_modules/@letta-ai/letta-code/dist/app-server-client.js
|
|
247
|
+
function isAppServerInfoResponseMessage(message) {
|
|
248
|
+
if (!message || typeof message !== "object" || Array.isArray(message)) {
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
const candidate = message;
|
|
252
|
+
const capabilities = candidate.capabilities;
|
|
253
|
+
if (!capabilities || typeof capabilities !== "object" || Array.isArray(capabilities)) {
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
const capabilityRecord = capabilities;
|
|
257
|
+
return candidate.type === "app_server_info_response" && typeof candidate.request_id === "string" && candidate.request_id.length > 0 && candidate.success === true && (candidate.backend === "local" || candidate.backend === "api") && typeof candidate.letta_code_version === "string" && typeof candidate.protocol_version === "number" && Number.isInteger(candidate.protocol_version) && typeof capabilityRecord.agent_management === "boolean" && typeof capabilityRecord.conversation_management === "boolean" && typeof capabilityRecord.memory_management === "boolean" && typeof capabilityRecord.runtime_start === "boolean" && typeof capabilityRecord.split_channels === "boolean";
|
|
258
|
+
}
|
|
245
259
|
var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
246
260
|
var WEBSOCKET_OPEN_STATE = 1;
|
|
247
261
|
function getGlobalWebSocket() {
|
|
@@ -261,9 +275,9 @@ function normalizeBaseUrl(url) {
|
|
|
261
275
|
}
|
|
262
276
|
return parsed;
|
|
263
277
|
}
|
|
264
|
-
function
|
|
278
|
+
function resolveAppServerUrl(url) {
|
|
265
279
|
const parsed = normalizeBaseUrl(url);
|
|
266
|
-
parsed.searchParams.
|
|
280
|
+
parsed.searchParams.delete("channel");
|
|
267
281
|
return parsed.toString();
|
|
268
282
|
}
|
|
269
283
|
function attachSocketListener(socket, type, listener) {
|
|
@@ -376,6 +390,7 @@ function streamDeltaErrorMessage(message) {
|
|
|
376
390
|
}
|
|
377
391
|
|
|
378
392
|
class AppServerClient {
|
|
393
|
+
socket;
|
|
379
394
|
control;
|
|
380
395
|
stream;
|
|
381
396
|
requestTimeoutMs;
|
|
@@ -394,26 +409,18 @@ class AppServerClient {
|
|
|
394
409
|
}
|
|
395
410
|
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
396
411
|
const socketOptions = appServerSocketOptions(options.authToken);
|
|
397
|
-
this.
|
|
398
|
-
this.
|
|
399
|
-
|
|
412
|
+
this.socket = new WebSocket(resolveAppServerUrl(options.url), socketOptions);
|
|
413
|
+
this.control = this.socket;
|
|
414
|
+
this.stream = this.socket;
|
|
415
|
+
attachSocketListener(this.socket, "message", (event) => {
|
|
400
416
|
this.handleMessage(event, "control");
|
|
401
417
|
});
|
|
402
|
-
attachSocketListener(this.
|
|
403
|
-
this.handleMessage(event, "stream");
|
|
404
|
-
});
|
|
405
|
-
attachSocketListener(this.control, "close", (event) => {
|
|
418
|
+
attachSocketListener(this.socket, "close", (event) => {
|
|
406
419
|
this.handleDisconnect("control", event);
|
|
407
420
|
});
|
|
408
|
-
attachSocketListener(this.stream, "close", (event) => {
|
|
409
|
-
this.handleDisconnect("stream", event);
|
|
410
|
-
});
|
|
411
421
|
}
|
|
412
422
|
async connect() {
|
|
413
|
-
await
|
|
414
|
-
waitForSocketOpen(this.control),
|
|
415
|
-
waitForSocketOpen(this.stream)
|
|
416
|
-
]);
|
|
423
|
+
await waitForSocketOpen(this.socket);
|
|
417
424
|
return this;
|
|
418
425
|
}
|
|
419
426
|
close() {
|
|
@@ -421,8 +428,7 @@ class AppServerClient {
|
|
|
421
428
|
return;
|
|
422
429
|
this.explicitlyClosed = true;
|
|
423
430
|
this.rejectAllPending("App-server client closed");
|
|
424
|
-
this.
|
|
425
|
-
this.stream.close();
|
|
431
|
+
this.socket.close();
|
|
426
432
|
}
|
|
427
433
|
onMessage(handler) {
|
|
428
434
|
this.messageHandlers.add(handler);
|
|
@@ -441,10 +447,38 @@ class AppServerClient {
|
|
|
441
447
|
return `${prefix}-${this.nextRequestNumber}`;
|
|
442
448
|
}
|
|
443
449
|
send(command) {
|
|
450
|
+
this.writeCommand(command);
|
|
451
|
+
}
|
|
452
|
+
writeCommand(command) {
|
|
444
453
|
for (const handler of this.sendHandlers) {
|
|
445
454
|
handler(command);
|
|
446
455
|
}
|
|
447
|
-
this.
|
|
456
|
+
this.socket.send(JSON.stringify(command));
|
|
457
|
+
}
|
|
458
|
+
sendRaw(command) {
|
|
459
|
+
this.writeCommand(command);
|
|
460
|
+
}
|
|
461
|
+
requestRaw(command, options) {
|
|
462
|
+
const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
|
|
463
|
+
return new Promise((resolve, reject) => {
|
|
464
|
+
const timeout = setTimeout(() => {
|
|
465
|
+
this.pending.delete(command.request_id);
|
|
466
|
+
reject(new Error(`Timed out waiting for ${command.request_id}`));
|
|
467
|
+
}, timeoutMs);
|
|
468
|
+
this.pending.set(command.request_id, {
|
|
469
|
+
resolve: (message) => resolve(message),
|
|
470
|
+
reject,
|
|
471
|
+
predicate: options.predicate,
|
|
472
|
+
timeout
|
|
473
|
+
});
|
|
474
|
+
try {
|
|
475
|
+
this.sendRaw(command);
|
|
476
|
+
} catch (error) {
|
|
477
|
+
clearTimeout(timeout);
|
|
478
|
+
this.pending.delete(command.request_id);
|
|
479
|
+
reject(error instanceof Error ? error : new Error(String(error)));
|
|
480
|
+
}
|
|
481
|
+
});
|
|
448
482
|
}
|
|
449
483
|
request(commandOrType, bodyOrOptions = {}, maybeOptions = {}) {
|
|
450
484
|
const isTypeRequest = typeof commandOrType === "string";
|
|
@@ -475,6 +509,15 @@ class AppServerClient {
|
|
|
475
509
|
}
|
|
476
510
|
});
|
|
477
511
|
}
|
|
512
|
+
info(options = {}) {
|
|
513
|
+
return this.request({
|
|
514
|
+
type: "app_server_info",
|
|
515
|
+
request_id: this.nextRequestId("app-server-info")
|
|
516
|
+
}, {
|
|
517
|
+
...options,
|
|
518
|
+
predicate: isAppServerInfoResponseMessage
|
|
519
|
+
});
|
|
520
|
+
}
|
|
478
521
|
runtimeStart(command, options = {}) {
|
|
479
522
|
return this.request({
|
|
480
523
|
type: "runtime_start",
|
|
@@ -694,44 +737,6 @@ function applyUniqueRequestIds(client) {
|
|
|
694
737
|
}
|
|
695
738
|
|
|
696
739
|
// src/app-server-management.ts
|
|
697
|
-
var DEFAULT_IDLE_LINGER_MS = 250;
|
|
698
|
-
var registeredTransportsByUrl = new Map;
|
|
699
|
-
function appServerUrlKey(value) {
|
|
700
|
-
try {
|
|
701
|
-
const url = new URL(value);
|
|
702
|
-
if (url.protocol === "http:")
|
|
703
|
-
url.protocol = "ws:";
|
|
704
|
-
if (url.protocol === "https:")
|
|
705
|
-
url.protocol = "wss:";
|
|
706
|
-
url.hash = "";
|
|
707
|
-
url.searchParams.delete("channel");
|
|
708
|
-
return url.toString();
|
|
709
|
-
} catch {
|
|
710
|
-
return value;
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
function registerManagementTransport(url, transport) {
|
|
714
|
-
const key = appServerUrlKey(url);
|
|
715
|
-
const registration = { transport };
|
|
716
|
-
const transports = registeredTransportsByUrl.get(key) ?? new Set;
|
|
717
|
-
transports.add(registration);
|
|
718
|
-
registeredTransportsByUrl.set(key, transports);
|
|
719
|
-
return () => {
|
|
720
|
-
transports.delete(registration);
|
|
721
|
-
if (transports.size === 0) {
|
|
722
|
-
registeredTransportsByUrl.delete(key);
|
|
723
|
-
}
|
|
724
|
-
};
|
|
725
|
-
}
|
|
726
|
-
async function releaseAppServerManagementConnections(url) {
|
|
727
|
-
const key = appServerUrlKey(url);
|
|
728
|
-
while (true) {
|
|
729
|
-
const transports = [...registeredTransportsByUrl.get(key) ?? []];
|
|
730
|
-
if (transports.length === 0)
|
|
731
|
-
return;
|
|
732
|
-
await Promise.all(transports.map(({ transport }) => transport.releaseIdleConnection()));
|
|
733
|
-
}
|
|
734
|
-
}
|
|
735
740
|
function ensureResponse(response, value, fallback) {
|
|
736
741
|
if (!response.success || value == null) {
|
|
737
742
|
throw new Error(response.error ?? fallback);
|
|
@@ -768,15 +773,9 @@ function stringRecord(value) {
|
|
|
768
773
|
class AppServerManagementTransport {
|
|
769
774
|
options;
|
|
770
775
|
connectionPromise = null;
|
|
771
|
-
releasePromise = null;
|
|
772
776
|
closingConnections = new Set;
|
|
773
|
-
inFlightRequests = 0;
|
|
774
|
-
idleWaiters = new Set;
|
|
775
|
-
idleTimer = null;
|
|
776
|
-
idleLingerMs;
|
|
777
777
|
constructor(options) {
|
|
778
778
|
this.options = options;
|
|
779
|
-
this.idleLingerMs = options.idleLingerMs ?? DEFAULT_IDLE_LINGER_MS;
|
|
780
779
|
}
|
|
781
780
|
async listAgents(query) {
|
|
782
781
|
const response = await this.request("agent_list", { query }, "agent_list_response");
|
|
@@ -843,73 +842,20 @@ class AppServerManagementTransport {
|
|
|
843
842
|
}
|
|
844
843
|
return { messages: response.messages };
|
|
845
844
|
}
|
|
846
|
-
releaseIdleConnection() {
|
|
847
|
-
this.clearIdleTimer();
|
|
848
|
-
if (this.releasePromise)
|
|
849
|
-
return this.releasePromise;
|
|
850
|
-
const release = this.releaseConnectionWhenIdle();
|
|
851
|
-
this.releasePromise = release;
|
|
852
|
-
release.then(() => {
|
|
853
|
-
if (this.releasePromise === release) {
|
|
854
|
-
this.releasePromise = null;
|
|
855
|
-
}
|
|
856
|
-
}, () => {
|
|
857
|
-
if (this.releasePromise === release) {
|
|
858
|
-
this.releasePromise = null;
|
|
859
|
-
}
|
|
860
|
-
});
|
|
861
|
-
return release;
|
|
862
|
-
}
|
|
863
845
|
async request(type, body, responseType) {
|
|
864
|
-
if (this.releasePromise) {
|
|
865
|
-
await this.releasePromise;
|
|
866
|
-
}
|
|
867
846
|
if (this.closingConnections.size > 0) {
|
|
868
847
|
await Promise.all([...this.closingConnections]);
|
|
869
848
|
}
|
|
870
|
-
this.
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
predicate: (message) => message.type === responseType
|
|
881
|
-
});
|
|
882
|
-
return response;
|
|
883
|
-
} finally {
|
|
884
|
-
this.inFlightRequests -= 1;
|
|
885
|
-
if (this.inFlightRequests === 0) {
|
|
886
|
-
const waiters = [...this.idleWaiters];
|
|
887
|
-
this.idleWaiters.clear();
|
|
888
|
-
for (const resolve of waiters)
|
|
889
|
-
resolve();
|
|
890
|
-
if (!this.releasePromise) {
|
|
891
|
-
this.scheduleIdleRelease();
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
}
|
|
895
|
-
}
|
|
896
|
-
async releaseConnectionWhenIdle() {
|
|
897
|
-
if (this.inFlightRequests > 0) {
|
|
898
|
-
await new Promise((resolve) => {
|
|
899
|
-
this.idleWaiters.add(resolve);
|
|
900
|
-
});
|
|
901
|
-
}
|
|
902
|
-
this.clearIdleTimer();
|
|
903
|
-
const promise = this.connectionPromise;
|
|
904
|
-
try {
|
|
905
|
-
if (promise) {
|
|
906
|
-
this.connectionPromise = null;
|
|
907
|
-
await this.trackClosingConnection(await promise);
|
|
908
|
-
}
|
|
909
|
-
if (this.closingConnections.size > 0) {
|
|
910
|
-
await Promise.all([...this.closingConnections]);
|
|
911
|
-
}
|
|
912
|
-
} catch {}
|
|
849
|
+
const { client } = await this.acquireConnection();
|
|
850
|
+
const command = {
|
|
851
|
+
type,
|
|
852
|
+
request_id: client.nextRequestId(type),
|
|
853
|
+
...body
|
|
854
|
+
};
|
|
855
|
+
const response = await client.request(command, {
|
|
856
|
+
predicate: (message) => message.type === responseType
|
|
857
|
+
});
|
|
858
|
+
return response;
|
|
913
859
|
}
|
|
914
860
|
acquireConnection() {
|
|
915
861
|
if (this.connectionPromise)
|
|
@@ -934,7 +880,6 @@ class AppServerManagementTransport {
|
|
|
934
880
|
if (!url) {
|
|
935
881
|
throw new Error("App-server management requires a url or connect hook.");
|
|
936
882
|
}
|
|
937
|
-
const unregister = registerManagementTransport(url, this);
|
|
938
883
|
let client = null;
|
|
939
884
|
try {
|
|
940
885
|
client = applyUniqueRequestIds(createAppServerClient({
|
|
@@ -949,29 +894,23 @@ class AppServerManagementTransport {
|
|
|
949
894
|
} catch (error) {
|
|
950
895
|
try {
|
|
951
896
|
if (client) {
|
|
952
|
-
const controlClosed = waitForSocketClose(client.control);
|
|
953
897
|
client.close();
|
|
954
898
|
ownedConnection?.close();
|
|
955
|
-
await controlClosed;
|
|
956
899
|
} else {
|
|
957
900
|
ownedConnection?.close();
|
|
958
901
|
}
|
|
959
|
-
} catch {}
|
|
960
|
-
unregister();
|
|
961
|
-
}
|
|
902
|
+
} catch {}
|
|
962
903
|
throw error;
|
|
963
904
|
}
|
|
964
905
|
return {
|
|
965
906
|
client,
|
|
966
907
|
ownedConnection,
|
|
967
|
-
detachDisconnect: () => {}
|
|
968
|
-
unregister
|
|
908
|
+
detachDisconnect: () => {}
|
|
969
909
|
};
|
|
970
910
|
}
|
|
971
911
|
discardConnection(promise, connection) {
|
|
972
912
|
if (this.connectionPromise === promise) {
|
|
973
913
|
this.connectionPromise = null;
|
|
974
|
-
this.clearIdleTimer();
|
|
975
914
|
}
|
|
976
915
|
this.trackClosingConnection(connection);
|
|
977
916
|
}
|
|
@@ -981,67 +920,11 @@ class AppServerManagementTransport {
|
|
|
981
920
|
closing.then(() => this.closingConnections.delete(closing), () => this.closingConnections.delete(closing));
|
|
982
921
|
return closing;
|
|
983
922
|
}
|
|
984
|
-
scheduleIdleRelease() {
|
|
985
|
-
if (!this.connectionPromise)
|
|
986
|
-
return;
|
|
987
|
-
this.clearIdleTimer();
|
|
988
|
-
const timer = setTimeout(() => {
|
|
989
|
-
this.idleTimer = null;
|
|
990
|
-
this.releaseIdleConnection();
|
|
991
|
-
}, this.idleLingerMs);
|
|
992
|
-
this.idleTimer = timer;
|
|
993
|
-
timer.unref?.();
|
|
994
|
-
}
|
|
995
|
-
clearIdleTimer() {
|
|
996
|
-
if (this.idleTimer === null)
|
|
997
|
-
return;
|
|
998
|
-
clearTimeout(this.idleTimer);
|
|
999
|
-
this.idleTimer = null;
|
|
1000
|
-
}
|
|
1001
923
|
}
|
|
1002
924
|
async function closeConnection(connection) {
|
|
1003
925
|
connection.detachDisconnect();
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
connection.client.close();
|
|
1007
|
-
connection.ownedConnection?.close();
|
|
1008
|
-
await controlClosed;
|
|
1009
|
-
} finally {
|
|
1010
|
-
connection.unregister();
|
|
1011
|
-
}
|
|
1012
|
-
}
|
|
1013
|
-
function waitForSocketClose(socket) {
|
|
1014
|
-
if (socket.readyState === 3)
|
|
1015
|
-
return Promise.resolve();
|
|
1016
|
-
return new Promise((resolve) => {
|
|
1017
|
-
let settled = false;
|
|
1018
|
-
let detach = () => {};
|
|
1019
|
-
const finish = () => {
|
|
1020
|
-
if (settled)
|
|
1021
|
-
return;
|
|
1022
|
-
settled = true;
|
|
1023
|
-
clearTimeout(timeout);
|
|
1024
|
-
detach();
|
|
1025
|
-
resolve();
|
|
1026
|
-
};
|
|
1027
|
-
if (socket.addEventListener) {
|
|
1028
|
-
socket.addEventListener("close", finish);
|
|
1029
|
-
detach = () => socket.removeEventListener?.("close", finish);
|
|
1030
|
-
} else if (socket.once) {
|
|
1031
|
-
socket.once("close", finish);
|
|
1032
|
-
detach = () => socket.off?.("close", finish);
|
|
1033
|
-
} else if (socket.on) {
|
|
1034
|
-
socket.on("close", finish);
|
|
1035
|
-
detach = () => socket.off?.("close", finish);
|
|
1036
|
-
} else {
|
|
1037
|
-
resolve();
|
|
1038
|
-
return;
|
|
1039
|
-
}
|
|
1040
|
-
const timeout = setTimeout(finish, 1000);
|
|
1041
|
-
timeout.unref?.();
|
|
1042
|
-
if (socket.readyState === 3)
|
|
1043
|
-
finish();
|
|
1044
|
-
});
|
|
926
|
+
connection.client.close();
|
|
927
|
+
connection.ownedConnection?.close();
|
|
1045
928
|
}
|
|
1046
929
|
|
|
1047
930
|
// node_modules/@letta-ai/letta-code/dist/agent-presets.js
|
|
@@ -5711,7 +5594,7 @@ function buildCanUseToolContext(request, requestId) {
|
|
|
5711
5594
|
return context;
|
|
5712
5595
|
}
|
|
5713
5596
|
|
|
5714
|
-
// src/remote-
|
|
5597
|
+
// src/remote-session-protocol.ts
|
|
5715
5598
|
var FAILURE_STOP_REASONS = new Set([
|
|
5716
5599
|
"error",
|
|
5717
5600
|
"llm_api_error",
|
|
@@ -5948,6 +5831,12 @@ function streamDeltaMessageType2(delta) {
|
|
|
5948
5831
|
function streamDeltaRunId2(delta) {
|
|
5949
5832
|
return typeof delta.run_id === "string" ? delta.run_id : undefined;
|
|
5950
5833
|
}
|
|
5834
|
+
function streamDeltaOtid(delta) {
|
|
5835
|
+
return typeof delta.otid === "string" || delta.otid === null ? delta.otid : undefined;
|
|
5836
|
+
}
|
|
5837
|
+
function streamDeltaSeqId(delta) {
|
|
5838
|
+
return typeof delta.seq_id === "number" ? delta.seq_id : undefined;
|
|
5839
|
+
}
|
|
5951
5840
|
function streamDeltaStopReason2(delta) {
|
|
5952
5841
|
return typeof delta.stop_reason === "string" ? delta.stop_reason : undefined;
|
|
5953
5842
|
}
|
|
@@ -6106,168 +5995,572 @@ function normalizeSendMessage(message) {
|
|
|
6106
5995
|
return message;
|
|
6107
5996
|
}
|
|
6108
5997
|
|
|
6109
|
-
|
|
6110
|
-
|
|
6111
|
-
controller = null;
|
|
6112
|
-
runtime = null;
|
|
6113
|
-
initialized = false;
|
|
6114
|
-
closed = false;
|
|
6115
|
-
activeTurnStartedAt = 0;
|
|
6116
|
-
_agentId = null;
|
|
6117
|
-
_sessionId = null;
|
|
6118
|
-
_conversationId = null;
|
|
6119
|
-
_model = "";
|
|
6120
|
-
_modelSettings = null;
|
|
5998
|
+
// src/remote-turn-coordinator.ts
|
|
5999
|
+
class RemoteTurnCoordinator {
|
|
6121
6000
|
label;
|
|
6122
6001
|
requestTimeoutMs;
|
|
6123
|
-
|
|
6002
|
+
onDeviceStatus;
|
|
6124
6003
|
streamQueue = [];
|
|
6125
6004
|
streamResolvers = [];
|
|
6126
|
-
removeMessageHandler = null;
|
|
6127
6005
|
activeTurn = null;
|
|
6128
6006
|
pendingTurns = [];
|
|
6129
6007
|
nextTurnId = 0;
|
|
6130
6008
|
messageCounter = 0;
|
|
6131
6009
|
clientMessageCounter = 0;
|
|
6132
|
-
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
constructor(mode, config) {
|
|
6136
|
-
this.mode = mode;
|
|
6010
|
+
closed = false;
|
|
6011
|
+
_activeTurnStartedAt = 0;
|
|
6012
|
+
constructor(config) {
|
|
6137
6013
|
this.label = config.label;
|
|
6138
6014
|
this.requestTimeoutMs = config.requestTimeoutMs;
|
|
6015
|
+
this.onDeviceStatus = config.onDeviceStatus;
|
|
6139
6016
|
}
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
throw new Error("Session is closed");
|
|
6143
|
-
}
|
|
6144
|
-
if (this.initializePromise) {
|
|
6145
|
-
return this.initializePromise;
|
|
6146
|
-
}
|
|
6147
|
-
if (this.initialized) {
|
|
6148
|
-
throw new Error("Session already initialized");
|
|
6149
|
-
}
|
|
6150
|
-
const attempt = this.performInitialize();
|
|
6151
|
-
const memo = attempt.catch((error) => {
|
|
6152
|
-
this.cleanupFailedInitialize();
|
|
6153
|
-
throw error;
|
|
6154
|
-
}).finally(() => {
|
|
6155
|
-
if (this.initializePromise === memo) {
|
|
6156
|
-
this.initializePromise = null;
|
|
6157
|
-
}
|
|
6158
|
-
});
|
|
6159
|
-
this.initializePromise = memo;
|
|
6160
|
-
return memo;
|
|
6161
|
-
}
|
|
6162
|
-
async performInitialize() {
|
|
6163
|
-
const init = await this.initializeRuntimeController();
|
|
6164
|
-
this.controller = init.controller;
|
|
6165
|
-
this.runtime = init.runtime;
|
|
6166
|
-
this._agentId = init.runtime.agent_id;
|
|
6167
|
-
this._conversationId = init.runtime.conversation_id;
|
|
6168
|
-
this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}`;
|
|
6169
|
-
this._modelSettings = init.modelSettings ?? null;
|
|
6170
|
-
this._model = typeof init.model === "string" ? init.model : typeof this._modelSettings?.model === "string" ? this._modelSettings.model : "";
|
|
6171
|
-
this.toolNames = init.tools;
|
|
6172
|
-
this.removeMessageHandler = this.controller.onMessage(this.handleProtocolMessage);
|
|
6173
|
-
await this.afterRuntimeInitialized();
|
|
6174
|
-
await this.applyPostInitializeOptions();
|
|
6175
|
-
if (this.closed) {
|
|
6176
|
-
throw new Error("Session is closed");
|
|
6177
|
-
}
|
|
6178
|
-
this.initialized = true;
|
|
6179
|
-
const initMessage = {
|
|
6180
|
-
type: "init",
|
|
6181
|
-
agentId: init.runtime.agent_id,
|
|
6182
|
-
sessionId: this._sessionId,
|
|
6183
|
-
conversationId: init.runtime.conversation_id,
|
|
6184
|
-
model: this._model
|
|
6185
|
-
};
|
|
6186
|
-
if (this.toolNames !== undefined)
|
|
6187
|
-
initMessage.tools = this.toolNames;
|
|
6188
|
-
if (init.skillSources !== undefined) {
|
|
6189
|
-
initMessage.skillSources = init.skillSources;
|
|
6190
|
-
}
|
|
6191
|
-
return initMessage;
|
|
6192
|
-
}
|
|
6193
|
-
cleanupFailedInitialize() {
|
|
6194
|
-
this.removeMessageHandler?.();
|
|
6195
|
-
this.removeMessageHandler = null;
|
|
6196
|
-
this.controller?.close();
|
|
6197
|
-
this.controller = null;
|
|
6198
|
-
this.onCoreClose();
|
|
6199
|
-
this.runtime = null;
|
|
6200
|
-
this._agentId = null;
|
|
6201
|
-
this._conversationId = null;
|
|
6202
|
-
this._sessionId = null;
|
|
6203
|
-
this._modelSettings = null;
|
|
6204
|
-
this._model = "";
|
|
6205
|
-
this.toolNames = undefined;
|
|
6206
|
-
this.initialized = false;
|
|
6017
|
+
get activeTurnStartedAt() {
|
|
6018
|
+
return this._activeTurnStartedAt;
|
|
6207
6019
|
}
|
|
6208
|
-
|
|
6209
|
-
|
|
6210
|
-
await this.initialize();
|
|
6211
|
-
}
|
|
6212
|
-
if (!this.controller || !this.runtime) {
|
|
6213
|
-
throw new Error("Session is not initialized");
|
|
6214
|
-
}
|
|
6215
|
-
await this.beforeTurn();
|
|
6216
|
-
const turn = this.trackSentTurn(this.runtime);
|
|
6217
|
-
try {
|
|
6218
|
-
this.controller.sendTurnMessage(this.runtime, message, {
|
|
6219
|
-
clientMessageId: turn.clientMessageId
|
|
6220
|
-
});
|
|
6221
|
-
} catch (error) {
|
|
6222
|
-
this.removeTrackedTurn(turn);
|
|
6223
|
-
throw error;
|
|
6224
|
-
}
|
|
6020
|
+
hasInFlightTurn() {
|
|
6021
|
+
return this.activeTurn !== null || this.pendingTurns.length > 0;
|
|
6225
6022
|
}
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
|
|
6231
|
-
|
|
6232
|
-
|
|
6233
|
-
|
|
6234
|
-
|
|
6235
|
-
|
|
6236
|
-
|
|
6237
|
-
|
|
6238
|
-
|
|
6239
|
-
error: "stream_closed",
|
|
6240
|
-
errorCode: "stream_closed",
|
|
6241
|
-
recoverable: false,
|
|
6242
|
-
errorDetail: "Stream ended before terminal result",
|
|
6243
|
-
durationMs: Date.now() - this.activeTurnStartedAt,
|
|
6244
|
-
conversationId: this._conversationId
|
|
6023
|
+
trackSentTurn(runtime) {
|
|
6024
|
+
const turn = {
|
|
6025
|
+
id: ++this.nextTurnId,
|
|
6026
|
+
runtime,
|
|
6027
|
+
clientMessageId: `sdk-message-${Date.now()}-${++this.clientMessageCounter}`,
|
|
6028
|
+
queuedAt: Date.now(),
|
|
6029
|
+
startedAt: 0,
|
|
6030
|
+
assistantText: "",
|
|
6031
|
+
runIds: new Set,
|
|
6032
|
+
observedTurnEvidence: false,
|
|
6033
|
+
observedRequiresApprovalStop: false,
|
|
6034
|
+
abortRequested: false,
|
|
6035
|
+
timeout: null
|
|
6245
6036
|
};
|
|
6246
|
-
|
|
6247
|
-
|
|
6248
|
-
|
|
6249
|
-
|
|
6250
|
-
if (!msg)
|
|
6251
|
-
break;
|
|
6252
|
-
yield msg;
|
|
6253
|
-
if (msg.type === "result")
|
|
6254
|
-
break;
|
|
6037
|
+
if (this.activeTurn) {
|
|
6038
|
+
this.pendingTurns.push(turn);
|
|
6039
|
+
} else {
|
|
6040
|
+
this.activateTurn(turn);
|
|
6255
6041
|
}
|
|
6042
|
+
return turn;
|
|
6256
6043
|
}
|
|
6257
|
-
|
|
6258
|
-
if (
|
|
6259
|
-
|
|
6260
|
-
if (
|
|
6044
|
+
removeTrackedTurn(turn) {
|
|
6045
|
+
if (turn.timeout)
|
|
6046
|
+
clearTimeout(turn.timeout);
|
|
6047
|
+
if (this.activeTurn === turn) {
|
|
6048
|
+
this.activeTurn = null;
|
|
6261
6049
|
return;
|
|
6050
|
+
}
|
|
6051
|
+
const index = this.pendingTurns.indexOf(turn);
|
|
6052
|
+
if (index !== -1)
|
|
6053
|
+
this.pendingTurns.splice(index, 1);
|
|
6054
|
+
}
|
|
6055
|
+
markAbortRequested() {
|
|
6262
6056
|
if (this.activeTurn)
|
|
6263
6057
|
this.activeTurn.abortRequested = true;
|
|
6264
|
-
await this.controller.abort(this.runtime);
|
|
6265
6058
|
}
|
|
6266
|
-
|
|
6267
|
-
if (!
|
|
6268
|
-
|
|
6059
|
+
handleProtocolMessage(message, runtime) {
|
|
6060
|
+
if (!sameRuntime2(message, runtime))
|
|
6061
|
+
return;
|
|
6062
|
+
const statusRecord = deviceStatusRecord(message);
|
|
6063
|
+
if (statusRecord) {
|
|
6064
|
+
const status = toSessionDeviceStatus(statusRecord);
|
|
6065
|
+
if (status)
|
|
6066
|
+
this.onDeviceStatus(status);
|
|
6067
|
+
return;
|
|
6269
6068
|
}
|
|
6270
|
-
if (
|
|
6069
|
+
if (message.type === "update_queue") {
|
|
6070
|
+
const sdkMessage2 = {
|
|
6071
|
+
type: "queue_update",
|
|
6072
|
+
queue: queueItems(message)
|
|
6073
|
+
};
|
|
6074
|
+
this.enqueue(sdkMessage2);
|
|
6075
|
+
return;
|
|
6076
|
+
}
|
|
6077
|
+
if (message.type === "update_loop_status") {
|
|
6078
|
+
this.handleLoopStatusMessage(message);
|
|
6079
|
+
return;
|
|
6080
|
+
}
|
|
6081
|
+
const delta = streamDeltaRecord(message);
|
|
6082
|
+
if (!delta)
|
|
6083
|
+
return;
|
|
6084
|
+
const active = this.activateNextTurnFromProtocol();
|
|
6085
|
+
if (active) {
|
|
6086
|
+
active.observedTurnEvidence = true;
|
|
6087
|
+
const runId = streamDeltaRunId2(delta);
|
|
6088
|
+
if (runId)
|
|
6089
|
+
active.runIds.add(runId);
|
|
6090
|
+
}
|
|
6091
|
+
const sdkMessage = this.transformStreamDelta(delta);
|
|
6092
|
+
if (sdkMessage)
|
|
6093
|
+
this.enqueue(sdkMessage);
|
|
6094
|
+
this.handleTurnTerminalDelta(delta, sdkMessage);
|
|
6095
|
+
}
|
|
6096
|
+
nextMessage() {
|
|
6097
|
+
const next = this.streamQueue.shift();
|
|
6098
|
+
if (next)
|
|
6099
|
+
return Promise.resolve(next);
|
|
6100
|
+
if (this.closed)
|
|
6101
|
+
return Promise.resolve(null);
|
|
6102
|
+
return new Promise((resolve) => {
|
|
6103
|
+
this.streamResolvers.push(resolve);
|
|
6104
|
+
});
|
|
6105
|
+
}
|
|
6106
|
+
close() {
|
|
6107
|
+
if (this.closed)
|
|
6108
|
+
return;
|
|
6109
|
+
this.closed = true;
|
|
6110
|
+
if (this.activeTurn?.timeout)
|
|
6111
|
+
clearTimeout(this.activeTurn.timeout);
|
|
6112
|
+
for (const turn of this.pendingTurns) {
|
|
6113
|
+
if (turn.timeout)
|
|
6114
|
+
clearTimeout(turn.timeout);
|
|
6115
|
+
}
|
|
6116
|
+
this.activeTurn = null;
|
|
6117
|
+
this.pendingTurns.length = 0;
|
|
6118
|
+
this.resolveAll(null);
|
|
6119
|
+
}
|
|
6120
|
+
activateTurn(turn) {
|
|
6121
|
+
this.activeTurn = turn;
|
|
6122
|
+
turn.startedAt = Date.now();
|
|
6123
|
+
this._activeTurnStartedAt = turn.startedAt;
|
|
6124
|
+
if (this.requestTimeoutMs !== undefined) {
|
|
6125
|
+
turn.timeout = setTimeout(() => {
|
|
6126
|
+
this.failTurn(turn, `Timed out waiting for ${this.label} turn`);
|
|
6127
|
+
}, this.requestTimeoutMs);
|
|
6128
|
+
turn.timeout.unref?.();
|
|
6129
|
+
}
|
|
6130
|
+
}
|
|
6131
|
+
activateNextTurnFromProtocol() {
|
|
6132
|
+
if (this.activeTurn)
|
|
6133
|
+
return this.activeTurn;
|
|
6134
|
+
const next = this.pendingTurns.shift();
|
|
6135
|
+
if (!next)
|
|
6136
|
+
return null;
|
|
6137
|
+
this.activateTurn(next);
|
|
6138
|
+
return next;
|
|
6139
|
+
}
|
|
6140
|
+
failTurn(turn, detail) {
|
|
6141
|
+
if (this.activeTurn !== turn)
|
|
6142
|
+
return;
|
|
6143
|
+
this.enqueue({
|
|
6144
|
+
type: "error",
|
|
6145
|
+
message: detail,
|
|
6146
|
+
errorCode: "error",
|
|
6147
|
+
stopReason: "error",
|
|
6148
|
+
errorDetail: detail,
|
|
6149
|
+
recoverable: false
|
|
6150
|
+
});
|
|
6151
|
+
this.completeActiveTurn({
|
|
6152
|
+
runtime: turn.runtime,
|
|
6153
|
+
stopReason: "error",
|
|
6154
|
+
runIds: [...turn.runIds],
|
|
6155
|
+
success: false,
|
|
6156
|
+
detail,
|
|
6157
|
+
errorCode: "error"
|
|
6158
|
+
});
|
|
6159
|
+
}
|
|
6160
|
+
completeActiveTurn(turn) {
|
|
6161
|
+
const active = this.activeTurn;
|
|
6162
|
+
if (!active)
|
|
6163
|
+
return;
|
|
6164
|
+
if (active.timeout) {
|
|
6165
|
+
clearTimeout(active.timeout);
|
|
6166
|
+
active.timeout = null;
|
|
6167
|
+
}
|
|
6168
|
+
this.enqueue(this.resultFromTurn(turn, active));
|
|
6169
|
+
this.activeTurn = null;
|
|
6170
|
+
}
|
|
6171
|
+
handleLoopStatusMessage(message) {
|
|
6172
|
+
const status = loopStatusValue(message);
|
|
6173
|
+
if (!status)
|
|
6174
|
+
return;
|
|
6175
|
+
const activeRunIds = loopStatusRunIds(message);
|
|
6176
|
+
const sdkMessage = {
|
|
6177
|
+
type: "loop_status",
|
|
6178
|
+
status,
|
|
6179
|
+
activeRunIds
|
|
6180
|
+
};
|
|
6181
|
+
this.enqueue(sdkMessage);
|
|
6182
|
+
const active = this.activeTurn;
|
|
6183
|
+
if (!active)
|
|
6184
|
+
return;
|
|
6185
|
+
for (const runId of activeRunIds)
|
|
6186
|
+
active.runIds.add(runId);
|
|
6187
|
+
const hadTurnEvidence = active.observedTurnEvidence || active.observedRequiresApprovalStop;
|
|
6188
|
+
if (!hadTurnEvidence)
|
|
6189
|
+
return;
|
|
6190
|
+
if (status === "WAITING_ON_APPROVAL") {
|
|
6191
|
+
this.completeActiveTurn({
|
|
6192
|
+
runtime: active.runtime,
|
|
6193
|
+
stopReason: "requires_approval",
|
|
6194
|
+
runIds: [...active.runIds]
|
|
6195
|
+
});
|
|
6196
|
+
return;
|
|
6197
|
+
}
|
|
6198
|
+
if (status === "WAITING_ON_INPUT" && active.abortRequested) {
|
|
6199
|
+
this.completeActiveTurn({
|
|
6200
|
+
runtime: active.runtime,
|
|
6201
|
+
stopReason: "interrupted",
|
|
6202
|
+
runIds: [...active.runIds],
|
|
6203
|
+
success: false,
|
|
6204
|
+
detail: "Interrupted",
|
|
6205
|
+
errorCode: "interrupted"
|
|
6206
|
+
});
|
|
6207
|
+
return;
|
|
6208
|
+
}
|
|
6209
|
+
if (status === "WAITING_ON_INPUT" && active.observedTurnEvidence) {
|
|
6210
|
+
this.completeActiveTurn({
|
|
6211
|
+
runtime: active.runtime,
|
|
6212
|
+
stopReason: null,
|
|
6213
|
+
runIds: [...active.runIds]
|
|
6214
|
+
});
|
|
6215
|
+
}
|
|
6216
|
+
}
|
|
6217
|
+
handleTurnTerminalDelta(delta, sdkMessage) {
|
|
6218
|
+
const active = this.activeTurn;
|
|
6219
|
+
if (!active)
|
|
6220
|
+
return;
|
|
6221
|
+
const messageType = streamDeltaMessageType2(delta);
|
|
6222
|
+
if (messageType === "stop_reason") {
|
|
6223
|
+
const stopReason = streamDeltaStopReason2(delta) ?? null;
|
|
6224
|
+
if (stopReason === "requires_approval") {
|
|
6225
|
+
active.observedRequiresApprovalStop = true;
|
|
6226
|
+
return;
|
|
6227
|
+
}
|
|
6228
|
+
this.completeActiveTurn({
|
|
6229
|
+
runtime: active.runtime,
|
|
6230
|
+
stopReason,
|
|
6231
|
+
runIds: [...active.runIds]
|
|
6232
|
+
});
|
|
6233
|
+
return;
|
|
6234
|
+
}
|
|
6235
|
+
if (sdkMessage?.type === "error") {
|
|
6236
|
+
this.completeActiveTurn({
|
|
6237
|
+
runtime: active.runtime,
|
|
6238
|
+
stopReason: sdkMessage.stopReason,
|
|
6239
|
+
runIds: [...active.runIds],
|
|
6240
|
+
success: false,
|
|
6241
|
+
detail: sdkMessage.errorDetail ?? sdkMessage.message,
|
|
6242
|
+
errorCode: sdkMessage.errorCode
|
|
6243
|
+
});
|
|
6244
|
+
}
|
|
6245
|
+
}
|
|
6246
|
+
transformStreamDelta(delta) {
|
|
6247
|
+
const messageType = typeof delta.message_type === "string" ? delta.message_type : undefined;
|
|
6248
|
+
const runId = typeof delta.run_id === "string" ? delta.run_id : undefined;
|
|
6249
|
+
const otid = streamDeltaOtid(delta);
|
|
6250
|
+
const seqId = streamDeltaSeqId(delta);
|
|
6251
|
+
const uuid = typeof delta.id === "string" ? delta.id : `${this.label}-${++this.messageCounter}`;
|
|
6252
|
+
if (messageType === "assistant_message") {
|
|
6253
|
+
const content = extractTextFromContent(delta.content);
|
|
6254
|
+
if (!content)
|
|
6255
|
+
return null;
|
|
6256
|
+
if (this.activeTurn)
|
|
6257
|
+
this.activeTurn.assistantText += content;
|
|
6258
|
+
return {
|
|
6259
|
+
type: "assistant",
|
|
6260
|
+
content,
|
|
6261
|
+
uuid,
|
|
6262
|
+
...otid !== undefined ? { otid } : {},
|
|
6263
|
+
...seqId !== undefined ? { seqId } : {},
|
|
6264
|
+
runId
|
|
6265
|
+
};
|
|
6266
|
+
}
|
|
6267
|
+
if (messageType === "reasoning_message") {
|
|
6268
|
+
const content = typeof delta.reasoning === "string" ? delta.reasoning : extractTextFromContent(delta.content);
|
|
6269
|
+
if (!content)
|
|
6270
|
+
return null;
|
|
6271
|
+
return {
|
|
6272
|
+
type: "reasoning",
|
|
6273
|
+
content,
|
|
6274
|
+
uuid,
|
|
6275
|
+
...otid !== undefined ? { otid } : {},
|
|
6276
|
+
...seqId !== undefined ? { seqId } : {},
|
|
6277
|
+
runId
|
|
6278
|
+
};
|
|
6279
|
+
}
|
|
6280
|
+
if (messageType === "tool_call_message" || messageType === "approval_request_message") {
|
|
6281
|
+
const toolCall = firstToolCall(delta);
|
|
6282
|
+
if (!toolCall)
|
|
6283
|
+
return null;
|
|
6284
|
+
const fn = toolCall.function && typeof toolCall.function === "object" ? toolCall.function : undefined;
|
|
6285
|
+
const toolCallId = (typeof toolCall.tool_call_id === "string" ? toolCall.tool_call_id : undefined) ?? (typeof toolCall.id === "string" ? toolCall.id : undefined);
|
|
6286
|
+
if (!toolCallId) {
|
|
6287
|
+
const detail = `Missing tool_call_id in ${messageType} (uuid=${uuid})`;
|
|
6288
|
+
return {
|
|
6289
|
+
type: "error",
|
|
6290
|
+
message: detail,
|
|
6291
|
+
errorCode: "protocol_error",
|
|
6292
|
+
stopReason: "protocol_error",
|
|
6293
|
+
runId,
|
|
6294
|
+
recoverable: false,
|
|
6295
|
+
errorDetail: detail
|
|
6296
|
+
};
|
|
6297
|
+
}
|
|
6298
|
+
const toolName = (typeof toolCall.name === "string" ? toolCall.name : undefined) ?? (typeof fn?.name === "string" ? fn.name : undefined) ?? "?";
|
|
6299
|
+
const { input, raw } = toolInputFromArguments(toolCall.arguments ?? fn?.arguments);
|
|
6300
|
+
return {
|
|
6301
|
+
type: "tool_call",
|
|
6302
|
+
toolCallId,
|
|
6303
|
+
toolName,
|
|
6304
|
+
toolInput: input,
|
|
6305
|
+
rawArguments: raw,
|
|
6306
|
+
uuid,
|
|
6307
|
+
runId
|
|
6308
|
+
};
|
|
6309
|
+
}
|
|
6310
|
+
if (messageType === "tool_return_message") {
|
|
6311
|
+
const toolReturn = firstToolReturn(delta) ?? delta;
|
|
6312
|
+
const toolCallId = (typeof delta.tool_call_id === "string" ? delta.tool_call_id : undefined) ?? (typeof toolReturn.tool_call_id === "string" ? toolReturn.tool_call_id : undefined);
|
|
6313
|
+
if (!toolCallId)
|
|
6314
|
+
return null;
|
|
6315
|
+
const content = extractTextFromContent(delta.tool_return ?? toolReturn.tool_return ?? toolReturn.content) ?? "";
|
|
6316
|
+
const status = typeof delta.status === "string" ? delta.status : toolReturn.status;
|
|
6317
|
+
return {
|
|
6318
|
+
type: "tool_result",
|
|
6319
|
+
toolCallId,
|
|
6320
|
+
content,
|
|
6321
|
+
isError: status === "error",
|
|
6322
|
+
uuid,
|
|
6323
|
+
runId
|
|
6324
|
+
};
|
|
6325
|
+
}
|
|
6326
|
+
if (messageType === "error_message" || messageType === "loop_error") {
|
|
6327
|
+
const detail = (typeof delta.detail === "string" ? delta.detail : undefined) ?? (typeof delta.message === "string" ? delta.message : undefined) ?? `${this.label} turn failed`;
|
|
6328
|
+
const stopReason = (typeof delta.stop_reason === "string" ? delta.stop_reason : undefined) ?? (typeof delta.error_type === "string" ? delta.error_type : undefined) ?? "error";
|
|
6329
|
+
const approvalConflict = isApprovalConflictSignal({
|
|
6330
|
+
detail,
|
|
6331
|
+
message: typeof delta.message === "string" ? delta.message : undefined,
|
|
6332
|
+
stopReason
|
|
6333
|
+
});
|
|
6334
|
+
return {
|
|
6335
|
+
type: "error",
|
|
6336
|
+
message: detail,
|
|
6337
|
+
errorCode: approvalConflict ? "approval_conflict" : toSdkErrorCode(stopReason),
|
|
6338
|
+
approvalConflict: approvalConflict || undefined,
|
|
6339
|
+
recoverable: approvalConflict ? true : false,
|
|
6340
|
+
errorDetail: detail,
|
|
6341
|
+
stopReason,
|
|
6342
|
+
runId
|
|
6343
|
+
};
|
|
6344
|
+
}
|
|
6345
|
+
if (messageType === "retry") {
|
|
6346
|
+
return {
|
|
6347
|
+
type: "retry",
|
|
6348
|
+
reason: typeof delta.reason === "string" ? delta.reason : "error",
|
|
6349
|
+
attempt: typeof delta.attempt === "number" ? delta.attempt : 0,
|
|
6350
|
+
maxAttempts: typeof delta.max_attempts === "number" ? delta.max_attempts : 0,
|
|
6351
|
+
delayMs: typeof delta.delay_ms === "number" ? delta.delay_ms : 0,
|
|
6352
|
+
runId
|
|
6353
|
+
};
|
|
6354
|
+
}
|
|
6355
|
+
if (messageType === "stop_reason" || messageType === "ping") {
|
|
6356
|
+
return null;
|
|
6357
|
+
}
|
|
6358
|
+
return {
|
|
6359
|
+
type: "stream_event",
|
|
6360
|
+
event: delta,
|
|
6361
|
+
uuid
|
|
6362
|
+
};
|
|
6363
|
+
}
|
|
6364
|
+
resultFromTurn(turn, tracker) {
|
|
6365
|
+
const stopReason = turn.stopReason ?? (turn.success === false ? "error" : undefined);
|
|
6366
|
+
const approvalConflict = isApprovalConflictSignal({
|
|
6367
|
+
detail: turn.detail,
|
|
6368
|
+
stopReason
|
|
6369
|
+
});
|
|
6370
|
+
const success = turn.success !== undefined ? turn.success && !approvalConflict && !FAILURE_STOP_REASONS.has(stopReason ?? "") : !approvalConflict && !FAILURE_STOP_REASONS.has(stopReason ?? "");
|
|
6371
|
+
const errorCode = approvalConflict ? "approval_conflict" : turn.errorCode ?? toSdkErrorCode(stopReason);
|
|
6372
|
+
return {
|
|
6373
|
+
type: "result",
|
|
6374
|
+
success,
|
|
6375
|
+
result: success ? tracker?.assistantText || undefined : undefined,
|
|
6376
|
+
error: success ? undefined : errorCode ?? stopReason ?? "error",
|
|
6377
|
+
errorCode: success ? undefined : errorCode ?? "error",
|
|
6378
|
+
approvalConflict: approvalConflict || undefined,
|
|
6379
|
+
recoverable: approvalConflict ? true : success ? undefined : false,
|
|
6380
|
+
errorDetail: success ? undefined : turn.detail,
|
|
6381
|
+
stopReason,
|
|
6382
|
+
durationMs: Date.now() - (tracker?.startedAt || this._activeTurnStartedAt),
|
|
6383
|
+
conversationId: turn.runtime.conversation_id,
|
|
6384
|
+
runIds: turn.runIds.length > 0 ? turn.runIds : undefined
|
|
6385
|
+
};
|
|
6386
|
+
}
|
|
6387
|
+
enqueue(message) {
|
|
6388
|
+
const resolver = this.streamResolvers.shift();
|
|
6389
|
+
if (resolver) {
|
|
6390
|
+
resolver(message);
|
|
6391
|
+
return;
|
|
6392
|
+
}
|
|
6393
|
+
this.streamQueue.push(message);
|
|
6394
|
+
}
|
|
6395
|
+
resolveAll(value) {
|
|
6396
|
+
for (const resolve of this.streamResolvers.splice(0)) {
|
|
6397
|
+
resolve(value);
|
|
6398
|
+
}
|
|
6399
|
+
}
|
|
6400
|
+
}
|
|
6401
|
+
// src/remote-client-session-core.ts
|
|
6402
|
+
class RemoteClientSessionCore {
|
|
6403
|
+
mode;
|
|
6404
|
+
controller = null;
|
|
6405
|
+
runtime = null;
|
|
6406
|
+
initialized = false;
|
|
6407
|
+
closed = false;
|
|
6408
|
+
_agentId = null;
|
|
6409
|
+
_sessionId = null;
|
|
6410
|
+
_conversationId = null;
|
|
6411
|
+
_model = "";
|
|
6412
|
+
_modelSettings = null;
|
|
6413
|
+
label;
|
|
6414
|
+
requestTimeoutMs;
|
|
6415
|
+
initializePromise = null;
|
|
6416
|
+
removeMessageHandler = null;
|
|
6417
|
+
turns;
|
|
6418
|
+
toolNames;
|
|
6419
|
+
deviceStatusListeners = new Set;
|
|
6420
|
+
deviceStatusRefreshCancels = new Set;
|
|
6421
|
+
constructor(mode, config) {
|
|
6422
|
+
this.mode = mode;
|
|
6423
|
+
this.label = config.label;
|
|
6424
|
+
this.requestTimeoutMs = config.requestTimeoutMs;
|
|
6425
|
+
this.turns = new RemoteTurnCoordinator({
|
|
6426
|
+
label: config.label,
|
|
6427
|
+
requestTimeoutMs: config.requestTimeoutMs,
|
|
6428
|
+
onDeviceStatus: (status) => this.emitDeviceStatus(status)
|
|
6429
|
+
});
|
|
6430
|
+
}
|
|
6431
|
+
async initialize() {
|
|
6432
|
+
if (this.closed) {
|
|
6433
|
+
throw new Error("Session is closed");
|
|
6434
|
+
}
|
|
6435
|
+
if (this.initializePromise) {
|
|
6436
|
+
return this.initializePromise;
|
|
6437
|
+
}
|
|
6438
|
+
if (this.initialized) {
|
|
6439
|
+
throw new Error("Session already initialized");
|
|
6440
|
+
}
|
|
6441
|
+
const attempt = this.performInitialize();
|
|
6442
|
+
const memo = attempt.catch((error) => {
|
|
6443
|
+
this.cleanupFailedInitialize();
|
|
6444
|
+
throw error;
|
|
6445
|
+
}).finally(() => {
|
|
6446
|
+
if (this.initializePromise === memo) {
|
|
6447
|
+
this.initializePromise = null;
|
|
6448
|
+
}
|
|
6449
|
+
});
|
|
6450
|
+
this.initializePromise = memo;
|
|
6451
|
+
return memo;
|
|
6452
|
+
}
|
|
6453
|
+
async performInitialize() {
|
|
6454
|
+
const init = await this.initializeRuntimeController();
|
|
6455
|
+
this.controller = init.controller;
|
|
6456
|
+
this.runtime = init.runtime;
|
|
6457
|
+
this._agentId = init.runtime.agent_id;
|
|
6458
|
+
this._conversationId = init.runtime.conversation_id;
|
|
6459
|
+
this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}`;
|
|
6460
|
+
this._modelSettings = init.modelSettings ?? null;
|
|
6461
|
+
this._model = typeof init.model === "string" ? init.model : typeof this._modelSettings?.model === "string" ? this._modelSettings.model : "";
|
|
6462
|
+
this.toolNames = init.tools;
|
|
6463
|
+
this.removeMessageHandler = this.controller.onMessage((message) => {
|
|
6464
|
+
if (this.runtime)
|
|
6465
|
+
this.turns.handleProtocolMessage(message, this.runtime);
|
|
6466
|
+
});
|
|
6467
|
+
await this.afterRuntimeInitialized();
|
|
6468
|
+
await this.applyPostInitializeOptions();
|
|
6469
|
+
if (this.closed) {
|
|
6470
|
+
throw new Error("Session is closed");
|
|
6471
|
+
}
|
|
6472
|
+
this.initialized = true;
|
|
6473
|
+
const initMessage = {
|
|
6474
|
+
type: "init",
|
|
6475
|
+
agentId: init.runtime.agent_id,
|
|
6476
|
+
sessionId: this._sessionId,
|
|
6477
|
+
conversationId: init.runtime.conversation_id,
|
|
6478
|
+
model: this._model
|
|
6479
|
+
};
|
|
6480
|
+
if (this.toolNames !== undefined)
|
|
6481
|
+
initMessage.tools = this.toolNames;
|
|
6482
|
+
if (init.skillSources !== undefined) {
|
|
6483
|
+
initMessage.skillSources = init.skillSources;
|
|
6484
|
+
}
|
|
6485
|
+
return initMessage;
|
|
6486
|
+
}
|
|
6487
|
+
cleanupFailedInitialize() {
|
|
6488
|
+
this.removeMessageHandler?.();
|
|
6489
|
+
this.removeMessageHandler = null;
|
|
6490
|
+
this.controller?.close();
|
|
6491
|
+
this.controller = null;
|
|
6492
|
+
this.onCoreClose();
|
|
6493
|
+
this.runtime = null;
|
|
6494
|
+
this._agentId = null;
|
|
6495
|
+
this._conversationId = null;
|
|
6496
|
+
this._sessionId = null;
|
|
6497
|
+
this._modelSettings = null;
|
|
6498
|
+
this._model = "";
|
|
6499
|
+
this.toolNames = undefined;
|
|
6500
|
+
this.initialized = false;
|
|
6501
|
+
}
|
|
6502
|
+
async send(message) {
|
|
6503
|
+
if (!this.initialized) {
|
|
6504
|
+
await this.initialize();
|
|
6505
|
+
}
|
|
6506
|
+
if (!this.controller || !this.runtime) {
|
|
6507
|
+
throw new Error("Session is not initialized");
|
|
6508
|
+
}
|
|
6509
|
+
await this.beforeTurn();
|
|
6510
|
+
const turn = this.turns.trackSentTurn(this.runtime);
|
|
6511
|
+
try {
|
|
6512
|
+
this.controller.sendTurnMessage(this.runtime, message, {
|
|
6513
|
+
clientMessageId: turn.clientMessageId
|
|
6514
|
+
});
|
|
6515
|
+
} catch (error) {
|
|
6516
|
+
this.turns.removeTrackedTurn(turn);
|
|
6517
|
+
throw error;
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6520
|
+
async runTurn(message, _options = {}) {
|
|
6521
|
+
if (this.turns.hasInFlightTurn()) {
|
|
6522
|
+
throw new Error(`A turn is already in flight for this ${this.label} session. Use send() and stream() to let the listener queue messages.`);
|
|
6523
|
+
}
|
|
6524
|
+
await this.send(message);
|
|
6525
|
+
for await (const msg of this.stream()) {
|
|
6526
|
+
if (msg.type === "result") {
|
|
6527
|
+
return msg;
|
|
6528
|
+
}
|
|
6529
|
+
}
|
|
6530
|
+
return {
|
|
6531
|
+
type: "result",
|
|
6532
|
+
success: false,
|
|
6533
|
+
error: "stream_closed",
|
|
6534
|
+
errorCode: "stream_closed",
|
|
6535
|
+
recoverable: false,
|
|
6536
|
+
errorDetail: "Stream ended before terminal result",
|
|
6537
|
+
durationMs: Date.now() - this.turns.activeTurnStartedAt,
|
|
6538
|
+
conversationId: this._conversationId
|
|
6539
|
+
};
|
|
6540
|
+
}
|
|
6541
|
+
async* stream() {
|
|
6542
|
+
while (true) {
|
|
6543
|
+
const msg = await this.turns.nextMessage();
|
|
6544
|
+
if (!msg)
|
|
6545
|
+
break;
|
|
6546
|
+
yield msg;
|
|
6547
|
+
if (msg.type === "result")
|
|
6548
|
+
break;
|
|
6549
|
+
}
|
|
6550
|
+
}
|
|
6551
|
+
async abort() {
|
|
6552
|
+
if (!this.initialized)
|
|
6553
|
+
return;
|
|
6554
|
+
if (!this.controller || !this.runtime)
|
|
6555
|
+
return;
|
|
6556
|
+
this.turns.markAbortRequested();
|
|
6557
|
+
await this.controller.abort(this.runtime);
|
|
6558
|
+
}
|
|
6559
|
+
async sendCommand(command, options) {
|
|
6560
|
+
if (!this.initialized) {
|
|
6561
|
+
await this.initialize();
|
|
6562
|
+
}
|
|
6563
|
+
if (!this.controller) {
|
|
6271
6564
|
throw new Error("Session is not initialized");
|
|
6272
6565
|
}
|
|
6273
6566
|
if (!command || typeof command !== "object" || Array.isArray(command)) {
|
|
@@ -6481,14 +6774,7 @@ class RemoteClientSessionCore {
|
|
|
6481
6774
|
this.closed = true;
|
|
6482
6775
|
this.removeMessageHandler?.();
|
|
6483
6776
|
this.removeMessageHandler = null;
|
|
6484
|
-
|
|
6485
|
-
clearTimeout(this.activeTurn.timeout);
|
|
6486
|
-
for (const turn of this.pendingTurns) {
|
|
6487
|
-
if (turn.timeout)
|
|
6488
|
-
clearTimeout(turn.timeout);
|
|
6489
|
-
}
|
|
6490
|
-
this.activeTurn = null;
|
|
6491
|
-
this.pendingTurns.length = 0;
|
|
6777
|
+
this.turns.close();
|
|
6492
6778
|
for (const cancel of [...this.deviceStatusRefreshCancels]) {
|
|
6493
6779
|
cancel(new Error(`Session closed while waiting for ${this.label} device status`));
|
|
6494
6780
|
}
|
|
@@ -6497,7 +6783,6 @@ class RemoteClientSessionCore {
|
|
|
6497
6783
|
this.controller?.close();
|
|
6498
6784
|
this.controller = null;
|
|
6499
6785
|
this.onCoreClose();
|
|
6500
|
-
this.resolveAll(null);
|
|
6501
6786
|
}
|
|
6502
6787
|
get agentId() {
|
|
6503
6788
|
return this._agentId;
|
|
@@ -6558,95 +6843,12 @@ class RemoteClientSessionCore {
|
|
|
6558
6843
|
return false;
|
|
6559
6844
|
}
|
|
6560
6845
|
enableMemfsBody() {
|
|
6561
|
-
if (!this.runtime)
|
|
6562
|
-
return {};
|
|
6563
|
-
return { agent_id: this.runtime.agent_id };
|
|
6564
|
-
}
|
|
6565
|
-
setModel(model) {
|
|
6566
|
-
this._model = model;
|
|
6567
|
-
}
|
|
6568
|
-
trackSentTurn(runtime) {
|
|
6569
|
-
const turn = {
|
|
6570
|
-
id: ++this.nextTurnId,
|
|
6571
|
-
runtime,
|
|
6572
|
-
clientMessageId: `sdk-message-${Date.now()}-${++this.clientMessageCounter}`,
|
|
6573
|
-
queuedAt: Date.now(),
|
|
6574
|
-
startedAt: 0,
|
|
6575
|
-
assistantText: "",
|
|
6576
|
-
runIds: new Set,
|
|
6577
|
-
observedTurnEvidence: false,
|
|
6578
|
-
observedRequiresApprovalStop: false,
|
|
6579
|
-
abortRequested: false,
|
|
6580
|
-
timeout: null
|
|
6581
|
-
};
|
|
6582
|
-
if (this.activeTurn) {
|
|
6583
|
-
this.pendingTurns.push(turn);
|
|
6584
|
-
} else {
|
|
6585
|
-
this.activateTurn(turn);
|
|
6586
|
-
}
|
|
6587
|
-
return turn;
|
|
6588
|
-
}
|
|
6589
|
-
activateTurn(turn) {
|
|
6590
|
-
this.activeTurn = turn;
|
|
6591
|
-
turn.startedAt = Date.now();
|
|
6592
|
-
this.activeTurnStartedAt = turn.startedAt;
|
|
6593
|
-
if (this.requestTimeoutMs !== undefined) {
|
|
6594
|
-
turn.timeout = setTimeout(() => {
|
|
6595
|
-
this.failTurn(turn, `Timed out waiting for ${this.label} turn`);
|
|
6596
|
-
}, this.requestTimeoutMs);
|
|
6597
|
-
turn.timeout.unref?.();
|
|
6598
|
-
}
|
|
6599
|
-
}
|
|
6600
|
-
activateNextTurnFromProtocol() {
|
|
6601
|
-
if (this.activeTurn)
|
|
6602
|
-
return this.activeTurn;
|
|
6603
|
-
const next = this.pendingTurns.shift();
|
|
6604
|
-
if (!next)
|
|
6605
|
-
return null;
|
|
6606
|
-
this.activateTurn(next);
|
|
6607
|
-
return next;
|
|
6608
|
-
}
|
|
6609
|
-
removeTrackedTurn(turn) {
|
|
6610
|
-
if (turn.timeout)
|
|
6611
|
-
clearTimeout(turn.timeout);
|
|
6612
|
-
if (this.activeTurn === turn) {
|
|
6613
|
-
this.activeTurn = null;
|
|
6614
|
-
return;
|
|
6615
|
-
}
|
|
6616
|
-
const index = this.pendingTurns.indexOf(turn);
|
|
6617
|
-
if (index !== -1)
|
|
6618
|
-
this.pendingTurns.splice(index, 1);
|
|
6619
|
-
}
|
|
6620
|
-
failTurn(turn, detail) {
|
|
6621
|
-
if (this.activeTurn !== turn)
|
|
6622
|
-
return;
|
|
6623
|
-
this.enqueue({
|
|
6624
|
-
type: "error",
|
|
6625
|
-
message: detail,
|
|
6626
|
-
errorCode: "error",
|
|
6627
|
-
stopReason: "error",
|
|
6628
|
-
errorDetail: detail,
|
|
6629
|
-
recoverable: false
|
|
6630
|
-
});
|
|
6631
|
-
this.completeActiveTurn({
|
|
6632
|
-
runtime: turn.runtime,
|
|
6633
|
-
stopReason: "error",
|
|
6634
|
-
runIds: [...turn.runIds],
|
|
6635
|
-
success: false,
|
|
6636
|
-
detail,
|
|
6637
|
-
errorCode: "error"
|
|
6638
|
-
});
|
|
6639
|
-
}
|
|
6640
|
-
completeActiveTurn(turn) {
|
|
6641
|
-
const active = this.activeTurn;
|
|
6642
|
-
if (!active)
|
|
6643
|
-
return;
|
|
6644
|
-
if (active.timeout) {
|
|
6645
|
-
clearTimeout(active.timeout);
|
|
6646
|
-
active.timeout = null;
|
|
6647
|
-
}
|
|
6648
|
-
this.enqueue(this.resultFromTurn(turn, active));
|
|
6649
|
-
this.activeTurn = null;
|
|
6846
|
+
if (!this.runtime)
|
|
6847
|
+
return {};
|
|
6848
|
+
return { agent_id: this.runtime.agent_id };
|
|
6849
|
+
}
|
|
6850
|
+
setModel(model) {
|
|
6851
|
+
this._model = model;
|
|
6650
6852
|
}
|
|
6651
6853
|
async resolveUpdateModelPayload(input) {
|
|
6652
6854
|
if (input.reasoningEffort === undefined) {
|
|
@@ -6711,287 +6913,28 @@ class RemoteClientSessionCore {
|
|
|
6711
6913
|
ensureSuccess(response, "Failed to enable memfs");
|
|
6712
6914
|
}
|
|
6713
6915
|
const dreamingSettings = resolveDreamingSettings(options.dreaming);
|
|
6714
|
-
if (dreamingSettings) {
|
|
6715
|
-
const response = await this.controller.request("set_reflection_settings", {
|
|
6716
|
-
runtime: this.runtime,
|
|
6717
|
-
settings: dreamingSettings,
|
|
6718
|
-
scope: "both"
|
|
6719
|
-
}, { predicate: (message) => message.type === "set_reflection_settings_response" });
|
|
6720
|
-
ensureSuccess(response, "Failed to update dreaming settings");
|
|
6721
|
-
}
|
|
6722
|
-
if (this.mode.kind !== "session")
|
|
6723
|
-
return;
|
|
6724
|
-
if (this.mode.options.model !== undefined || this.mode.options.reasoningEffort !== undefined) {
|
|
6725
|
-
await this.applyModelUpdate({
|
|
6726
|
-
...this.mode.options.model !== undefined ? { model: this.mode.options.model } : {},
|
|
6727
|
-
...this.mode.options.reasoningEffort !== undefined ? { reasoningEffort: this.mode.options.reasoningEffort } : {}
|
|
6728
|
-
});
|
|
6729
|
-
}
|
|
6730
|
-
}
|
|
6731
|
-
handleProtocolMessage = (message) => {
|
|
6732
|
-
if (!this.runtime || !sameRuntime2(message, this.runtime))
|
|
6733
|
-
return;
|
|
6734
|
-
const deviceStatus = deviceStatusRecord(message);
|
|
6735
|
-
if (deviceStatus) {
|
|
6736
|
-
const status = toSessionDeviceStatus(deviceStatus);
|
|
6737
|
-
if (!status)
|
|
6738
|
-
return;
|
|
6739
|
-
for (const listener of [...this.deviceStatusListeners]) {
|
|
6740
|
-
try {
|
|
6741
|
-
listener(status);
|
|
6742
|
-
} catch {}
|
|
6743
|
-
}
|
|
6744
|
-
return;
|
|
6745
|
-
}
|
|
6746
|
-
if (message.type === "update_queue") {
|
|
6747
|
-
const sdkMessage2 = {
|
|
6748
|
-
type: "queue_update",
|
|
6749
|
-
queue: queueItems(message)
|
|
6750
|
-
};
|
|
6751
|
-
this.enqueue(sdkMessage2);
|
|
6752
|
-
return;
|
|
6753
|
-
}
|
|
6754
|
-
if (message.type === "update_loop_status") {
|
|
6755
|
-
this.handleLoopStatusMessage(message);
|
|
6756
|
-
return;
|
|
6757
|
-
}
|
|
6758
|
-
const delta = streamDeltaRecord(message);
|
|
6759
|
-
if (!delta)
|
|
6760
|
-
return;
|
|
6761
|
-
const active = this.activateNextTurnFromProtocol();
|
|
6762
|
-
if (active) {
|
|
6763
|
-
active.observedTurnEvidence = true;
|
|
6764
|
-
const runId = streamDeltaRunId2(delta);
|
|
6765
|
-
if (runId)
|
|
6766
|
-
active.runIds.add(runId);
|
|
6767
|
-
}
|
|
6768
|
-
const sdkMessage = this.transformStreamDelta(delta);
|
|
6769
|
-
if (sdkMessage) {
|
|
6770
|
-
this.enqueue(sdkMessage);
|
|
6771
|
-
}
|
|
6772
|
-
this.handleTurnTerminalDelta(delta, sdkMessage);
|
|
6773
|
-
};
|
|
6774
|
-
handleLoopStatusMessage(message) {
|
|
6775
|
-
const status = loopStatusValue(message);
|
|
6776
|
-
if (!status)
|
|
6777
|
-
return;
|
|
6778
|
-
const activeRunIds = loopStatusRunIds(message);
|
|
6779
|
-
const sdkMessage = {
|
|
6780
|
-
type: "loop_status",
|
|
6781
|
-
status,
|
|
6782
|
-
activeRunIds
|
|
6783
|
-
};
|
|
6784
|
-
this.enqueue(sdkMessage);
|
|
6785
|
-
const active = this.activeTurn;
|
|
6786
|
-
if (!active)
|
|
6787
|
-
return;
|
|
6788
|
-
for (const runId of activeRunIds)
|
|
6789
|
-
active.runIds.add(runId);
|
|
6790
|
-
const hadTurnEvidence = active.observedTurnEvidence || active.observedRequiresApprovalStop;
|
|
6791
|
-
if (!hadTurnEvidence)
|
|
6792
|
-
return;
|
|
6793
|
-
if (status === "WAITING_ON_APPROVAL") {
|
|
6794
|
-
this.completeActiveTurn({
|
|
6795
|
-
runtime: active.runtime,
|
|
6796
|
-
stopReason: "requires_approval",
|
|
6797
|
-
runIds: [...active.runIds]
|
|
6798
|
-
});
|
|
6799
|
-
return;
|
|
6800
|
-
}
|
|
6801
|
-
if (status === "WAITING_ON_INPUT" && active.abortRequested) {
|
|
6802
|
-
this.completeActiveTurn({
|
|
6803
|
-
runtime: active.runtime,
|
|
6804
|
-
stopReason: "interrupted",
|
|
6805
|
-
runIds: [...active.runIds],
|
|
6806
|
-
success: false,
|
|
6807
|
-
detail: "Interrupted",
|
|
6808
|
-
errorCode: "interrupted"
|
|
6809
|
-
});
|
|
6810
|
-
return;
|
|
6811
|
-
}
|
|
6812
|
-
if (status === "WAITING_ON_INPUT" && active.observedTurnEvidence) {
|
|
6813
|
-
this.completeActiveTurn({
|
|
6814
|
-
runtime: active.runtime,
|
|
6815
|
-
stopReason: null,
|
|
6816
|
-
runIds: [...active.runIds]
|
|
6817
|
-
});
|
|
6818
|
-
}
|
|
6819
|
-
}
|
|
6820
|
-
handleTurnTerminalDelta(delta, sdkMessage) {
|
|
6821
|
-
const active = this.activeTurn;
|
|
6822
|
-
if (!active)
|
|
6823
|
-
return;
|
|
6824
|
-
const messageType = streamDeltaMessageType2(delta);
|
|
6825
|
-
if (messageType === "stop_reason") {
|
|
6826
|
-
const stopReason = streamDeltaStopReason2(delta) ?? null;
|
|
6827
|
-
if (stopReason === "requires_approval") {
|
|
6828
|
-
active.observedRequiresApprovalStop = true;
|
|
6829
|
-
return;
|
|
6830
|
-
}
|
|
6831
|
-
this.completeActiveTurn({
|
|
6832
|
-
runtime: active.runtime,
|
|
6833
|
-
stopReason,
|
|
6834
|
-
runIds: [...active.runIds]
|
|
6835
|
-
});
|
|
6836
|
-
return;
|
|
6837
|
-
}
|
|
6838
|
-
if (sdkMessage?.type === "error") {
|
|
6839
|
-
this.completeActiveTurn({
|
|
6840
|
-
runtime: active.runtime,
|
|
6841
|
-
stopReason: sdkMessage.stopReason,
|
|
6842
|
-
runIds: [...active.runIds],
|
|
6843
|
-
success: false,
|
|
6844
|
-
detail: sdkMessage.errorDetail ?? sdkMessage.message,
|
|
6845
|
-
errorCode: sdkMessage.errorCode
|
|
6846
|
-
});
|
|
6847
|
-
}
|
|
6848
|
-
}
|
|
6849
|
-
transformStreamDelta(delta) {
|
|
6850
|
-
const messageType = typeof delta.message_type === "string" ? delta.message_type : undefined;
|
|
6851
|
-
const runId = typeof delta.run_id === "string" ? delta.run_id : undefined;
|
|
6852
|
-
const uuid = typeof delta.id === "string" ? delta.id : `${this.label}-${++this.messageCounter}`;
|
|
6853
|
-
if (messageType === "assistant_message") {
|
|
6854
|
-
const content = extractTextFromContent(delta.content);
|
|
6855
|
-
if (!content)
|
|
6856
|
-
return null;
|
|
6857
|
-
if (this.activeTurn)
|
|
6858
|
-
this.activeTurn.assistantText += content;
|
|
6859
|
-
return { type: "assistant", content, uuid, runId };
|
|
6860
|
-
}
|
|
6861
|
-
if (messageType === "reasoning_message") {
|
|
6862
|
-
const content = typeof delta.reasoning === "string" ? delta.reasoning : extractTextFromContent(delta.content);
|
|
6863
|
-
if (!content)
|
|
6864
|
-
return null;
|
|
6865
|
-
return { type: "reasoning", content, uuid, runId };
|
|
6866
|
-
}
|
|
6867
|
-
if (messageType === "tool_call_message" || messageType === "approval_request_message") {
|
|
6868
|
-
const toolCall = firstToolCall(delta);
|
|
6869
|
-
if (!toolCall)
|
|
6870
|
-
return null;
|
|
6871
|
-
const fn = toolCall.function && typeof toolCall.function === "object" ? toolCall.function : undefined;
|
|
6872
|
-
const toolCallId = (typeof toolCall.tool_call_id === "string" ? toolCall.tool_call_id : undefined) ?? (typeof toolCall.id === "string" ? toolCall.id : undefined);
|
|
6873
|
-
if (!toolCallId) {
|
|
6874
|
-
const detail = `Missing tool_call_id in ${messageType} (uuid=${uuid})`;
|
|
6875
|
-
return {
|
|
6876
|
-
type: "error",
|
|
6877
|
-
message: detail,
|
|
6878
|
-
errorCode: "protocol_error",
|
|
6879
|
-
stopReason: "protocol_error",
|
|
6880
|
-
runId,
|
|
6881
|
-
recoverable: false,
|
|
6882
|
-
errorDetail: detail
|
|
6883
|
-
};
|
|
6884
|
-
}
|
|
6885
|
-
const toolName = (typeof toolCall.name === "string" ? toolCall.name : undefined) ?? (typeof fn?.name === "string" ? fn.name : undefined) ?? "?";
|
|
6886
|
-
const { input, raw } = toolInputFromArguments(toolCall.arguments ?? fn?.arguments);
|
|
6887
|
-
return {
|
|
6888
|
-
type: "tool_call",
|
|
6889
|
-
toolCallId,
|
|
6890
|
-
toolName,
|
|
6891
|
-
toolInput: input,
|
|
6892
|
-
rawArguments: raw,
|
|
6893
|
-
uuid,
|
|
6894
|
-
runId
|
|
6895
|
-
};
|
|
6896
|
-
}
|
|
6897
|
-
if (messageType === "tool_return_message") {
|
|
6898
|
-
const toolReturn = firstToolReturn(delta) ?? delta;
|
|
6899
|
-
const toolCallId = (typeof delta.tool_call_id === "string" ? delta.tool_call_id : undefined) ?? (typeof toolReturn.tool_call_id === "string" ? toolReturn.tool_call_id : undefined);
|
|
6900
|
-
if (!toolCallId)
|
|
6901
|
-
return null;
|
|
6902
|
-
const content = extractTextFromContent(delta.tool_return ?? toolReturn.tool_return ?? toolReturn.content) ?? "";
|
|
6903
|
-
const status = typeof delta.status === "string" ? delta.status : toolReturn.status;
|
|
6904
|
-
return {
|
|
6905
|
-
type: "tool_result",
|
|
6906
|
-
toolCallId,
|
|
6907
|
-
content,
|
|
6908
|
-
isError: status === "error",
|
|
6909
|
-
uuid,
|
|
6910
|
-
runId
|
|
6911
|
-
};
|
|
6912
|
-
}
|
|
6913
|
-
if (messageType === "error_message" || messageType === "loop_error") {
|
|
6914
|
-
const detail = (typeof delta.detail === "string" ? delta.detail : undefined) ?? (typeof delta.message === "string" ? delta.message : undefined) ?? `${this.label} turn failed`;
|
|
6915
|
-
const stopReason = (typeof delta.stop_reason === "string" ? delta.stop_reason : undefined) ?? (typeof delta.error_type === "string" ? delta.error_type : undefined) ?? "error";
|
|
6916
|
-
const approvalConflict = isApprovalConflictSignal({
|
|
6917
|
-
detail,
|
|
6918
|
-
message: typeof delta.message === "string" ? delta.message : undefined,
|
|
6919
|
-
stopReason
|
|
6920
|
-
});
|
|
6921
|
-
return {
|
|
6922
|
-
type: "error",
|
|
6923
|
-
message: detail,
|
|
6924
|
-
errorCode: approvalConflict ? "approval_conflict" : toSdkErrorCode(stopReason),
|
|
6925
|
-
approvalConflict: approvalConflict || undefined,
|
|
6926
|
-
recoverable: approvalConflict ? true : false,
|
|
6927
|
-
errorDetail: detail,
|
|
6928
|
-
stopReason,
|
|
6929
|
-
runId
|
|
6930
|
-
};
|
|
6931
|
-
}
|
|
6932
|
-
if (messageType === "retry") {
|
|
6933
|
-
return {
|
|
6934
|
-
type: "retry",
|
|
6935
|
-
reason: typeof delta.reason === "string" ? delta.reason : "error",
|
|
6936
|
-
attempt: typeof delta.attempt === "number" ? delta.attempt : 0,
|
|
6937
|
-
maxAttempts: typeof delta.max_attempts === "number" ? delta.max_attempts : 0,
|
|
6938
|
-
delayMs: typeof delta.delay_ms === "number" ? delta.delay_ms : 0,
|
|
6939
|
-
runId
|
|
6940
|
-
};
|
|
6941
|
-
}
|
|
6942
|
-
if (messageType === "stop_reason" || messageType === "ping") {
|
|
6943
|
-
return null;
|
|
6944
|
-
}
|
|
6945
|
-
return {
|
|
6946
|
-
type: "stream_event",
|
|
6947
|
-
event: delta,
|
|
6948
|
-
uuid
|
|
6949
|
-
};
|
|
6950
|
-
}
|
|
6951
|
-
resultFromTurn(turn, tracker) {
|
|
6952
|
-
const stopReason = turn.stopReason ?? (turn.success === false ? "error" : undefined);
|
|
6953
|
-
const approvalConflict = isApprovalConflictSignal({
|
|
6954
|
-
detail: turn.detail,
|
|
6955
|
-
stopReason
|
|
6956
|
-
});
|
|
6957
|
-
const success = turn.success !== undefined ? turn.success && !approvalConflict && !FAILURE_STOP_REASONS.has(stopReason ?? "") : !approvalConflict && !FAILURE_STOP_REASONS.has(stopReason ?? "");
|
|
6958
|
-
const errorCode = approvalConflict ? "approval_conflict" : turn.errorCode ?? toSdkErrorCode(stopReason);
|
|
6959
|
-
return {
|
|
6960
|
-
type: "result",
|
|
6961
|
-
success,
|
|
6962
|
-
result: success ? tracker?.assistantText || undefined : undefined,
|
|
6963
|
-
error: success ? undefined : errorCode ?? stopReason ?? "error",
|
|
6964
|
-
errorCode: success ? undefined : errorCode ?? "error",
|
|
6965
|
-
approvalConflict: approvalConflict || undefined,
|
|
6966
|
-
recoverable: approvalConflict ? true : success ? undefined : false,
|
|
6967
|
-
errorDetail: success ? undefined : turn.detail,
|
|
6968
|
-
stopReason,
|
|
6969
|
-
durationMs: Date.now() - (tracker?.startedAt || this.activeTurnStartedAt),
|
|
6970
|
-
conversationId: turn.runtime.conversation_id,
|
|
6971
|
-
runIds: turn.runIds.length > 0 ? turn.runIds : undefined
|
|
6972
|
-
};
|
|
6973
|
-
}
|
|
6974
|
-
enqueue(message) {
|
|
6975
|
-
const resolver = this.streamResolvers.shift();
|
|
6976
|
-
if (resolver) {
|
|
6977
|
-
resolver(message);
|
|
6916
|
+
if (dreamingSettings) {
|
|
6917
|
+
const response = await this.controller.request("set_reflection_settings", {
|
|
6918
|
+
runtime: this.runtime,
|
|
6919
|
+
settings: dreamingSettings,
|
|
6920
|
+
scope: "both"
|
|
6921
|
+
}, { predicate: (message) => message.type === "set_reflection_settings_response" });
|
|
6922
|
+
ensureSuccess(response, "Failed to update dreaming settings");
|
|
6923
|
+
}
|
|
6924
|
+
if (this.mode.kind !== "session")
|
|
6978
6925
|
return;
|
|
6926
|
+
if (this.mode.options.model !== undefined || this.mode.options.reasoningEffort !== undefined) {
|
|
6927
|
+
await this.applyModelUpdate({
|
|
6928
|
+
...this.mode.options.model !== undefined ? { model: this.mode.options.model } : {},
|
|
6929
|
+
...this.mode.options.reasoningEffort !== undefined ? { reasoningEffort: this.mode.options.reasoningEffort } : {}
|
|
6930
|
+
});
|
|
6979
6931
|
}
|
|
6980
|
-
this.streamQueue.push(message);
|
|
6981
|
-
}
|
|
6982
|
-
nextMessage() {
|
|
6983
|
-
const next = this.streamQueue.shift();
|
|
6984
|
-
if (next)
|
|
6985
|
-
return Promise.resolve(next);
|
|
6986
|
-
if (this.closed)
|
|
6987
|
-
return Promise.resolve(null);
|
|
6988
|
-
return new Promise((resolve) => {
|
|
6989
|
-
this.streamResolvers.push(resolve);
|
|
6990
|
-
});
|
|
6991
6932
|
}
|
|
6992
|
-
|
|
6993
|
-
for (const
|
|
6994
|
-
|
|
6933
|
+
emitDeviceStatus(status) {
|
|
6934
|
+
for (const listener of [...this.deviceStatusListeners]) {
|
|
6935
|
+
try {
|
|
6936
|
+
listener(status);
|
|
6937
|
+
} catch {}
|
|
6995
6938
|
}
|
|
6996
6939
|
}
|
|
6997
6940
|
}
|
|
@@ -7035,26 +6978,6 @@ function assertRemoteCreateAgentOptionsSupported(options) {
|
|
|
7035
6978
|
throw new Error("App-server createAgent() does not yet support dreaming.behavior overrides.");
|
|
7036
6979
|
}
|
|
7037
6980
|
}
|
|
7038
|
-
function assertRemoteSessionOptionsSupported(action, options) {
|
|
7039
|
-
if (options.systemPrompt !== undefined) {
|
|
7040
|
-
throw new Error(`App-server ${action}() does not yet support systemPrompt overrides for existing agents.`);
|
|
7041
|
-
}
|
|
7042
|
-
if (options.disallowedTools !== undefined) {
|
|
7043
|
-
throw new Error(`App-server ${action}() does not yet support disallowedTools.`);
|
|
7044
|
-
}
|
|
7045
|
-
if (options.systemInfoReminder !== undefined) {
|
|
7046
|
-
throw new Error(`App-server ${action}() does not yet support systemInfoReminder overrides.`);
|
|
7047
|
-
}
|
|
7048
|
-
if (options.dreaming?.behavior !== undefined) {
|
|
7049
|
-
throw new Error(`App-server ${action}() does not yet support dreaming.behavior overrides.`);
|
|
7050
|
-
}
|
|
7051
|
-
if (options.memfsStartup !== undefined) {
|
|
7052
|
-
throw new Error(`App-server ${action}() does not support memfsStartup.`);
|
|
7053
|
-
}
|
|
7054
|
-
if (options.includePartialMessages !== undefined) {
|
|
7055
|
-
throw new Error(`App-server ${action}() streams app-server deltas directly and does not support includePartialMessages.`);
|
|
7056
|
-
}
|
|
7057
|
-
}
|
|
7058
6981
|
function normalizeMemoryBlock(block) {
|
|
7059
6982
|
const normalized = { ...block };
|
|
7060
6983
|
if (normalized.value === undefined && typeof normalized.content === "string") {
|
|
@@ -7485,9 +7408,7 @@ class AppServerSession extends RemoteClientSessionCore {
|
|
|
7485
7408
|
return options.memfs !== false;
|
|
7486
7409
|
}
|
|
7487
7410
|
async initializeRuntimeController() {
|
|
7488
|
-
await this.remoteOptions.beforeConnect?.();
|
|
7489
7411
|
const url = await this.resolveAppServerUrl();
|
|
7490
|
-
await releaseAppServerManagementConnections(url);
|
|
7491
7412
|
const client = applyUniqueRequestIds(createAppServerClient({
|
|
7492
7413
|
url,
|
|
7493
7414
|
...this.remoteOptions.authToken !== undefined ? { authToken: this.remoteOptions.authToken } : {},
|
|
@@ -7797,6 +7718,222 @@ class CloudManagementTransport {
|
|
|
7797
7718
|
}
|
|
7798
7719
|
}
|
|
7799
7720
|
|
|
7721
|
+
// src/cloud-status-transport.ts
|
|
7722
|
+
var CONNECTING = 0;
|
|
7723
|
+
var OPEN = 1;
|
|
7724
|
+
var CLOSING = 2;
|
|
7725
|
+
var CLOSED = 3;
|
|
7726
|
+
var MAX_IDEMPOTENCY_KEYS = 1000;
|
|
7727
|
+
function addSocketListener(socket, type, listener) {
|
|
7728
|
+
if (socket.addEventListener) {
|
|
7729
|
+
socket.addEventListener(type, listener);
|
|
7730
|
+
return () => socket.removeEventListener?.(type, listener);
|
|
7731
|
+
}
|
|
7732
|
+
if (socket.on) {
|
|
7733
|
+
socket.on(type, listener);
|
|
7734
|
+
return () => socket.off?.(type, listener);
|
|
7735
|
+
}
|
|
7736
|
+
throw new Error("WebSocket implementation does not support event listeners.");
|
|
7737
|
+
}
|
|
7738
|
+
function messageEventData(event) {
|
|
7739
|
+
if (typeof event === "string")
|
|
7740
|
+
return event;
|
|
7741
|
+
if (!event || typeof event !== "object")
|
|
7742
|
+
return null;
|
|
7743
|
+
const data = event.data;
|
|
7744
|
+
if (typeof data === "string")
|
|
7745
|
+
return data;
|
|
7746
|
+
if (data instanceof ArrayBuffer) {
|
|
7747
|
+
return new TextDecoder().decode(data);
|
|
7748
|
+
}
|
|
7749
|
+
if (data instanceof Uint8Array) {
|
|
7750
|
+
return new TextDecoder().decode(data);
|
|
7751
|
+
}
|
|
7752
|
+
return null;
|
|
7753
|
+
}
|
|
7754
|
+
function channelUrl(url, channel) {
|
|
7755
|
+
const parsed = new URL(url);
|
|
7756
|
+
parsed.searchParams.set("channel", channel);
|
|
7757
|
+
return parsed.toString();
|
|
7758
|
+
}
|
|
7759
|
+
|
|
7760
|
+
class CloudStatusTransport {
|
|
7761
|
+
options;
|
|
7762
|
+
controlSocket;
|
|
7763
|
+
streamSocket;
|
|
7764
|
+
state = CONNECTING;
|
|
7765
|
+
openedChannels = new Set;
|
|
7766
|
+
listeners = new Map;
|
|
7767
|
+
removers = [];
|
|
7768
|
+
seenIdempotencyKeys = new Set;
|
|
7769
|
+
seenIdempotencyOrder = [];
|
|
7770
|
+
lastEventSeq = null;
|
|
7771
|
+
pingTimer = null;
|
|
7772
|
+
closeEmitted = false;
|
|
7773
|
+
constructor(options) {
|
|
7774
|
+
this.options = options;
|
|
7775
|
+
const socketOptions = options.headers ? { headers: options.headers } : undefined;
|
|
7776
|
+
this.controlSocket = new options.WebSocket(channelUrl(options.url, "control"), socketOptions);
|
|
7777
|
+
this.streamSocket = new options.WebSocket(channelUrl(options.url, "stream"), socketOptions);
|
|
7778
|
+
this.bindSocket("control", this.controlSocket);
|
|
7779
|
+
this.bindSocket("stream", this.streamSocket);
|
|
7780
|
+
this.startPing();
|
|
7781
|
+
}
|
|
7782
|
+
get readyState() {
|
|
7783
|
+
return this.state;
|
|
7784
|
+
}
|
|
7785
|
+
send(data) {
|
|
7786
|
+
if (this.state !== OPEN || this.controlSocket.readyState !== OPEN) {
|
|
7787
|
+
throw new Error("Cloud status control socket is not open");
|
|
7788
|
+
}
|
|
7789
|
+
this.controlSocket.send(data);
|
|
7790
|
+
}
|
|
7791
|
+
close() {
|
|
7792
|
+
if (this.state === CLOSED || this.state === CLOSING)
|
|
7793
|
+
return;
|
|
7794
|
+
this.state = CLOSING;
|
|
7795
|
+
this.stopPing();
|
|
7796
|
+
this.closeUnderlyingSockets();
|
|
7797
|
+
this.finishClose({});
|
|
7798
|
+
}
|
|
7799
|
+
addEventListener(type, listener) {
|
|
7800
|
+
let listeners = this.listeners.get(type);
|
|
7801
|
+
if (!listeners) {
|
|
7802
|
+
listeners = new Set;
|
|
7803
|
+
this.listeners.set(type, listeners);
|
|
7804
|
+
}
|
|
7805
|
+
listeners.add(listener);
|
|
7806
|
+
}
|
|
7807
|
+
removeEventListener(type, listener) {
|
|
7808
|
+
this.listeners.get(type)?.delete(listener);
|
|
7809
|
+
}
|
|
7810
|
+
bindSocket(channel, socket) {
|
|
7811
|
+
this.removers.push(addSocketListener(socket, "open", () => {
|
|
7812
|
+
this.openedChannels.add(channel);
|
|
7813
|
+
if (this.openedChannels.size === 2 && this.state === CONNECTING) {
|
|
7814
|
+
this.state = OPEN;
|
|
7815
|
+
this.emit("open", {});
|
|
7816
|
+
}
|
|
7817
|
+
}), addSocketListener(socket, "message", (event) => {
|
|
7818
|
+
if (this.shouldForwardMessage(channel, socket, event)) {
|
|
7819
|
+
this.emit("message", event);
|
|
7820
|
+
}
|
|
7821
|
+
}), addSocketListener(socket, "error", (event) => {
|
|
7822
|
+
this.emit("error", event);
|
|
7823
|
+
}), addSocketListener(socket, "close", (event) => {
|
|
7824
|
+
if (this.state !== CLOSED) {
|
|
7825
|
+
this.state = CLOSING;
|
|
7826
|
+
this.stopPing();
|
|
7827
|
+
this.closeUnderlyingSockets(socket);
|
|
7828
|
+
this.finishClose(event);
|
|
7829
|
+
}
|
|
7830
|
+
}));
|
|
7831
|
+
}
|
|
7832
|
+
shouldForwardMessage(channel, socket, event) {
|
|
7833
|
+
const data = messageEventData(event);
|
|
7834
|
+
if (!data)
|
|
7835
|
+
return true;
|
|
7836
|
+
let message;
|
|
7837
|
+
try {
|
|
7838
|
+
message = JSON.parse(data);
|
|
7839
|
+
} catch {
|
|
7840
|
+
return true;
|
|
7841
|
+
}
|
|
7842
|
+
this.ackIfSequenced(socket, message);
|
|
7843
|
+
if (channel === "control" && message.type === "stream_delta") {
|
|
7844
|
+
return false;
|
|
7845
|
+
}
|
|
7846
|
+
if (this.isDuplicate(message))
|
|
7847
|
+
return false;
|
|
7848
|
+
this.trackEventSequence(message);
|
|
7849
|
+
return true;
|
|
7850
|
+
}
|
|
7851
|
+
ackIfSequenced(socket, message) {
|
|
7852
|
+
if (typeof message.seq !== "number")
|
|
7853
|
+
return;
|
|
7854
|
+
this.sendCommand(socket, { type: "ack", seq: message.seq });
|
|
7855
|
+
}
|
|
7856
|
+
isDuplicate(message) {
|
|
7857
|
+
const key = typeof message.idempotency_key === "string" ? message.idempotency_key : null;
|
|
7858
|
+
if (!key)
|
|
7859
|
+
return false;
|
|
7860
|
+
if (this.seenIdempotencyKeys.has(key))
|
|
7861
|
+
return true;
|
|
7862
|
+
this.seenIdempotencyKeys.add(key);
|
|
7863
|
+
this.seenIdempotencyOrder.push(key);
|
|
7864
|
+
while (this.seenIdempotencyOrder.length > MAX_IDEMPOTENCY_KEYS) {
|
|
7865
|
+
const oldest = this.seenIdempotencyOrder.shift();
|
|
7866
|
+
if (oldest)
|
|
7867
|
+
this.seenIdempotencyKeys.delete(oldest);
|
|
7868
|
+
}
|
|
7869
|
+
return false;
|
|
7870
|
+
}
|
|
7871
|
+
trackEventSequence(message) {
|
|
7872
|
+
if (typeof message.event_seq !== "number")
|
|
7873
|
+
return;
|
|
7874
|
+
if (this.lastEventSeq !== null && message.event_seq > this.lastEventSeq + 1) {
|
|
7875
|
+
this.sendCommand(this.controlSocket, {
|
|
7876
|
+
type: "sync",
|
|
7877
|
+
runtime: this.options.runtime,
|
|
7878
|
+
recover_approvals: true,
|
|
7879
|
+
force_device_status: true
|
|
7880
|
+
});
|
|
7881
|
+
}
|
|
7882
|
+
if (this.lastEventSeq === null || message.event_seq > this.lastEventSeq) {
|
|
7883
|
+
this.lastEventSeq = message.event_seq;
|
|
7884
|
+
}
|
|
7885
|
+
}
|
|
7886
|
+
sendCommand(socket, command) {
|
|
7887
|
+
if (socket.readyState !== OPEN)
|
|
7888
|
+
return;
|
|
7889
|
+
try {
|
|
7890
|
+
socket.send(JSON.stringify(command));
|
|
7891
|
+
} catch {}
|
|
7892
|
+
}
|
|
7893
|
+
startPing() {
|
|
7894
|
+
this.pingTimer = setInterval(() => {
|
|
7895
|
+
this.sendCommand(this.controlSocket, { type: "ping" });
|
|
7896
|
+
this.sendCommand(this.streamSocket, { type: "ping" });
|
|
7897
|
+
}, this.options.pingIntervalMs);
|
|
7898
|
+
this.pingTimer.unref?.();
|
|
7899
|
+
}
|
|
7900
|
+
stopPing() {
|
|
7901
|
+
if (!this.pingTimer)
|
|
7902
|
+
return;
|
|
7903
|
+
clearInterval(this.pingTimer);
|
|
7904
|
+
this.pingTimer = null;
|
|
7905
|
+
}
|
|
7906
|
+
closeUnderlyingSockets(except) {
|
|
7907
|
+
for (const socket of [this.controlSocket, this.streamSocket]) {
|
|
7908
|
+
if (socket !== except && (socket.readyState === CONNECTING || socket.readyState === OPEN)) {
|
|
7909
|
+
socket.close();
|
|
7910
|
+
}
|
|
7911
|
+
}
|
|
7912
|
+
}
|
|
7913
|
+
finishClose(event) {
|
|
7914
|
+
if (this.closeEmitted)
|
|
7915
|
+
return;
|
|
7916
|
+
this.closeEmitted = true;
|
|
7917
|
+
this.state = CLOSED;
|
|
7918
|
+
this.stopPing();
|
|
7919
|
+
for (const remove of this.removers.splice(0))
|
|
7920
|
+
remove();
|
|
7921
|
+
this.emit("close", event);
|
|
7922
|
+
}
|
|
7923
|
+
emit(type, event) {
|
|
7924
|
+
for (const listener of this.listeners.get(type) ?? []) {
|
|
7925
|
+
listener(event);
|
|
7926
|
+
}
|
|
7927
|
+
}
|
|
7928
|
+
}
|
|
7929
|
+
function createCloudStatusTransportConstructor(options) {
|
|
7930
|
+
return class CloudStatusTransportSocket extends CloudStatusTransport {
|
|
7931
|
+
constructor(_url, _socketOptions) {
|
|
7932
|
+
super(options);
|
|
7933
|
+
}
|
|
7934
|
+
};
|
|
7935
|
+
}
|
|
7936
|
+
|
|
7800
7937
|
// src/remote.ts
|
|
7801
7938
|
function getDefaultApiKey2() {
|
|
7802
7939
|
if (typeof process === "undefined") {
|
|
@@ -7892,13 +8029,58 @@ class RemoteEnvironmentClient {
|
|
|
7892
8029
|
}
|
|
7893
8030
|
}
|
|
7894
8031
|
|
|
8032
|
+
// src/cloud-sandbox.ts
|
|
8033
|
+
var MIN_TTL_MINUTES = 1;
|
|
8034
|
+
var MAX_TTL_MINUTES = 60;
|
|
8035
|
+
var MAX_GITHUB_REPOSITORIES = 10;
|
|
8036
|
+
var GITHUB_OWNER_PATTERN = /^[A-Za-z0-9-]+$/;
|
|
8037
|
+
var GITHUB_REPOSITORY_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
8038
|
+
function validatePositiveInteger(value, name) {
|
|
8039
|
+
if (value !== undefined && (!Number.isInteger(value) || value <= 0)) {
|
|
8040
|
+
throw new Error(`Invalid ${name}. Expected a positive integer.`);
|
|
8041
|
+
}
|
|
8042
|
+
}
|
|
8043
|
+
function validateCloudSandboxOptions(options, name) {
|
|
8044
|
+
if (options === undefined)
|
|
8045
|
+
return;
|
|
8046
|
+
if (options === null || typeof options !== "object" || Array.isArray(options)) {
|
|
8047
|
+
throw new Error(`Invalid ${name}. Expected an object.`);
|
|
8048
|
+
}
|
|
8049
|
+
if (options.ttlMinutes !== undefined && (!Number.isInteger(options.ttlMinutes) || options.ttlMinutes < MIN_TTL_MINUTES || options.ttlMinutes > MAX_TTL_MINUTES)) {
|
|
8050
|
+
throw new Error(`Invalid ${name}.ttlMinutes. Expected an integer between ${MIN_TTL_MINUTES} and ${MAX_TTL_MINUTES}.`);
|
|
8051
|
+
}
|
|
8052
|
+
validatePositiveInteger(options.readyTimeoutMs, `${name}.readyTimeoutMs`);
|
|
8053
|
+
validatePositiveInteger(options.readyPollIntervalMs, `${name}.readyPollIntervalMs`);
|
|
8054
|
+
validatePositiveInteger(options.refreshIntervalMs, `${name}.refreshIntervalMs`);
|
|
8055
|
+
if (options.githubRepositories !== undefined) {
|
|
8056
|
+
if (!Array.isArray(options.githubRepositories)) {
|
|
8057
|
+
throw new Error(`Invalid ${name}.githubRepositories. Expected an array.`);
|
|
8058
|
+
}
|
|
8059
|
+
if (options.githubRepositories.length > MAX_GITHUB_REPOSITORIES) {
|
|
8060
|
+
throw new Error(`Invalid ${name}.githubRepositories. Expected at most ${MAX_GITHUB_REPOSITORIES} repositories.`);
|
|
8061
|
+
}
|
|
8062
|
+
for (const [index, repository] of options.githubRepositories.entries()) {
|
|
8063
|
+
if (repository === null || typeof repository !== "object" || Array.isArray(repository)) {
|
|
8064
|
+
throw new Error(`Invalid ${name}.githubRepositories[${index}]. Expected an object.`);
|
|
8065
|
+
}
|
|
8066
|
+
if (typeof repository.owner !== "string" || !GITHUB_OWNER_PATTERN.test(repository.owner)) {
|
|
8067
|
+
throw new Error(`Invalid ${name}.githubRepositories[${index}].owner.`);
|
|
8068
|
+
}
|
|
8069
|
+
if (typeof repository.repo !== "string" || !GITHUB_REPOSITORY_PATTERN.test(repository.repo)) {
|
|
8070
|
+
throw new Error(`Invalid ${name}.githubRepositories[${index}].repo.`);
|
|
8071
|
+
}
|
|
8072
|
+
}
|
|
8073
|
+
}
|
|
8074
|
+
if (options.terminateOnClose !== undefined && typeof options.terminateOnClose !== "boolean") {
|
|
8075
|
+
throw new Error(`Invalid ${name}.terminateOnClose. Expected a boolean.`);
|
|
8076
|
+
}
|
|
8077
|
+
}
|
|
8078
|
+
|
|
7895
8079
|
// src/cloud-session.ts
|
|
7896
8080
|
var DEFAULT_CLOUD_API_BASE_URL3 = "https://api.letta.com";
|
|
7897
8081
|
var DEFAULT_TURN_TIMEOUT_MS = 120000;
|
|
7898
8082
|
var DEFAULT_PING_INTERVAL_MS = 30000;
|
|
7899
8083
|
var DEFAULT_SANDBOX_TTL_MINUTES = 5;
|
|
7900
|
-
var MIN_SANDBOX_TTL_MINUTES = 1;
|
|
7901
|
-
var MAX_SANDBOX_TTL_MINUTES = 60;
|
|
7902
8084
|
var DEFAULT_SANDBOX_READY_TIMEOUT_MS = 120000;
|
|
7903
8085
|
var DEFAULT_SANDBOX_READY_POLL_INTERVAL_MS = 1000;
|
|
7904
8086
|
var DEFAULT_REPOSITORY_ATTACH_TIMEOUT_MS = 1e4;
|
|
@@ -8009,34 +8191,13 @@ function assertOkResponse2(response, body, action, url) {
|
|
|
8009
8191
|
throw new Error(parts.join(" — "));
|
|
8010
8192
|
}
|
|
8011
8193
|
}
|
|
8012
|
-
function
|
|
8194
|
+
function validatePositiveInteger2(value, name) {
|
|
8013
8195
|
if (value !== undefined && (!Number.isInteger(value) || value <= 0)) {
|
|
8014
8196
|
throw new Error(`Invalid ${name}. Expected a positive integer.`);
|
|
8015
8197
|
}
|
|
8016
8198
|
}
|
|
8017
|
-
function validateIntegerRange(value, name, min, max) {
|
|
8018
|
-
if (value === undefined)
|
|
8019
|
-
return;
|
|
8020
|
-
if (!Number.isInteger(value) || value < min || value > max) {
|
|
8021
|
-
throw new Error(`Invalid ${name}. Expected an integer between ${min} and ${max}.`);
|
|
8022
|
-
}
|
|
8023
|
-
}
|
|
8024
|
-
function validateCloudSandboxOptions(options, name) {
|
|
8025
|
-
if (options === undefined)
|
|
8026
|
-
return;
|
|
8027
|
-
if (options === null || typeof options !== "object" || Array.isArray(options)) {
|
|
8028
|
-
throw new Error(`Invalid ${name}. Expected an object.`);
|
|
8029
|
-
}
|
|
8030
|
-
validateIntegerRange(options.ttlMinutes, `${name}.ttlMinutes`, MIN_SANDBOX_TTL_MINUTES, MAX_SANDBOX_TTL_MINUTES);
|
|
8031
|
-
validatePositiveInteger(options.readyTimeoutMs, `${name}.readyTimeoutMs`);
|
|
8032
|
-
validatePositiveInteger(options.readyPollIntervalMs, `${name}.readyPollIntervalMs`);
|
|
8033
|
-
validatePositiveInteger(options.refreshIntervalMs, `${name}.refreshIntervalMs`);
|
|
8034
|
-
if (options.terminateOnClose !== undefined && typeof options.terminateOnClose !== "boolean") {
|
|
8035
|
-
throw new Error(`Invalid ${name}.terminateOnClose. Expected a boolean.`);
|
|
8036
|
-
}
|
|
8037
|
-
}
|
|
8038
8199
|
function validateCloudClientOptions(options) {
|
|
8039
|
-
|
|
8200
|
+
validatePositiveInteger2(options.requestTimeoutMs, "requestTimeoutMs");
|
|
8040
8201
|
validateCloudSandboxOptions(options.sandbox, "sandbox");
|
|
8041
8202
|
if (options.environment !== undefined && options.sandbox !== undefined) {
|
|
8042
8203
|
throw new Error("Letta Cloud sessions cannot specify both environment and sandbox options.");
|
|
@@ -8081,191 +8242,6 @@ function buildCloudStatusWebSocketUrl(params) {
|
|
|
8081
8242
|
}
|
|
8082
8243
|
return base.toString();
|
|
8083
8244
|
}
|
|
8084
|
-
function addSocketListener(socket, type, listener) {
|
|
8085
|
-
if (socket.addEventListener) {
|
|
8086
|
-
socket.addEventListener(type, listener);
|
|
8087
|
-
return () => socket.removeEventListener?.(type, listener);
|
|
8088
|
-
}
|
|
8089
|
-
if (socket.on) {
|
|
8090
|
-
socket.on(type, listener);
|
|
8091
|
-
return () => socket.off?.(type, listener);
|
|
8092
|
-
}
|
|
8093
|
-
throw new Error("WebSocket implementation does not support event listeners.");
|
|
8094
|
-
}
|
|
8095
|
-
function messageEventData(event) {
|
|
8096
|
-
if (typeof event === "string")
|
|
8097
|
-
return event;
|
|
8098
|
-
if (event && typeof event === "object") {
|
|
8099
|
-
const data = event.data;
|
|
8100
|
-
if (typeof data === "string")
|
|
8101
|
-
return data;
|
|
8102
|
-
if (data instanceof ArrayBuffer) {
|
|
8103
|
-
return new TextDecoder().decode(data);
|
|
8104
|
-
}
|
|
8105
|
-
if (data instanceof Uint8Array) {
|
|
8106
|
-
return new TextDecoder().decode(data);
|
|
8107
|
-
}
|
|
8108
|
-
}
|
|
8109
|
-
return null;
|
|
8110
|
-
}
|
|
8111
|
-
function createCloudStatusWebSocketConstructor(params) {
|
|
8112
|
-
const WebSocketCtor = getWebSocketConstructor(params.cloudOptions.WebSocket);
|
|
8113
|
-
const authMode = params.cloudOptions.webSocketAuth ?? "header";
|
|
8114
|
-
const cloudHeaders3 = authMode === "header" ? cloudWebSocketHeaders(params.cloudOptions) : undefined;
|
|
8115
|
-
const pingIntervalMs = params.cloudOptions.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
|
|
8116
|
-
const state = {
|
|
8117
|
-
runtime: params.runtime,
|
|
8118
|
-
seenIdempotencyKeys: new Set,
|
|
8119
|
-
seenIdempotencyOrder: [],
|
|
8120
|
-
lastEventSeq: null,
|
|
8121
|
-
controlSocket: null
|
|
8122
|
-
};
|
|
8123
|
-
|
|
8124
|
-
class CloudStatusSocketAdapter {
|
|
8125
|
-
socket;
|
|
8126
|
-
channel;
|
|
8127
|
-
listenerRemovers = new Map;
|
|
8128
|
-
pingTimer = null;
|
|
8129
|
-
removeCloseListener = null;
|
|
8130
|
-
constructor(url, options) {
|
|
8131
|
-
this.channel = new URL(url).searchParams.get("channel");
|
|
8132
|
-
const mergedHeaders = authMode === "header" ? {
|
|
8133
|
-
...options?.headers ?? {},
|
|
8134
|
-
...cloudHeaders3 ?? {}
|
|
8135
|
-
} : undefined;
|
|
8136
|
-
const socketOptions = mergedHeaders && Object.keys(mergedHeaders).length > 0 ? { headers: mergedHeaders } : undefined;
|
|
8137
|
-
this.socket = new WebSocketCtor(url, socketOptions);
|
|
8138
|
-
if (this.channel === "control") {
|
|
8139
|
-
state.controlSocket = this;
|
|
8140
|
-
}
|
|
8141
|
-
this.removeCloseListener = addSocketListener(this.socket, "close", () => {
|
|
8142
|
-
this.stopPing();
|
|
8143
|
-
if (state.controlSocket === this)
|
|
8144
|
-
state.controlSocket = null;
|
|
8145
|
-
});
|
|
8146
|
-
this.startPing(pingIntervalMs);
|
|
8147
|
-
}
|
|
8148
|
-
get readyState() {
|
|
8149
|
-
return this.socket.readyState;
|
|
8150
|
-
}
|
|
8151
|
-
send(data) {
|
|
8152
|
-
this.socket.send(data);
|
|
8153
|
-
}
|
|
8154
|
-
close() {
|
|
8155
|
-
this.stopPing();
|
|
8156
|
-
this.removeCloseListener?.();
|
|
8157
|
-
this.removeCloseListener = null;
|
|
8158
|
-
if (state.controlSocket === this)
|
|
8159
|
-
state.controlSocket = null;
|
|
8160
|
-
this.socket.close();
|
|
8161
|
-
}
|
|
8162
|
-
addEventListener(type, listener) {
|
|
8163
|
-
const wrapped = type === "message" ? (event) => {
|
|
8164
|
-
if (this.handleIncomingMessage(event)) {
|
|
8165
|
-
listener(event);
|
|
8166
|
-
}
|
|
8167
|
-
} : listener;
|
|
8168
|
-
const remove = addSocketListener(this.socket, type, wrapped);
|
|
8169
|
-
let typeRemovers = this.listenerRemovers.get(type);
|
|
8170
|
-
if (!typeRemovers) {
|
|
8171
|
-
typeRemovers = new Map;
|
|
8172
|
-
this.listenerRemovers.set(type, typeRemovers);
|
|
8173
|
-
}
|
|
8174
|
-
typeRemovers.set(listener, remove);
|
|
8175
|
-
}
|
|
8176
|
-
removeEventListener(type, listener) {
|
|
8177
|
-
const typeRemovers = this.listenerRemovers.get(type);
|
|
8178
|
-
const remove = typeRemovers?.get(listener);
|
|
8179
|
-
if (!remove)
|
|
8180
|
-
return;
|
|
8181
|
-
remove();
|
|
8182
|
-
typeRemovers?.delete(listener);
|
|
8183
|
-
if (typeRemovers?.size === 0) {
|
|
8184
|
-
this.listenerRemovers.delete(type);
|
|
8185
|
-
}
|
|
8186
|
-
}
|
|
8187
|
-
handleIncomingMessage(event) {
|
|
8188
|
-
const data = messageEventData(event);
|
|
8189
|
-
if (!data)
|
|
8190
|
-
return true;
|
|
8191
|
-
let message;
|
|
8192
|
-
try {
|
|
8193
|
-
message = JSON.parse(data);
|
|
8194
|
-
} catch {
|
|
8195
|
-
return true;
|
|
8196
|
-
}
|
|
8197
|
-
this.ackIfSequenced(message);
|
|
8198
|
-
if (this.channel === "control" && message.type === "stream_delta") {
|
|
8199
|
-
return false;
|
|
8200
|
-
}
|
|
8201
|
-
if (this.isDuplicate(message))
|
|
8202
|
-
return false;
|
|
8203
|
-
this.trackEventSeq(message);
|
|
8204
|
-
return true;
|
|
8205
|
-
}
|
|
8206
|
-
ackIfSequenced(message) {
|
|
8207
|
-
if (typeof message.seq !== "number")
|
|
8208
|
-
return;
|
|
8209
|
-
this.sendCloudCommand({ type: "ack", seq: message.seq });
|
|
8210
|
-
}
|
|
8211
|
-
isDuplicate(message) {
|
|
8212
|
-
const idempotencyKey = typeof message.idempotency_key === "string" ? message.idempotency_key : undefined;
|
|
8213
|
-
if (!idempotencyKey)
|
|
8214
|
-
return false;
|
|
8215
|
-
if (state.seenIdempotencyKeys.has(idempotencyKey))
|
|
8216
|
-
return true;
|
|
8217
|
-
state.seenIdempotencyKeys.add(idempotencyKey);
|
|
8218
|
-
state.seenIdempotencyOrder.push(idempotencyKey);
|
|
8219
|
-
while (state.seenIdempotencyOrder.length > 1000) {
|
|
8220
|
-
const oldest = state.seenIdempotencyOrder.shift();
|
|
8221
|
-
if (oldest)
|
|
8222
|
-
state.seenIdempotencyKeys.delete(oldest);
|
|
8223
|
-
}
|
|
8224
|
-
return false;
|
|
8225
|
-
}
|
|
8226
|
-
trackEventSeq(message) {
|
|
8227
|
-
if (typeof message.event_seq !== "number")
|
|
8228
|
-
return;
|
|
8229
|
-
if (state.lastEventSeq !== null && message.event_seq > state.lastEventSeq + 1) {
|
|
8230
|
-
this.sendSync(true);
|
|
8231
|
-
}
|
|
8232
|
-
if (state.lastEventSeq === null || message.event_seq > state.lastEventSeq) {
|
|
8233
|
-
state.lastEventSeq = message.event_seq;
|
|
8234
|
-
}
|
|
8235
|
-
}
|
|
8236
|
-
sendSync(recoverApprovals) {
|
|
8237
|
-
const target = state.controlSocket?.readyState === 1 ? state.controlSocket : this;
|
|
8238
|
-
target.sendCloudCommand({
|
|
8239
|
-
type: "sync",
|
|
8240
|
-
runtime: state.runtime,
|
|
8241
|
-
recover_approvals: recoverApprovals,
|
|
8242
|
-
force_device_status: true
|
|
8243
|
-
});
|
|
8244
|
-
}
|
|
8245
|
-
sendCloudCommand(command) {
|
|
8246
|
-
if (this.readyState !== 1)
|
|
8247
|
-
return;
|
|
8248
|
-
try {
|
|
8249
|
-
this.socket.send(JSON.stringify(command));
|
|
8250
|
-
} catch {}
|
|
8251
|
-
}
|
|
8252
|
-
startPing(intervalMs) {
|
|
8253
|
-
if (this.pingTimer)
|
|
8254
|
-
return;
|
|
8255
|
-
this.pingTimer = setInterval(() => {
|
|
8256
|
-
this.sendCloudCommand({ type: "ping" });
|
|
8257
|
-
}, intervalMs);
|
|
8258
|
-
this.pingTimer.unref?.();
|
|
8259
|
-
}
|
|
8260
|
-
stopPing() {
|
|
8261
|
-
if (!this.pingTimer)
|
|
8262
|
-
return;
|
|
8263
|
-
clearInterval(this.pingTimer);
|
|
8264
|
-
this.pingTimer = null;
|
|
8265
|
-
}
|
|
8266
|
-
}
|
|
8267
|
-
return CloudStatusSocketAdapter;
|
|
8268
|
-
}
|
|
8269
8245
|
function isCloudConversation(value) {
|
|
8270
8246
|
return Boolean(value && typeof value === "object" && typeof value.id === "string");
|
|
8271
8247
|
}
|
|
@@ -8309,24 +8285,6 @@ function assertCloudSessionOptionsSupported(action, options) {
|
|
|
8309
8285
|
if (options.environment !== undefined && options.sandbox !== undefined) {
|
|
8310
8286
|
throw new Error(`Letta Cloud ${action}() cannot specify both environment and sandbox options.`);
|
|
8311
8287
|
}
|
|
8312
|
-
if (options.systemPrompt !== undefined) {
|
|
8313
|
-
throw new Error(`Letta Cloud ${action}() cannot rewrite an existing agent's systemPrompt from the SDK adapter yet.`);
|
|
8314
|
-
}
|
|
8315
|
-
if (options.disallowedTools !== undefined) {
|
|
8316
|
-
throw new Error(`Letta Cloud ${action}() has not wired disallowedTools to the remote device protocol yet.`);
|
|
8317
|
-
}
|
|
8318
|
-
if (options.systemInfoReminder !== undefined) {
|
|
8319
|
-
throw new Error(`Letta Cloud ${action}() has not wired systemInfoReminder to the remote device protocol yet.`);
|
|
8320
|
-
}
|
|
8321
|
-
if (options.dreaming?.behavior !== undefined) {
|
|
8322
|
-
throw new Error(`Letta Cloud ${action}() does not yet support dreaming.behavior overrides.`);
|
|
8323
|
-
}
|
|
8324
|
-
if (options.memfsStartup !== undefined) {
|
|
8325
|
-
throw new Error(`Letta Cloud ${action}() does not support memfsStartup.`);
|
|
8326
|
-
}
|
|
8327
|
-
if (options.includePartialMessages !== undefined) {
|
|
8328
|
-
throw new Error(`Letta Cloud ${action}() streams Remote Client deltas directly; includePartialMessages is not a separate toggle.`);
|
|
8329
|
-
}
|
|
8330
8288
|
}
|
|
8331
8289
|
|
|
8332
8290
|
class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
@@ -8366,8 +8324,11 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
8366
8324
|
});
|
|
8367
8325
|
const client = applyUniqueRequestIds(createAppServerClient({
|
|
8368
8326
|
url,
|
|
8369
|
-
WebSocket:
|
|
8370
|
-
|
|
8327
|
+
WebSocket: createCloudStatusTransportConstructor({
|
|
8328
|
+
url,
|
|
8329
|
+
WebSocket: getWebSocketConstructor(this.cloudOptions.WebSocket),
|
|
8330
|
+
...(this.cloudOptions.webSocketAuth ?? "header") === "header" ? { headers: cloudWebSocketHeaders(this.cloudOptions) } : {},
|
|
8331
|
+
pingIntervalMs: this.cloudOptions.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,
|
|
8371
8332
|
runtime: resolved.runtime
|
|
8372
8333
|
}),
|
|
8373
8334
|
requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS
|
|
@@ -8639,10 +8600,15 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
8639
8600
|
async createManagedSandbox(agentId, conversationId) {
|
|
8640
8601
|
const fetchImpl = getFetch3(this.cloudOptions.fetch);
|
|
8641
8602
|
const baseUrl = normalizeCloudApiBaseUrl2(this.cloudOptions.apiBaseUrl);
|
|
8603
|
+
const sandboxOptions = this.resolvedSandboxOptions();
|
|
8604
|
+
const githubRepositories = sandboxOptions.githubRepositories;
|
|
8642
8605
|
const response = await fetchImpl(`${baseUrl}/v1/agents/${encodeURIComponent(agentId)}/sandboxes`, {
|
|
8643
8606
|
method: "POST",
|
|
8644
8607
|
headers: cloudHeaders2(this.cloudOptions),
|
|
8645
|
-
body: JSON.stringify(
|
|
8608
|
+
body: JSON.stringify({
|
|
8609
|
+
...conversationId ? { conversationId } : {},
|
|
8610
|
+
...githubRepositories && githubRepositories.length > 0 ? { githubRepositories } : {}
|
|
8611
|
+
})
|
|
8646
8612
|
});
|
|
8647
8613
|
const body = await parseJsonResponse3(response);
|
|
8648
8614
|
assertOkResponse2(response, body, "Cloud create managed sandbox", `${baseUrl}/v1/agents/${encodeURIComponent(agentId)}/sandboxes`);
|
|
@@ -8653,7 +8619,6 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
|
|
|
8653
8619
|
if (responseConversationId !== null && responseConversationId !== conversationId) {
|
|
8654
8620
|
throw new Error(`Cloud managed sandbox response conversation mismatch: expected ${conversationId ?? "none"}, got ${responseConversationId}.`);
|
|
8655
8621
|
}
|
|
8656
|
-
const sandboxOptions = this.resolvedSandboxOptions();
|
|
8657
8622
|
const ttlMinutes = sandboxOptions.ttlMinutes ?? DEFAULT_SANDBOX_TTL_MINUTES;
|
|
8658
8623
|
const readyTimeoutMs = sandboxOptions.readyTimeoutMs ?? DEFAULT_SANDBOX_READY_TIMEOUT_MS;
|
|
8659
8624
|
const readyPollIntervalMs = sandboxOptions.readyPollIntervalMs ?? DEFAULT_SANDBOX_READY_POLL_INTERVAL_MS;
|
|
@@ -8951,7 +8916,23 @@ function validateApprovalRecoveryOptions(options) {
|
|
|
8951
8916
|
}
|
|
8952
8917
|
}
|
|
8953
8918
|
function validateRemovedSessionOptions(options) {
|
|
8954
|
-
|
|
8919
|
+
const removedOptions = options;
|
|
8920
|
+
if (removedOptions.systemPrompt !== undefined) {
|
|
8921
|
+
throw new Error("systemPrompt is not supported when opening an existing agent session.");
|
|
8922
|
+
}
|
|
8923
|
+
if (removedOptions.disallowedTools !== undefined) {
|
|
8924
|
+
throw new Error("disallowedTools is not supported when opening an existing agent session.");
|
|
8925
|
+
}
|
|
8926
|
+
if (removedOptions.systemInfoReminder !== undefined) {
|
|
8927
|
+
throw new Error("systemInfoReminder is not supported when opening an existing agent session.");
|
|
8928
|
+
}
|
|
8929
|
+
if (removedOptions.includePartialMessages !== undefined) {
|
|
8930
|
+
throw new Error("includePartialMessages is not supported by app-server sessions.");
|
|
8931
|
+
}
|
|
8932
|
+
if (options.dreaming && "behavior" in options.dreaming && options.dreaming.behavior !== undefined) {
|
|
8933
|
+
throw new Error("dreaming.behavior is not supported when opening an existing agent session.");
|
|
8934
|
+
}
|
|
8935
|
+
if (removedOptions.memfsStartup !== undefined) {
|
|
8955
8936
|
throw new Error("memfsStartup is not supported by the SDK.");
|
|
8956
8937
|
}
|
|
8957
8938
|
}
|
|
@@ -9001,9 +8982,6 @@ function validateReasoningEffort(value) {
|
|
|
9001
8982
|
}
|
|
9002
8983
|
}
|
|
9003
8984
|
function validateCreateSessionOptions(options) {
|
|
9004
|
-
if (options.systemPrompt !== undefined) {
|
|
9005
|
-
validateSystemPromptPreset(options.systemPrompt);
|
|
9006
|
-
}
|
|
9007
8985
|
validateSkillSources(options.skillSources);
|
|
9008
8986
|
validateReasoningEffort(options.reasoningEffort);
|
|
9009
8987
|
validateDreamingOptions(options.dreaming);
|
|
@@ -9059,6 +9037,7 @@ function stripCloudExecutionOptions(options) {
|
|
|
9059
9037
|
const sessionOptions = { ...options };
|
|
9060
9038
|
delete sessionOptions.environment;
|
|
9061
9039
|
delete sessionOptions.sandbox;
|
|
9040
|
+
delete sessionOptions.filesystemConfinement;
|
|
9062
9041
|
return sessionOptions;
|
|
9063
9042
|
}
|
|
9064
9043
|
function hasRepositoryResources(options) {
|
|
@@ -9102,17 +9081,13 @@ class LettaAgentClientBase {
|
|
|
9102
9081
|
}
|
|
9103
9082
|
if (this.backend === "local") {
|
|
9104
9083
|
const localOptions = options;
|
|
9105
|
-
if (
|
|
9106
|
-
throw new Error("
|
|
9084
|
+
if ("transport" in localOptions) {
|
|
9085
|
+
throw new Error("Local transport selection has been removed. The local backend always uses the app-server protocol.");
|
|
9107
9086
|
}
|
|
9108
9087
|
const requestTimeoutMs = localOptions.appServer?.requestTimeoutMs;
|
|
9109
9088
|
if (requestTimeoutMs !== undefined && (!Number.isInteger(requestTimeoutMs) || requestTimeoutMs <= 0)) {
|
|
9110
9089
|
throw new Error("Invalid appServer.requestTimeoutMs. Expected a positive integer.");
|
|
9111
9090
|
}
|
|
9112
|
-
const idleLingerMs = localOptions.appServer?.idleLingerMs;
|
|
9113
|
-
if (idleLingerMs !== undefined && (!Number.isInteger(idleLingerMs) || idleLingerMs < 0)) {
|
|
9114
|
-
throw new Error("Invalid appServer.idleLingerMs. Expected a non-negative integer.");
|
|
9115
|
-
}
|
|
9116
9091
|
const startupTimeoutMs = localOptions.appServer?.startupTimeoutMs;
|
|
9117
9092
|
if (startupTimeoutMs !== undefined && (!Number.isInteger(startupTimeoutMs) || startupTimeoutMs <= 0)) {
|
|
9118
9093
|
throw new Error("Invalid appServer.startupTimeoutMs. Expected a positive integer.");
|
|
@@ -9125,9 +9100,6 @@ class LettaAgentClientBase {
|
|
|
9125
9100
|
if (options.requestTimeoutMs !== undefined && (!Number.isInteger(options.requestTimeoutMs) || options.requestTimeoutMs <= 0)) {
|
|
9126
9101
|
throw new Error("Invalid requestTimeoutMs. Expected a positive integer.");
|
|
9127
9102
|
}
|
|
9128
|
-
if (options.idleLingerMs !== undefined && (!Number.isInteger(options.idleLingerMs) || options.idleLingerMs < 0)) {
|
|
9129
|
-
throw new Error("Invalid idleLingerMs. Expected a non-negative integer.");
|
|
9130
|
-
}
|
|
9131
9103
|
}
|
|
9132
9104
|
if (this.backend === "cloud") {
|
|
9133
9105
|
validateCloudClientOptions(options);
|
|
@@ -9155,40 +9127,35 @@ class LettaAgentClientBase {
|
|
|
9155
9127
|
}
|
|
9156
9128
|
return this.createLocalAgent(options);
|
|
9157
9129
|
}
|
|
9158
|
-
createSession(
|
|
9159
|
-
|
|
9160
|
-
|
|
9161
|
-
|
|
9162
|
-
const sessionOptions = stripCloudExecutionOptions(
|
|
9130
|
+
createSession(agentId, options = {}) {
|
|
9131
|
+
if (typeof agentId !== "string" || agentId.length === 0) {
|
|
9132
|
+
throw new Error("createSession() requires a non-empty agent id.");
|
|
9133
|
+
}
|
|
9134
|
+
const sessionOptions = stripCloudExecutionOptions(options);
|
|
9163
9135
|
validateCreateSessionOptions(sessionOptions);
|
|
9136
|
+
this.assertSessionBackend("createSession", options);
|
|
9164
9137
|
if (this.backend === "remote") {
|
|
9165
|
-
if (!agentId) {
|
|
9166
|
-
throw new Error("App-server createSession() requires an agent id. Call createAgent() first or pass an agent id.");
|
|
9167
|
-
}
|
|
9168
9138
|
return new AppServerSession(this.appServerSessionOptions(), {
|
|
9169
9139
|
kind: "session",
|
|
9170
9140
|
agentId,
|
|
9171
9141
|
newConversation: true,
|
|
9172
|
-
options
|
|
9142
|
+
options
|
|
9173
9143
|
});
|
|
9174
9144
|
}
|
|
9175
9145
|
if (this.backend === "cloud") {
|
|
9176
|
-
if (!agentId) {
|
|
9177
|
-
throw new Error("Letta Cloud createSession() requires an agent id. Call createAgent() first or pass an agent id.");
|
|
9178
|
-
}
|
|
9179
9146
|
return new CloudEnvironmentSession(this.cloudOptions(), {
|
|
9180
9147
|
kind: "session",
|
|
9181
9148
|
agentId,
|
|
9182
9149
|
newConversation: true,
|
|
9183
|
-
options
|
|
9150
|
+
options
|
|
9184
9151
|
});
|
|
9185
9152
|
}
|
|
9186
|
-
return this.createLocalSession(agentId,
|
|
9153
|
+
return this.createLocalSession(agentId, options);
|
|
9187
9154
|
}
|
|
9188
9155
|
resumeSession(id, options = {}) {
|
|
9189
|
-
this.assertSessionBackend("resumeSession", options);
|
|
9190
9156
|
const sessionOptions = stripCloudExecutionOptions(options);
|
|
9191
9157
|
validateCreateSessionOptions(sessionOptions);
|
|
9158
|
+
this.assertSessionBackend("resumeSession", options);
|
|
9192
9159
|
if (this.backend === "remote") {
|
|
9193
9160
|
if (looksLikeConversationId(id)) {
|
|
9194
9161
|
return new AppServerSession(this.appServerSessionOptions(), {
|
|
@@ -9219,10 +9186,10 @@ class LettaAgentClientBase {
|
|
|
9219
9186
|
options
|
|
9220
9187
|
});
|
|
9221
9188
|
}
|
|
9222
|
-
return this.resumeLocalSession(id, options
|
|
9189
|
+
return this.resumeLocalSession(id, options);
|
|
9223
9190
|
}
|
|
9224
9191
|
async prompt(message, agentId, options = {}) {
|
|
9225
|
-
const session =
|
|
9192
|
+
const session = this.createSession(agentId, options);
|
|
9226
9193
|
try {
|
|
9227
9194
|
return await session.runTurn(message);
|
|
9228
9195
|
} finally {
|
|
@@ -9230,6 +9197,9 @@ class LettaAgentClientBase {
|
|
|
9230
9197
|
}
|
|
9231
9198
|
}
|
|
9232
9199
|
assertSessionBackend(action, options) {
|
|
9200
|
+
if (options.filesystemConfinement !== undefined && options.filesystemConfinement !== "memory") {
|
|
9201
|
+
throw new Error(`Invalid filesystemConfinement '${String(options.filesystemConfinement)}'. Valid value: memory.`);
|
|
9202
|
+
}
|
|
9233
9203
|
const effectiveEnvironment = options.environment ?? this.environment;
|
|
9234
9204
|
if (this.backend === "local") {
|
|
9235
9205
|
if (effectiveEnvironment !== undefined) {
|
|
@@ -9241,12 +9211,18 @@ class LettaAgentClientBase {
|
|
|
9241
9211
|
if (hasRepositoryResources(options)) {
|
|
9242
9212
|
throw new Error(`${action}() repository resources are only valid with backend: "cloud".`);
|
|
9243
9213
|
}
|
|
9244
|
-
if (
|
|
9245
|
-
|
|
9214
|
+
if (options.filesystemConfinement !== undefined) {
|
|
9215
|
+
const localOptions = this.options;
|
|
9216
|
+
if (localOptions.appServer?.url !== undefined) {
|
|
9217
|
+
throw new Error(`${action}() filesystemConfinement requires an SDK-owned local app-server process.`);
|
|
9218
|
+
}
|
|
9246
9219
|
}
|
|
9247
9220
|
return;
|
|
9248
9221
|
}
|
|
9249
9222
|
if (this.backend === "remote") {
|
|
9223
|
+
if (options.filesystemConfinement !== undefined) {
|
|
9224
|
+
throw new Error(`${action}() filesystemConfinement requires an SDK-owned local app-server process.`);
|
|
9225
|
+
}
|
|
9250
9226
|
if (options.environment !== undefined) {
|
|
9251
9227
|
throw new Error(`${action}() environment overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
|
|
9252
9228
|
}
|
|
@@ -9256,10 +9232,12 @@ class LettaAgentClientBase {
|
|
|
9256
9232
|
if (hasRepositoryResources(options)) {
|
|
9257
9233
|
throw new Error(`${action}() repository resources are only valid with backend: "cloud".`);
|
|
9258
9234
|
}
|
|
9259
|
-
assertRemoteSessionOptionsSupported(action, options);
|
|
9260
9235
|
return;
|
|
9261
9236
|
}
|
|
9262
9237
|
if (this.backend === "cloud") {
|
|
9238
|
+
if (options.filesystemConfinement !== undefined) {
|
|
9239
|
+
throw new Error(`${action}() filesystemConfinement is only supported with backend: "local".`);
|
|
9240
|
+
}
|
|
9263
9241
|
const cloudOptions = this.cloudOptions();
|
|
9264
9242
|
if (cloudOptions.environment !== undefined && options.sandbox !== undefined) {
|
|
9265
9243
|
throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default environment.`);
|
|
@@ -9272,16 +9250,13 @@ class LettaAgentClientBase {
|
|
|
9272
9250
|
}
|
|
9273
9251
|
throw new Error(`LettaAgentClient backend '${this.backend}' is not implemented yet. ${action} currently supports backend 'local' only.`);
|
|
9274
9252
|
}
|
|
9275
|
-
useLegacyLocalStdio() {
|
|
9276
|
-
return this.options.transport === "stdio";
|
|
9277
|
-
}
|
|
9278
9253
|
createLocalAgent(_options) {
|
|
9279
9254
|
throw this.localBackendUnavailableError();
|
|
9280
9255
|
}
|
|
9281
|
-
createLocalSession(_agentId, _options
|
|
9256
|
+
createLocalSession(_agentId, _options) {
|
|
9282
9257
|
throw this.localBackendUnavailableError();
|
|
9283
9258
|
}
|
|
9284
|
-
resumeLocalSession(_id, _options
|
|
9259
|
+
resumeLocalSession(_id, _options) {
|
|
9285
9260
|
throw this.localBackendUnavailableError();
|
|
9286
9261
|
}
|
|
9287
9262
|
localBackendUnavailableError() {
|
|
@@ -9297,10 +9272,7 @@ class LettaAgentClientBase {
|
|
|
9297
9272
|
return this.options;
|
|
9298
9273
|
}
|
|
9299
9274
|
appServerSessionOptions() {
|
|
9300
|
-
return
|
|
9301
|
-
...this.remoteOptions(),
|
|
9302
|
-
beforeConnect: () => this.releaseIdleManagementConnection()
|
|
9303
|
-
};
|
|
9275
|
+
return this.remoteOptions();
|
|
9304
9276
|
}
|
|
9305
9277
|
getRepositoriesClient() {
|
|
9306
9278
|
if (this.backend !== "cloud") {
|
|
@@ -9309,9 +9281,6 @@ class LettaAgentClientBase {
|
|
|
9309
9281
|
this.repositoriesClient ??= new RepositoriesClient(this.cloudOptions());
|
|
9310
9282
|
return this.repositoriesClient;
|
|
9311
9283
|
}
|
|
9312
|
-
async releaseIdleManagementConnection() {
|
|
9313
|
-
await this.managementTransport?.releaseIdleConnection?.();
|
|
9314
|
-
}
|
|
9315
9284
|
getManagementTransport() {
|
|
9316
9285
|
if (this.managementTransport)
|
|
9317
9286
|
return this.managementTransport;
|
|
@@ -9419,4 +9388,4 @@ export {
|
|
|
9419
9388
|
CloudManagedSandboxExpiredError
|
|
9420
9389
|
};
|
|
9421
9390
|
|
|
9422
|
-
//# debugId=
|
|
9391
|
+
//# debugId=AAFE1DD6848E90C364756E2164756E21
|