@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.
Files changed (70) hide show
  1. package/AGENTS.md +44 -0
  2. package/README.md +29 -0
  3. package/dist/app-server-management.d.ts +3 -48
  4. package/dist/app-server-management.d.ts.map +1 -1
  5. package/dist/app-server-session.d.ts +0 -6
  6. package/dist/app-server-session.d.ts.map +1 -1
  7. package/dist/client-base.d.ts +7 -15
  8. package/dist/client-base.d.ts.map +1 -1
  9. package/dist/client-entry.js +979 -1010
  10. package/dist/client-entry.js.map +15 -11
  11. package/dist/client.d.ts +3 -3
  12. package/dist/client.d.ts.map +1 -1
  13. package/dist/cloud-sandbox.d.ts +33 -0
  14. package/dist/cloud-sandbox.d.ts.map +1 -0
  15. package/dist/cloud-session.d.ts.map +1 -1
  16. package/dist/cloud-status-transport.d.ts +53 -0
  17. package/dist/cloud-status-transport.d.ts.map +1 -0
  18. package/dist/index.d.ts +7 -16
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +3242 -4298
  21. package/dist/index.js.map +20 -17
  22. package/dist/local-app-server-session.d.ts +1 -1
  23. package/dist/local-app-server-session.d.ts.map +1 -1
  24. package/dist/local-app-server.d.ts +11 -0
  25. package/dist/local-app-server.d.ts.map +1 -1
  26. package/dist/management.d.ts +0 -7
  27. package/dist/management.d.ts.map +1 -1
  28. package/dist/remote-client-session-core.d.ts +6 -93
  29. package/dist/remote-client-session-core.d.ts.map +1 -1
  30. package/dist/remote-session-protocol.d.ts +132 -0
  31. package/dist/remote-session-protocol.d.ts.map +1 -0
  32. package/dist/remote-turn-coordinator.d.ts +49 -0
  33. package/dist/remote-turn-coordinator.d.ts.map +1 -0
  34. package/dist/types.d.ts +59 -86
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/validation.d.ts.map +1 -1
  37. package/package.json +6 -3
  38. package/src/app-server-management.ts +405 -0
  39. package/src/app-server-session.ts +916 -0
  40. package/src/cli-resolver.ts +46 -0
  41. package/src/client-base.ts +456 -0
  42. package/src/client-entry.ts +31 -0
  43. package/src/client.ts +99 -0
  44. package/src/cloud-management.ts +360 -0
  45. package/src/cloud-sandbox.ts +117 -0
  46. package/src/cloud-session.ts +1055 -0
  47. package/src/cloud-status-transport.ts +305 -0
  48. package/src/index.ts +422 -0
  49. package/src/interactiveToolPolicy.ts +62 -0
  50. package/src/local-app-server-session.ts +49 -0
  51. package/src/local-app-server.ts +231 -0
  52. package/src/management-types.ts +133 -0
  53. package/src/management.ts +199 -0
  54. package/src/remote-client-session-core.ts +786 -0
  55. package/src/remote-session-protocol.ts +674 -0
  56. package/src/remote-turn-coordinator.ts +523 -0
  57. package/src/remote.ts +177 -0
  58. package/src/repositories.ts +340 -0
  59. package/src/request-ids.ts +33 -0
  60. package/src/stream-events.ts +88 -0
  61. package/src/tool-helpers.ts +147 -0
  62. package/src/types.ts +1281 -0
  63. package/src/validation.ts +238 -0
  64. package/src/websocket.ts +22 -0
  65. package/dist/protocol.d.ts +0 -205
  66. package/dist/protocol.d.ts.map +0 -1
  67. package/dist/session.d.ts +0 -155
  68. package/dist/session.d.ts.map +0 -1
  69. package/dist/transport.d.ts +0 -53
  70. package/dist/transport.d.ts.map +0 -1
@@ -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 resolveAppServerChannelUrl(url, channel) {
278
+ function resolveAppServerUrl(url) {
265
279
  const parsed = normalizeBaseUrl(url);
266
- parsed.searchParams.set("channel", channel);
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.control = new WebSocket(resolveAppServerChannelUrl(options.url, "control"), socketOptions);
398
- this.stream = new WebSocket(resolveAppServerChannelUrl(options.url, "stream"), socketOptions);
399
- attachSocketListener(this.control, "message", (event) => {
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.stream, "message", (event) => {
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 Promise.all([
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.control.close();
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.control.send(JSON.stringify(command));
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.clearIdleTimer();
871
- this.inFlightRequests += 1;
872
- try {
873
- const { client } = await this.acquireConnection();
874
- const command = {
875
- type,
876
- request_id: client.nextRequestId(type),
877
- ...body
878
- };
879
- const response = await client.request(command, {
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 {} finally {
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
- const controlClosed = waitForSocketClose(connection.client.control);
1005
- try {
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-client-session-core.ts
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
- class RemoteClientSessionCore {
6110
- mode;
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
- initializePromise = null;
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
- toolNames;
6133
- deviceStatusListeners = new Set;
6134
- deviceStatusRefreshCancels = new Set;
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
- async initialize() {
6141
- if (this.closed) {
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
- async send(message) {
6209
- if (!this.initialized) {
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
- async runTurn(message, _options = {}) {
6227
- if (this.activeTurn || this.pendingTurns.length > 0) {
6228
- throw new Error(`A turn is already in flight for this ${this.label} session. Use send() and stream() to let the listener queue messages.`);
6229
- }
6230
- await this.send(message);
6231
- for await (const msg of this.stream()) {
6232
- if (msg.type === "result") {
6233
- return msg;
6234
- }
6235
- }
6236
- return {
6237
- type: "result",
6238
- success: false,
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
- async* stream() {
6248
- while (true) {
6249
- const msg = await this.nextMessage();
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
- async abort() {
6258
- if (!this.initialized)
6259
- return;
6260
- if (!this.controller || !this.runtime)
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
- async sendCommand(command, options) {
6267
- if (!this.initialized) {
6268
- await this.initialize();
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 (!this.controller) {
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
- if (this.activeTurn?.timeout)
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
- resolveAll(value) {
6993
- for (const resolve of this.streamResolvers.splice(0)) {
6994
- resolve(value);
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 validatePositiveInteger(value, name) {
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
- validatePositiveInteger(options.requestTimeoutMs, "requestTimeoutMs");
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: createCloudStatusWebSocketConstructor({
8370
- cloudOptions: this.cloudOptions,
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(conversationId ? { conversationId } : {})
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
- if (options.memfsStartup !== undefined) {
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 (localOptions.transport !== undefined && localOptions.transport !== "app-server" && localOptions.transport !== "stdio") {
9106
- throw new Error("Invalid local transport. Valid values: app-server, stdio.");
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(agentIdOrOptions, options = {}) {
9159
- const agentId = typeof agentIdOrOptions === "string" ? agentIdOrOptions : undefined;
9160
- const resolvedOptions = typeof agentIdOrOptions === "string" ? options : agentIdOrOptions ?? {};
9161
- this.assertSessionBackend("createSession", resolvedOptions);
9162
- const sessionOptions = stripCloudExecutionOptions(resolvedOptions);
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: resolvedOptions
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: resolvedOptions
9150
+ options
9184
9151
  });
9185
9152
  }
9186
- return this.createLocalSession(agentId, resolvedOptions, sessionOptions);
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, sessionOptions);
9189
+ return this.resumeLocalSession(id, options);
9223
9190
  }
9224
9191
  async prompt(message, agentId, options = {}) {
9225
- const session = agentId ? this.createSession(agentId, options) : this.createSession(options);
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 (!this.useLegacyLocalStdio()) {
9245
- assertRemoteSessionOptionsSupported(action, options);
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, _sessionOptions) {
9256
+ createLocalSession(_agentId, _options) {
9282
9257
  throw this.localBackendUnavailableError();
9283
9258
  }
9284
- resumeLocalSession(_id, _options, _sessionOptions) {
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=EACEDA2AA17A075B64756E2164756E21
9391
+ //# debugId=AAFE1DD6848E90C364756E2164756E21