@letta-ai/letta-agent-sdk 0.3.3 → 0.5.1

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 (56) hide show
  1. package/AGENTS.md +0 -3
  2. package/README.md +8 -1
  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 +428 -486
  10. package/dist/client-entry.js.map +13 -12
  11. package/dist/client.d.ts +3 -3
  12. package/dist/client.d.ts.map +1 -1
  13. package/dist/cloud-session.d.ts +2 -0
  14. package/dist/cloud-session.d.ts.map +1 -1
  15. package/dist/cloud-status-transport.d.ts +53 -0
  16. package/dist/cloud-status-transport.d.ts.map +1 -0
  17. package/dist/index.d.ts +7 -16
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +911 -1993
  20. package/dist/index.js.map +18 -18
  21. package/dist/local-app-server-session.d.ts +1 -1
  22. package/dist/local-app-server-session.d.ts.map +1 -1
  23. package/dist/local-app-server.d.ts +11 -0
  24. package/dist/local-app-server.d.ts.map +1 -1
  25. package/dist/management.d.ts +0 -7
  26. package/dist/management.d.ts.map +1 -1
  27. package/dist/remote-session-protocol.d.ts +2 -0
  28. package/dist/remote-session-protocol.d.ts.map +1 -1
  29. package/dist/remote-turn-coordinator.d.ts.map +1 -1
  30. package/dist/types.d.ts +65 -66
  31. package/dist/types.d.ts.map +1 -1
  32. package/dist/validation.d.ts.map +1 -1
  33. package/package.json +2 -2
  34. package/src/app-server-management.ts +20 -256
  35. package/src/app-server-session.ts +0 -32
  36. package/src/client-base.ts +47 -73
  37. package/src/client.ts +31 -70
  38. package/src/cloud-session.ts +113 -287
  39. package/src/cloud-status-transport.ts +305 -0
  40. package/src/index.ts +11 -29
  41. package/src/local-app-server-session.ts +13 -3
  42. package/src/local-app-server.ts +97 -3
  43. package/src/management.ts +0 -7
  44. package/src/remote-session-protocol.ts +18 -0
  45. package/src/remote-turn-coordinator.ts +20 -2
  46. package/src/types.ts +72 -111
  47. package/src/validation.ts +21 -6
  48. package/dist/protocol.d.ts +0 -205
  49. package/dist/protocol.d.ts.map +0 -1
  50. package/dist/session.d.ts +0 -155
  51. package/dist/session.d.ts.map +0 -1
  52. package/dist/transport.d.ts +0 -53
  53. package/dist/transport.d.ts.map +0 -1
  54. package/src/protocol.ts +0 -249
  55. package/src/session.ts +0 -1638
  56. package/src/transport.ts +0 -484
@@ -244,6 +244,18 @@ class RepositoriesClient {
244
244
  }
245
245
 
246
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
+ }
247
259
  var DEFAULT_REQUEST_TIMEOUT_MS = 30000;
248
260
  var WEBSOCKET_OPEN_STATE = 1;
249
261
  function getGlobalWebSocket() {
@@ -263,9 +275,9 @@ function normalizeBaseUrl(url) {
263
275
  }
264
276
  return parsed;
265
277
  }
266
- function resolveAppServerChannelUrl(url, channel) {
278
+ function resolveAppServerUrl(url) {
267
279
  const parsed = normalizeBaseUrl(url);
268
- parsed.searchParams.set("channel", channel);
280
+ parsed.searchParams.delete("channel");
269
281
  return parsed.toString();
270
282
  }
271
283
  function attachSocketListener(socket, type, listener) {
@@ -378,6 +390,7 @@ function streamDeltaErrorMessage(message) {
378
390
  }
379
391
 
380
392
  class AppServerClient {
393
+ socket;
381
394
  control;
382
395
  stream;
383
396
  requestTimeoutMs;
@@ -396,26 +409,18 @@ class AppServerClient {
396
409
  }
397
410
  this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
398
411
  const socketOptions = appServerSocketOptions(options.authToken);
399
- this.control = new WebSocket(resolveAppServerChannelUrl(options.url, "control"), socketOptions);
400
- this.stream = new WebSocket(resolveAppServerChannelUrl(options.url, "stream"), socketOptions);
401
- 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) => {
402
416
  this.handleMessage(event, "control");
403
417
  });
404
- attachSocketListener(this.stream, "message", (event) => {
405
- this.handleMessage(event, "stream");
406
- });
407
- attachSocketListener(this.control, "close", (event) => {
418
+ attachSocketListener(this.socket, "close", (event) => {
408
419
  this.handleDisconnect("control", event);
409
420
  });
410
- attachSocketListener(this.stream, "close", (event) => {
411
- this.handleDisconnect("stream", event);
412
- });
413
421
  }
414
422
  async connect() {
415
- await Promise.all([
416
- waitForSocketOpen(this.control),
417
- waitForSocketOpen(this.stream)
418
- ]);
423
+ await waitForSocketOpen(this.socket);
419
424
  return this;
420
425
  }
421
426
  close() {
@@ -423,8 +428,7 @@ class AppServerClient {
423
428
  return;
424
429
  this.explicitlyClosed = true;
425
430
  this.rejectAllPending("App-server client closed");
426
- this.control.close();
427
- this.stream.close();
431
+ this.socket.close();
428
432
  }
429
433
  onMessage(handler) {
430
434
  this.messageHandlers.add(handler);
@@ -443,10 +447,38 @@ class AppServerClient {
443
447
  return `${prefix}-${this.nextRequestNumber}`;
444
448
  }
445
449
  send(command) {
450
+ this.writeCommand(command);
451
+ }
452
+ writeCommand(command) {
446
453
  for (const handler of this.sendHandlers) {
447
454
  handler(command);
448
455
  }
449
- 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
+ });
450
482
  }
451
483
  request(commandOrType, bodyOrOptions = {}, maybeOptions = {}) {
452
484
  const isTypeRequest = typeof commandOrType === "string";
@@ -477,6 +509,15 @@ class AppServerClient {
477
509
  }
478
510
  });
479
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
+ }
480
521
  runtimeStart(command, options = {}) {
481
522
  return this.request({
482
523
  type: "runtime_start",
@@ -696,44 +737,6 @@ function applyUniqueRequestIds(client) {
696
737
  }
697
738
 
698
739
  // src/app-server-management.ts
699
- var DEFAULT_IDLE_LINGER_MS = 250;
700
- var registeredTransportsByUrl = new Map;
701
- function appServerUrlKey(value) {
702
- try {
703
- const url = new URL(value);
704
- if (url.protocol === "http:")
705
- url.protocol = "ws:";
706
- if (url.protocol === "https:")
707
- url.protocol = "wss:";
708
- url.hash = "";
709
- url.searchParams.delete("channel");
710
- return url.toString();
711
- } catch {
712
- return value;
713
- }
714
- }
715
- function registerManagementTransport(url, transport) {
716
- const key = appServerUrlKey(url);
717
- const registration = { transport };
718
- const transports = registeredTransportsByUrl.get(key) ?? new Set;
719
- transports.add(registration);
720
- registeredTransportsByUrl.set(key, transports);
721
- return () => {
722
- transports.delete(registration);
723
- if (transports.size === 0) {
724
- registeredTransportsByUrl.delete(key);
725
- }
726
- };
727
- }
728
- async function releaseAppServerManagementConnections(url) {
729
- const key = appServerUrlKey(url);
730
- while (true) {
731
- const transports = [...registeredTransportsByUrl.get(key) ?? []];
732
- if (transports.length === 0)
733
- return;
734
- await Promise.all(transports.map(({ transport }) => transport.releaseIdleConnection()));
735
- }
736
- }
737
740
  function ensureResponse(response, value, fallback) {
738
741
  if (!response.success || value == null) {
739
742
  throw new Error(response.error ?? fallback);
@@ -770,15 +773,9 @@ function stringRecord(value) {
770
773
  class AppServerManagementTransport {
771
774
  options;
772
775
  connectionPromise = null;
773
- releasePromise = null;
774
776
  closingConnections = new Set;
775
- inFlightRequests = 0;
776
- idleWaiters = new Set;
777
- idleTimer = null;
778
- idleLingerMs;
779
777
  constructor(options) {
780
778
  this.options = options;
781
- this.idleLingerMs = options.idleLingerMs ?? DEFAULT_IDLE_LINGER_MS;
782
779
  }
783
780
  async listAgents(query) {
784
781
  const response = await this.request("agent_list", { query }, "agent_list_response");
@@ -845,73 +842,20 @@ class AppServerManagementTransport {
845
842
  }
846
843
  return { messages: response.messages };
847
844
  }
848
- releaseIdleConnection() {
849
- this.clearIdleTimer();
850
- if (this.releasePromise)
851
- return this.releasePromise;
852
- const release = this.releaseConnectionWhenIdle();
853
- this.releasePromise = release;
854
- release.then(() => {
855
- if (this.releasePromise === release) {
856
- this.releasePromise = null;
857
- }
858
- }, () => {
859
- if (this.releasePromise === release) {
860
- this.releasePromise = null;
861
- }
862
- });
863
- return release;
864
- }
865
845
  async request(type, body, responseType) {
866
- if (this.releasePromise) {
867
- await this.releasePromise;
868
- }
869
846
  if (this.closingConnections.size > 0) {
870
847
  await Promise.all([...this.closingConnections]);
871
848
  }
872
- this.clearIdleTimer();
873
- this.inFlightRequests += 1;
874
- try {
875
- const { client } = await this.acquireConnection();
876
- const command = {
877
- type,
878
- request_id: client.nextRequestId(type),
879
- ...body
880
- };
881
- const response = await client.request(command, {
882
- predicate: (message) => message.type === responseType
883
- });
884
- return response;
885
- } finally {
886
- this.inFlightRequests -= 1;
887
- if (this.inFlightRequests === 0) {
888
- const waiters = [...this.idleWaiters];
889
- this.idleWaiters.clear();
890
- for (const resolve of waiters)
891
- resolve();
892
- if (!this.releasePromise) {
893
- this.scheduleIdleRelease();
894
- }
895
- }
896
- }
897
- }
898
- async releaseConnectionWhenIdle() {
899
- if (this.inFlightRequests > 0) {
900
- await new Promise((resolve) => {
901
- this.idleWaiters.add(resolve);
902
- });
903
- }
904
- this.clearIdleTimer();
905
- const promise = this.connectionPromise;
906
- try {
907
- if (promise) {
908
- this.connectionPromise = null;
909
- await this.trackClosingConnection(await promise);
910
- }
911
- if (this.closingConnections.size > 0) {
912
- await Promise.all([...this.closingConnections]);
913
- }
914
- } 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;
915
859
  }
916
860
  acquireConnection() {
917
861
  if (this.connectionPromise)
@@ -936,7 +880,6 @@ class AppServerManagementTransport {
936
880
  if (!url) {
937
881
  throw new Error("App-server management requires a url or connect hook.");
938
882
  }
939
- const unregister = registerManagementTransport(url, this);
940
883
  let client = null;
941
884
  try {
942
885
  client = applyUniqueRequestIds(createAppServerClient({
@@ -951,29 +894,23 @@ class AppServerManagementTransport {
951
894
  } catch (error) {
952
895
  try {
953
896
  if (client) {
954
- const controlClosed = waitForSocketClose(client.control);
955
897
  client.close();
956
898
  ownedConnection?.close();
957
- await controlClosed;
958
899
  } else {
959
900
  ownedConnection?.close();
960
901
  }
961
- } catch {} finally {
962
- unregister();
963
- }
902
+ } catch {}
964
903
  throw error;
965
904
  }
966
905
  return {
967
906
  client,
968
907
  ownedConnection,
969
- detachDisconnect: () => {},
970
- unregister
908
+ detachDisconnect: () => {}
971
909
  };
972
910
  }
973
911
  discardConnection(promise, connection) {
974
912
  if (this.connectionPromise === promise) {
975
913
  this.connectionPromise = null;
976
- this.clearIdleTimer();
977
914
  }
978
915
  this.trackClosingConnection(connection);
979
916
  }
@@ -983,67 +920,11 @@ class AppServerManagementTransport {
983
920
  closing.then(() => this.closingConnections.delete(closing), () => this.closingConnections.delete(closing));
984
921
  return closing;
985
922
  }
986
- scheduleIdleRelease() {
987
- if (!this.connectionPromise)
988
- return;
989
- this.clearIdleTimer();
990
- const timer = setTimeout(() => {
991
- this.idleTimer = null;
992
- this.releaseIdleConnection();
993
- }, this.idleLingerMs);
994
- this.idleTimer = timer;
995
- timer.unref?.();
996
- }
997
- clearIdleTimer() {
998
- if (this.idleTimer === null)
999
- return;
1000
- clearTimeout(this.idleTimer);
1001
- this.idleTimer = null;
1002
- }
1003
923
  }
1004
924
  async function closeConnection(connection) {
1005
925
  connection.detachDisconnect();
1006
- const controlClosed = waitForSocketClose(connection.client.control);
1007
- try {
1008
- connection.client.close();
1009
- connection.ownedConnection?.close();
1010
- await controlClosed;
1011
- } finally {
1012
- connection.unregister();
1013
- }
1014
- }
1015
- function waitForSocketClose(socket) {
1016
- if (socket.readyState === 3)
1017
- return Promise.resolve();
1018
- return new Promise((resolve) => {
1019
- let settled = false;
1020
- let detach = () => {};
1021
- const finish = () => {
1022
- if (settled)
1023
- return;
1024
- settled = true;
1025
- clearTimeout(timeout);
1026
- detach();
1027
- resolve();
1028
- };
1029
- if (socket.addEventListener) {
1030
- socket.addEventListener("close", finish);
1031
- detach = () => socket.removeEventListener?.("close", finish);
1032
- } else if (socket.once) {
1033
- socket.once("close", finish);
1034
- detach = () => socket.off?.("close", finish);
1035
- } else if (socket.on) {
1036
- socket.on("close", finish);
1037
- detach = () => socket.off?.("close", finish);
1038
- } else {
1039
- resolve();
1040
- return;
1041
- }
1042
- const timeout = setTimeout(finish, 1000);
1043
- timeout.unref?.();
1044
- if (socket.readyState === 3)
1045
- finish();
1046
- });
926
+ connection.client.close();
927
+ connection.ownedConnection?.close();
1047
928
  }
1048
929
 
1049
930
  // node_modules/@letta-ai/letta-code/dist/agent-presets.js
@@ -5950,6 +5831,12 @@ function streamDeltaMessageType2(delta) {
5950
5831
  function streamDeltaRunId2(delta) {
5951
5832
  return typeof delta.run_id === "string" ? delta.run_id : undefined;
5952
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
+ }
5953
5840
  function streamDeltaStopReason2(delta) {
5954
5841
  return typeof delta.stop_reason === "string" ? delta.stop_reason : undefined;
5955
5842
  }
@@ -6100,6 +5987,7 @@ function toSessionDeviceStatus(status) {
6100
5987
  isProcessing: status.is_processing,
6101
5988
  permissionMode,
6102
5989
  workingDirectory: status.current_working_directory,
5990
+ memoryDirectory: typeof status.memory_directory === "string" ? status.memory_directory : null,
6103
5991
  pendingControlRequests: pendingControlRequests(status),
6104
5992
  raw: { ...status }
6105
5993
  };
@@ -6359,6 +6247,8 @@ class RemoteTurnCoordinator {
6359
6247
  transformStreamDelta(delta) {
6360
6248
  const messageType = typeof delta.message_type === "string" ? delta.message_type : undefined;
6361
6249
  const runId = typeof delta.run_id === "string" ? delta.run_id : undefined;
6250
+ const otid = streamDeltaOtid(delta);
6251
+ const seqId = streamDeltaSeqId(delta);
6362
6252
  const uuid = typeof delta.id === "string" ? delta.id : `${this.label}-${++this.messageCounter}`;
6363
6253
  if (messageType === "assistant_message") {
6364
6254
  const content = extractTextFromContent(delta.content);
@@ -6366,13 +6256,27 @@ class RemoteTurnCoordinator {
6366
6256
  return null;
6367
6257
  if (this.activeTurn)
6368
6258
  this.activeTurn.assistantText += content;
6369
- return { type: "assistant", content, uuid, runId };
6259
+ return {
6260
+ type: "assistant",
6261
+ content,
6262
+ uuid,
6263
+ ...otid !== undefined ? { otid } : {},
6264
+ ...seqId !== undefined ? { seqId } : {},
6265
+ runId
6266
+ };
6370
6267
  }
6371
6268
  if (messageType === "reasoning_message") {
6372
6269
  const content = typeof delta.reasoning === "string" ? delta.reasoning : extractTextFromContent(delta.content);
6373
6270
  if (!content)
6374
6271
  return null;
6375
- return { type: "reasoning", content, uuid, runId };
6272
+ return {
6273
+ type: "reasoning",
6274
+ content,
6275
+ uuid,
6276
+ ...otid !== undefined ? { otid } : {},
6277
+ ...seqId !== undefined ? { seqId } : {},
6278
+ runId
6279
+ };
6376
6280
  }
6377
6281
  if (messageType === "tool_call_message" || messageType === "approval_request_message") {
6378
6282
  const toolCall = firstToolCall(delta);
@@ -7075,26 +6979,6 @@ function assertRemoteCreateAgentOptionsSupported(options) {
7075
6979
  throw new Error("App-server createAgent() does not yet support dreaming.behavior overrides.");
7076
6980
  }
7077
6981
  }
7078
- function assertRemoteSessionOptionsSupported(action, options) {
7079
- if (options.systemPrompt !== undefined) {
7080
- throw new Error(`App-server ${action}() does not yet support systemPrompt overrides for existing agents.`);
7081
- }
7082
- if (options.disallowedTools !== undefined) {
7083
- throw new Error(`App-server ${action}() does not yet support disallowedTools.`);
7084
- }
7085
- if (options.systemInfoReminder !== undefined) {
7086
- throw new Error(`App-server ${action}() does not yet support systemInfoReminder overrides.`);
7087
- }
7088
- if (options.dreaming?.behavior !== undefined) {
7089
- throw new Error(`App-server ${action}() does not yet support dreaming.behavior overrides.`);
7090
- }
7091
- if (options.memfsStartup !== undefined) {
7092
- throw new Error(`App-server ${action}() does not support memfsStartup.`);
7093
- }
7094
- if (options.includePartialMessages !== undefined) {
7095
- throw new Error(`App-server ${action}() streams app-server deltas directly and does not support includePartialMessages.`);
7096
- }
7097
- }
7098
6982
  function normalizeMemoryBlock(block) {
7099
6983
  const normalized = { ...block };
7100
6984
  if (normalized.value === undefined && typeof normalized.content === "string") {
@@ -7525,9 +7409,7 @@ class AppServerSession extends RemoteClientSessionCore {
7525
7409
  return options.memfs !== false;
7526
7410
  }
7527
7411
  async initializeRuntimeController() {
7528
- await this.remoteOptions.beforeConnect?.();
7529
7412
  const url = await this.resolveAppServerUrl();
7530
- await releaseAppServerManagementConnections(url);
7531
7413
  const client = applyUniqueRequestIds(createAppServerClient({
7532
7414
  url,
7533
7415
  ...this.remoteOptions.authToken !== undefined ? { authToken: this.remoteOptions.authToken } : {},
@@ -7837,6 +7719,222 @@ class CloudManagementTransport {
7837
7719
  }
7838
7720
  }
7839
7721
 
7722
+ // src/cloud-status-transport.ts
7723
+ var CONNECTING = 0;
7724
+ var OPEN = 1;
7725
+ var CLOSING = 2;
7726
+ var CLOSED = 3;
7727
+ var MAX_IDEMPOTENCY_KEYS = 1000;
7728
+ function addSocketListener(socket, type, listener) {
7729
+ if (socket.addEventListener) {
7730
+ socket.addEventListener(type, listener);
7731
+ return () => socket.removeEventListener?.(type, listener);
7732
+ }
7733
+ if (socket.on) {
7734
+ socket.on(type, listener);
7735
+ return () => socket.off?.(type, listener);
7736
+ }
7737
+ throw new Error("WebSocket implementation does not support event listeners.");
7738
+ }
7739
+ function messageEventData(event) {
7740
+ if (typeof event === "string")
7741
+ return event;
7742
+ if (!event || typeof event !== "object")
7743
+ return null;
7744
+ const data = event.data;
7745
+ if (typeof data === "string")
7746
+ return data;
7747
+ if (data instanceof ArrayBuffer) {
7748
+ return new TextDecoder().decode(data);
7749
+ }
7750
+ if (data instanceof Uint8Array) {
7751
+ return new TextDecoder().decode(data);
7752
+ }
7753
+ return null;
7754
+ }
7755
+ function channelUrl(url, channel) {
7756
+ const parsed = new URL(url);
7757
+ parsed.searchParams.set("channel", channel);
7758
+ return parsed.toString();
7759
+ }
7760
+
7761
+ class CloudStatusTransport {
7762
+ options;
7763
+ controlSocket;
7764
+ streamSocket;
7765
+ state = CONNECTING;
7766
+ openedChannels = new Set;
7767
+ listeners = new Map;
7768
+ removers = [];
7769
+ seenIdempotencyKeys = new Set;
7770
+ seenIdempotencyOrder = [];
7771
+ lastEventSeq = null;
7772
+ pingTimer = null;
7773
+ closeEmitted = false;
7774
+ constructor(options) {
7775
+ this.options = options;
7776
+ const socketOptions = options.headers ? { headers: options.headers } : undefined;
7777
+ this.controlSocket = new options.WebSocket(channelUrl(options.url, "control"), socketOptions);
7778
+ this.streamSocket = new options.WebSocket(channelUrl(options.url, "stream"), socketOptions);
7779
+ this.bindSocket("control", this.controlSocket);
7780
+ this.bindSocket("stream", this.streamSocket);
7781
+ this.startPing();
7782
+ }
7783
+ get readyState() {
7784
+ return this.state;
7785
+ }
7786
+ send(data) {
7787
+ if (this.state !== OPEN || this.controlSocket.readyState !== OPEN) {
7788
+ throw new Error("Cloud status control socket is not open");
7789
+ }
7790
+ this.controlSocket.send(data);
7791
+ }
7792
+ close() {
7793
+ if (this.state === CLOSED || this.state === CLOSING)
7794
+ return;
7795
+ this.state = CLOSING;
7796
+ this.stopPing();
7797
+ this.closeUnderlyingSockets();
7798
+ this.finishClose({});
7799
+ }
7800
+ addEventListener(type, listener) {
7801
+ let listeners = this.listeners.get(type);
7802
+ if (!listeners) {
7803
+ listeners = new Set;
7804
+ this.listeners.set(type, listeners);
7805
+ }
7806
+ listeners.add(listener);
7807
+ }
7808
+ removeEventListener(type, listener) {
7809
+ this.listeners.get(type)?.delete(listener);
7810
+ }
7811
+ bindSocket(channel, socket) {
7812
+ this.removers.push(addSocketListener(socket, "open", () => {
7813
+ this.openedChannels.add(channel);
7814
+ if (this.openedChannels.size === 2 && this.state === CONNECTING) {
7815
+ this.state = OPEN;
7816
+ this.emit("open", {});
7817
+ }
7818
+ }), addSocketListener(socket, "message", (event) => {
7819
+ if (this.shouldForwardMessage(channel, socket, event)) {
7820
+ this.emit("message", event);
7821
+ }
7822
+ }), addSocketListener(socket, "error", (event) => {
7823
+ this.emit("error", event);
7824
+ }), addSocketListener(socket, "close", (event) => {
7825
+ if (this.state !== CLOSED) {
7826
+ this.state = CLOSING;
7827
+ this.stopPing();
7828
+ this.closeUnderlyingSockets(socket);
7829
+ this.finishClose(event);
7830
+ }
7831
+ }));
7832
+ }
7833
+ shouldForwardMessage(channel, socket, event) {
7834
+ const data = messageEventData(event);
7835
+ if (!data)
7836
+ return true;
7837
+ let message;
7838
+ try {
7839
+ message = JSON.parse(data);
7840
+ } catch {
7841
+ return true;
7842
+ }
7843
+ this.ackIfSequenced(socket, message);
7844
+ if (channel === "control" && message.type === "stream_delta") {
7845
+ return false;
7846
+ }
7847
+ if (this.isDuplicate(message))
7848
+ return false;
7849
+ this.trackEventSequence(message);
7850
+ return true;
7851
+ }
7852
+ ackIfSequenced(socket, message) {
7853
+ if (typeof message.seq !== "number")
7854
+ return;
7855
+ this.sendCommand(socket, { type: "ack", seq: message.seq });
7856
+ }
7857
+ isDuplicate(message) {
7858
+ const key = typeof message.idempotency_key === "string" ? message.idempotency_key : null;
7859
+ if (!key)
7860
+ return false;
7861
+ if (this.seenIdempotencyKeys.has(key))
7862
+ return true;
7863
+ this.seenIdempotencyKeys.add(key);
7864
+ this.seenIdempotencyOrder.push(key);
7865
+ while (this.seenIdempotencyOrder.length > MAX_IDEMPOTENCY_KEYS) {
7866
+ const oldest = this.seenIdempotencyOrder.shift();
7867
+ if (oldest)
7868
+ this.seenIdempotencyKeys.delete(oldest);
7869
+ }
7870
+ return false;
7871
+ }
7872
+ trackEventSequence(message) {
7873
+ if (typeof message.event_seq !== "number")
7874
+ return;
7875
+ if (this.lastEventSeq !== null && message.event_seq > this.lastEventSeq + 1) {
7876
+ this.sendCommand(this.controlSocket, {
7877
+ type: "sync",
7878
+ runtime: this.options.runtime,
7879
+ recover_approvals: true,
7880
+ force_device_status: true
7881
+ });
7882
+ }
7883
+ if (this.lastEventSeq === null || message.event_seq > this.lastEventSeq) {
7884
+ this.lastEventSeq = message.event_seq;
7885
+ }
7886
+ }
7887
+ sendCommand(socket, command) {
7888
+ if (socket.readyState !== OPEN)
7889
+ return;
7890
+ try {
7891
+ socket.send(JSON.stringify(command));
7892
+ } catch {}
7893
+ }
7894
+ startPing() {
7895
+ this.pingTimer = setInterval(() => {
7896
+ this.sendCommand(this.controlSocket, { type: "ping" });
7897
+ this.sendCommand(this.streamSocket, { type: "ping" });
7898
+ }, this.options.pingIntervalMs);
7899
+ this.pingTimer.unref?.();
7900
+ }
7901
+ stopPing() {
7902
+ if (!this.pingTimer)
7903
+ return;
7904
+ clearInterval(this.pingTimer);
7905
+ this.pingTimer = null;
7906
+ }
7907
+ closeUnderlyingSockets(except) {
7908
+ for (const socket of [this.controlSocket, this.streamSocket]) {
7909
+ if (socket !== except && (socket.readyState === CONNECTING || socket.readyState === OPEN)) {
7910
+ socket.close();
7911
+ }
7912
+ }
7913
+ }
7914
+ finishClose(event) {
7915
+ if (this.closeEmitted)
7916
+ return;
7917
+ this.closeEmitted = true;
7918
+ this.state = CLOSED;
7919
+ this.stopPing();
7920
+ for (const remove of this.removers.splice(0))
7921
+ remove();
7922
+ this.emit("close", event);
7923
+ }
7924
+ emit(type, event) {
7925
+ for (const listener of this.listeners.get(type) ?? []) {
7926
+ listener(event);
7927
+ }
7928
+ }
7929
+ }
7930
+ function createCloudStatusTransportConstructor(options) {
7931
+ return class CloudStatusTransportSocket extends CloudStatusTransport {
7932
+ constructor(_url, _socketOptions) {
7933
+ super(options);
7934
+ }
7935
+ };
7936
+ }
7937
+
7840
7938
  // src/remote.ts
7841
7939
  function getDefaultApiKey2() {
7842
7940
  if (typeof process === "undefined") {
@@ -8145,191 +8243,6 @@ function buildCloudStatusWebSocketUrl(params) {
8145
8243
  }
8146
8244
  return base.toString();
8147
8245
  }
8148
- function addSocketListener(socket, type, listener) {
8149
- if (socket.addEventListener) {
8150
- socket.addEventListener(type, listener);
8151
- return () => socket.removeEventListener?.(type, listener);
8152
- }
8153
- if (socket.on) {
8154
- socket.on(type, listener);
8155
- return () => socket.off?.(type, listener);
8156
- }
8157
- throw new Error("WebSocket implementation does not support event listeners.");
8158
- }
8159
- function messageEventData(event) {
8160
- if (typeof event === "string")
8161
- return event;
8162
- if (event && typeof event === "object") {
8163
- const data = event.data;
8164
- if (typeof data === "string")
8165
- return data;
8166
- if (data instanceof ArrayBuffer) {
8167
- return new TextDecoder().decode(data);
8168
- }
8169
- if (data instanceof Uint8Array) {
8170
- return new TextDecoder().decode(data);
8171
- }
8172
- }
8173
- return null;
8174
- }
8175
- function createCloudStatusWebSocketConstructor(params) {
8176
- const WebSocketCtor = getWebSocketConstructor(params.cloudOptions.WebSocket);
8177
- const authMode = params.cloudOptions.webSocketAuth ?? "header";
8178
- const cloudHeaders3 = authMode === "header" ? cloudWebSocketHeaders(params.cloudOptions) : undefined;
8179
- const pingIntervalMs = params.cloudOptions.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS;
8180
- const state = {
8181
- runtime: params.runtime,
8182
- seenIdempotencyKeys: new Set,
8183
- seenIdempotencyOrder: [],
8184
- lastEventSeq: null,
8185
- controlSocket: null
8186
- };
8187
-
8188
- class CloudStatusSocketAdapter {
8189
- socket;
8190
- channel;
8191
- listenerRemovers = new Map;
8192
- pingTimer = null;
8193
- removeCloseListener = null;
8194
- constructor(url, options) {
8195
- this.channel = new URL(url).searchParams.get("channel");
8196
- const mergedHeaders = authMode === "header" ? {
8197
- ...options?.headers ?? {},
8198
- ...cloudHeaders3 ?? {}
8199
- } : undefined;
8200
- const socketOptions = mergedHeaders && Object.keys(mergedHeaders).length > 0 ? { headers: mergedHeaders } : undefined;
8201
- this.socket = new WebSocketCtor(url, socketOptions);
8202
- if (this.channel === "control") {
8203
- state.controlSocket = this;
8204
- }
8205
- this.removeCloseListener = addSocketListener(this.socket, "close", () => {
8206
- this.stopPing();
8207
- if (state.controlSocket === this)
8208
- state.controlSocket = null;
8209
- });
8210
- this.startPing(pingIntervalMs);
8211
- }
8212
- get readyState() {
8213
- return this.socket.readyState;
8214
- }
8215
- send(data) {
8216
- this.socket.send(data);
8217
- }
8218
- close() {
8219
- this.stopPing();
8220
- this.removeCloseListener?.();
8221
- this.removeCloseListener = null;
8222
- if (state.controlSocket === this)
8223
- state.controlSocket = null;
8224
- this.socket.close();
8225
- }
8226
- addEventListener(type, listener) {
8227
- const wrapped = type === "message" ? (event) => {
8228
- if (this.handleIncomingMessage(event)) {
8229
- listener(event);
8230
- }
8231
- } : listener;
8232
- const remove = addSocketListener(this.socket, type, wrapped);
8233
- let typeRemovers = this.listenerRemovers.get(type);
8234
- if (!typeRemovers) {
8235
- typeRemovers = new Map;
8236
- this.listenerRemovers.set(type, typeRemovers);
8237
- }
8238
- typeRemovers.set(listener, remove);
8239
- }
8240
- removeEventListener(type, listener) {
8241
- const typeRemovers = this.listenerRemovers.get(type);
8242
- const remove = typeRemovers?.get(listener);
8243
- if (!remove)
8244
- return;
8245
- remove();
8246
- typeRemovers?.delete(listener);
8247
- if (typeRemovers?.size === 0) {
8248
- this.listenerRemovers.delete(type);
8249
- }
8250
- }
8251
- handleIncomingMessage(event) {
8252
- const data = messageEventData(event);
8253
- if (!data)
8254
- return true;
8255
- let message;
8256
- try {
8257
- message = JSON.parse(data);
8258
- } catch {
8259
- return true;
8260
- }
8261
- this.ackIfSequenced(message);
8262
- if (this.channel === "control" && message.type === "stream_delta") {
8263
- return false;
8264
- }
8265
- if (this.isDuplicate(message))
8266
- return false;
8267
- this.trackEventSeq(message);
8268
- return true;
8269
- }
8270
- ackIfSequenced(message) {
8271
- if (typeof message.seq !== "number")
8272
- return;
8273
- this.sendCloudCommand({ type: "ack", seq: message.seq });
8274
- }
8275
- isDuplicate(message) {
8276
- const idempotencyKey = typeof message.idempotency_key === "string" ? message.idempotency_key : undefined;
8277
- if (!idempotencyKey)
8278
- return false;
8279
- if (state.seenIdempotencyKeys.has(idempotencyKey))
8280
- return true;
8281
- state.seenIdempotencyKeys.add(idempotencyKey);
8282
- state.seenIdempotencyOrder.push(idempotencyKey);
8283
- while (state.seenIdempotencyOrder.length > 1000) {
8284
- const oldest = state.seenIdempotencyOrder.shift();
8285
- if (oldest)
8286
- state.seenIdempotencyKeys.delete(oldest);
8287
- }
8288
- return false;
8289
- }
8290
- trackEventSeq(message) {
8291
- if (typeof message.event_seq !== "number")
8292
- return;
8293
- if (state.lastEventSeq !== null && message.event_seq > state.lastEventSeq + 1) {
8294
- this.sendSync(true);
8295
- }
8296
- if (state.lastEventSeq === null || message.event_seq > state.lastEventSeq) {
8297
- state.lastEventSeq = message.event_seq;
8298
- }
8299
- }
8300
- sendSync(recoverApprovals) {
8301
- const target = state.controlSocket?.readyState === 1 ? state.controlSocket : this;
8302
- target.sendCloudCommand({
8303
- type: "sync",
8304
- runtime: state.runtime,
8305
- recover_approvals: recoverApprovals,
8306
- force_device_status: true
8307
- });
8308
- }
8309
- sendCloudCommand(command) {
8310
- if (this.readyState !== 1)
8311
- return;
8312
- try {
8313
- this.socket.send(JSON.stringify(command));
8314
- } catch {}
8315
- }
8316
- startPing(intervalMs) {
8317
- if (this.pingTimer)
8318
- return;
8319
- this.pingTimer = setInterval(() => {
8320
- this.sendCloudCommand({ type: "ping" });
8321
- }, intervalMs);
8322
- this.pingTimer.unref?.();
8323
- }
8324
- stopPing() {
8325
- if (!this.pingTimer)
8326
- return;
8327
- clearInterval(this.pingTimer);
8328
- this.pingTimer = null;
8329
- }
8330
- }
8331
- return CloudStatusSocketAdapter;
8332
- }
8333
8246
  function isCloudConversation(value) {
8334
8247
  return Boolean(value && typeof value === "object" && typeof value.id === "string");
8335
8248
  }
@@ -8373,24 +8286,6 @@ function assertCloudSessionOptionsSupported(action, options) {
8373
8286
  if (options.environment !== undefined && options.sandbox !== undefined) {
8374
8287
  throw new Error(`Letta Cloud ${action}() cannot specify both environment and sandbox options.`);
8375
8288
  }
8376
- if (options.systemPrompt !== undefined) {
8377
- throw new Error(`Letta Cloud ${action}() cannot rewrite an existing agent's systemPrompt from the SDK adapter yet.`);
8378
- }
8379
- if (options.disallowedTools !== undefined) {
8380
- throw new Error(`Letta Cloud ${action}() has not wired disallowedTools to the remote device protocol yet.`);
8381
- }
8382
- if (options.systemInfoReminder !== undefined) {
8383
- throw new Error(`Letta Cloud ${action}() has not wired systemInfoReminder to the remote device protocol yet.`);
8384
- }
8385
- if (options.dreaming?.behavior !== undefined) {
8386
- throw new Error(`Letta Cloud ${action}() does not yet support dreaming.behavior overrides.`);
8387
- }
8388
- if (options.memfsStartup !== undefined) {
8389
- throw new Error(`Letta Cloud ${action}() does not support memfsStartup.`);
8390
- }
8391
- if (options.includePartialMessages !== undefined) {
8392
- throw new Error(`Letta Cloud ${action}() streams Remote Client deltas directly; includePartialMessages is not a separate toggle.`);
8393
- }
8394
8289
  }
8395
8290
 
8396
8291
  class CloudEnvironmentSession extends RemoteClientSessionCore {
@@ -8404,6 +8299,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
8404
8299
  sandboxRefreshInFlight = null;
8405
8300
  sandboxLifecycleClosing = false;
8406
8301
  attachedRepositoryIds = new Set;
8302
+ repositoryIdsRequiringCleanupRecompile = new Set;
8407
8303
  cloudMode;
8408
8304
  constructor(cloudOptions, mode) {
8409
8305
  super(mode, {
@@ -8417,7 +8313,10 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
8417
8313
  }
8418
8314
  async initializeRuntimeController() {
8419
8315
  const resolved = await this.resolveRuntime();
8420
- const connection = await this.resolveConnectionForRuntime(resolved.runtime);
8316
+ const connection = await this.resolveConnectionForRuntime(resolved.runtime).catch(async (error) => {
8317
+ await this.cleanupSessionRepositories(resolved.runtime.agent_id, resolved.runtime.conversation_id);
8318
+ throw error;
8319
+ });
8421
8320
  this.connectionId = connection.connectionId;
8422
8321
  const apiKey = getCloudApiKey2(this.cloudOptions);
8423
8322
  const url = buildCloudStatusWebSocketUrl({
@@ -8430,8 +8329,11 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
8430
8329
  });
8431
8330
  const client = applyUniqueRequestIds(createAppServerClient({
8432
8331
  url,
8433
- WebSocket: createCloudStatusWebSocketConstructor({
8434
- cloudOptions: this.cloudOptions,
8332
+ WebSocket: createCloudStatusTransportConstructor({
8333
+ url,
8334
+ WebSocket: getWebSocketConstructor(this.cloudOptions.WebSocket),
8335
+ ...(this.cloudOptions.webSocketAuth ?? "header") === "header" ? { headers: cloudWebSocketHeaders(this.cloudOptions) } : {},
8336
+ pingIntervalMs: this.cloudOptions.pingIntervalMs ?? DEFAULT_PING_INTERVAL_MS,
8435
8337
  runtime: resolved.runtime
8436
8338
  }),
8437
8339
  requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS
@@ -8469,7 +8371,7 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
8469
8371
  this.removeControlRequestHandler = null;
8470
8372
  client.close();
8471
8373
  await this.cleanupManagedSandbox();
8472
- await this.cleanupSessionRepositories(resolved.runtime.agent_id);
8374
+ await this.cleanupSessionRepositories(resolved.runtime.agent_id, resolved.runtime.conversation_id);
8473
8375
  throw error;
8474
8376
  }
8475
8377
  }
@@ -8520,8 +8422,9 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
8520
8422
  this.removeControlRequestHandler?.();
8521
8423
  this.removeControlRequestHandler = null;
8522
8424
  this.cleanupManagedSandbox();
8523
- if (this.runtime?.agent_id)
8524
- this.cleanupSessionRepositories(this.runtime.agent_id);
8425
+ if (this.runtime?.agent_id) {
8426
+ this.cleanupSessionRepositories(this.runtime.agent_id, this.runtime.conversation_id);
8427
+ }
8525
8428
  }
8526
8429
  async resolveRuntime() {
8527
8430
  let agentId = this.cloudMode.agentId;
@@ -8536,22 +8439,30 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
8536
8439
  if (!agentId) {
8537
8440
  throw new Error("Letta Cloud createSession()/resumeSession() requires an agent id or conversation id.");
8538
8441
  }
8539
- await this.attachSessionRepositories(agentId);
8540
- if (this.cloudMode.newConversation) {
8541
- const conversation = await this.createConversation(agentId);
8542
- conversationId = conversation.id;
8543
- } else if (this.cloudMode.defaultConversation) {
8544
- conversationId = "default";
8545
- }
8546
- if (!conversationId) {
8547
- throw new Error("Letta Cloud createSession()/resumeSession() requires an agent id or conversation id.");
8442
+ const shouldRecompileRepositories = await this.attachSessionRepositories(agentId);
8443
+ try {
8444
+ if (this.cloudMode.newConversation) {
8445
+ const conversation = await this.createConversation(agentId);
8446
+ conversationId = conversation.id;
8447
+ } else if (this.cloudMode.defaultConversation) {
8448
+ conversationId = "default";
8449
+ }
8450
+ if (!conversationId) {
8451
+ throw new Error("Letta Cloud createSession()/resumeSession() requires an agent id or conversation id.");
8452
+ }
8453
+ if (shouldRecompileRepositories) {
8454
+ await this.recompileSystemPrompt(agentId, conversationId);
8455
+ }
8456
+ return { runtime: { agent_id: agentId, conversation_id: conversationId } };
8457
+ } catch (error) {
8458
+ await this.cleanupSessionRepositories(agentId, conversationId);
8459
+ throw error;
8548
8460
  }
8549
- return { runtime: { agent_id: agentId, conversation_id: conversationId } };
8550
8461
  }
8551
8462
  async attachSessionRepositories(agentId) {
8552
8463
  const resources = this.repositoryResources();
8553
8464
  if (resources.length === 0)
8554
- return;
8465
+ return false;
8555
8466
  const existing = await this.listAgentRepositories(agentId);
8556
8467
  const existingIds = new Set(existing.map((repository) => repository.id));
8557
8468
  try {
@@ -8567,15 +8478,42 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
8567
8478
  await this.cleanupSessionRepositories(agentId);
8568
8479
  throw error;
8569
8480
  }
8481
+ const shouldRecompile = resources.some((resource) => resource.recompile !== false);
8482
+ this.repositoryIdsRequiringCleanupRecompile = new Set(resources.filter((resource) => resource.recompile !== false && this.attachedRepositoryIds.has(resource.repositoryId)).map((resource) => resource.repositoryId));
8483
+ return shouldRecompile;
8570
8484
  }
8571
- async cleanupSessionRepositories(agentId) {
8485
+ async cleanupSessionRepositories(agentId, conversationId) {
8572
8486
  const repositoryIds = [...this.attachedRepositoryIds];
8487
+ const repositoryIdsRequiringRecompile = this.repositoryIdsRequiringCleanupRecompile;
8573
8488
  this.attachedRepositoryIds.clear();
8574
- await Promise.all(repositoryIds.map(async (repositoryId) => {
8489
+ this.repositoryIdsRequiringCleanupRecompile = new Set;
8490
+ const recompileAfterDetach = await Promise.all(repositoryIds.map(async (repositoryId) => {
8575
8491
  try {
8576
8492
  await this.unlinkAgentRepository(agentId, repositoryId);
8577
- } catch {}
8493
+ return repositoryIdsRequiringRecompile.has(repositoryId);
8494
+ } catch {
8495
+ return false;
8496
+ }
8578
8497
  }));
8498
+ if (recompileAfterDetach.some(Boolean)) {
8499
+ try {
8500
+ await this.recompileSystemPrompt(agentId, conversationId);
8501
+ } catch {}
8502
+ }
8503
+ }
8504
+ async recompileSystemPrompt(agentId, conversationId) {
8505
+ const fetchImpl = getFetch3(this.cloudOptions.fetch);
8506
+ const baseUrl = normalizeCloudApiBaseUrl2(this.cloudOptions.apiBaseUrl);
8507
+ const usesAgentPrompt = !conversationId || conversationId === "default";
8508
+ const path = usesAgentPrompt ? `/v1/agents/${encodeURIComponent(agentId)}/recompile` : `/v1/conversations/${encodeURIComponent(conversationId)}/recompile`;
8509
+ const url = `${baseUrl}${path}`;
8510
+ const response = await fetchImpl(url, {
8511
+ method: "POST",
8512
+ headers: cloudHeaders2(this.cloudOptions),
8513
+ body: JSON.stringify(usesAgentPrompt ? {} : { agent_id: agentId })
8514
+ });
8515
+ const body = await parseJsonResponse3(response);
8516
+ assertOkResponse2(response, body, "Cloud recompile system prompt", url);
8579
8517
  }
8580
8518
  repositoryResources() {
8581
8519
  const resources = this.cloudMode.options.resources ?? [];
@@ -9019,7 +8957,23 @@ function validateApprovalRecoveryOptions(options) {
9019
8957
  }
9020
8958
  }
9021
8959
  function validateRemovedSessionOptions(options) {
9022
- if (options.memfsStartup !== undefined) {
8960
+ const removedOptions = options;
8961
+ if (removedOptions.systemPrompt !== undefined) {
8962
+ throw new Error("systemPrompt is not supported when opening an existing agent session.");
8963
+ }
8964
+ if (removedOptions.disallowedTools !== undefined) {
8965
+ throw new Error("disallowedTools is not supported when opening an existing agent session.");
8966
+ }
8967
+ if (removedOptions.systemInfoReminder !== undefined) {
8968
+ throw new Error("systemInfoReminder is not supported when opening an existing agent session.");
8969
+ }
8970
+ if (removedOptions.includePartialMessages !== undefined) {
8971
+ throw new Error("includePartialMessages is not supported by app-server sessions.");
8972
+ }
8973
+ if (options.dreaming && "behavior" in options.dreaming && options.dreaming.behavior !== undefined) {
8974
+ throw new Error("dreaming.behavior is not supported when opening an existing agent session.");
8975
+ }
8976
+ if (removedOptions.memfsStartup !== undefined) {
9023
8977
  throw new Error("memfsStartup is not supported by the SDK.");
9024
8978
  }
9025
8979
  }
@@ -9069,9 +9023,6 @@ function validateReasoningEffort(value) {
9069
9023
  }
9070
9024
  }
9071
9025
  function validateCreateSessionOptions(options) {
9072
- if (options.systemPrompt !== undefined) {
9073
- validateSystemPromptPreset(options.systemPrompt);
9074
- }
9075
9026
  validateSkillSources(options.skillSources);
9076
9027
  validateReasoningEffort(options.reasoningEffort);
9077
9028
  validateDreamingOptions(options.dreaming);
@@ -9127,6 +9078,7 @@ function stripCloudExecutionOptions(options) {
9127
9078
  const sessionOptions = { ...options };
9128
9079
  delete sessionOptions.environment;
9129
9080
  delete sessionOptions.sandbox;
9081
+ delete sessionOptions.filesystemConfinement;
9130
9082
  return sessionOptions;
9131
9083
  }
9132
9084
  function hasRepositoryResources(options) {
@@ -9170,17 +9122,13 @@ class LettaAgentClientBase {
9170
9122
  }
9171
9123
  if (this.backend === "local") {
9172
9124
  const localOptions = options;
9173
- if (localOptions.transport !== undefined && localOptions.transport !== "app-server" && localOptions.transport !== "stdio") {
9174
- throw new Error("Invalid local transport. Valid values: app-server, stdio.");
9125
+ if ("transport" in localOptions) {
9126
+ throw new Error("Local transport selection has been removed. The local backend always uses the app-server protocol.");
9175
9127
  }
9176
9128
  const requestTimeoutMs = localOptions.appServer?.requestTimeoutMs;
9177
9129
  if (requestTimeoutMs !== undefined && (!Number.isInteger(requestTimeoutMs) || requestTimeoutMs <= 0)) {
9178
9130
  throw new Error("Invalid appServer.requestTimeoutMs. Expected a positive integer.");
9179
9131
  }
9180
- const idleLingerMs = localOptions.appServer?.idleLingerMs;
9181
- if (idleLingerMs !== undefined && (!Number.isInteger(idleLingerMs) || idleLingerMs < 0)) {
9182
- throw new Error("Invalid appServer.idleLingerMs. Expected a non-negative integer.");
9183
- }
9184
9132
  const startupTimeoutMs = localOptions.appServer?.startupTimeoutMs;
9185
9133
  if (startupTimeoutMs !== undefined && (!Number.isInteger(startupTimeoutMs) || startupTimeoutMs <= 0)) {
9186
9134
  throw new Error("Invalid appServer.startupTimeoutMs. Expected a positive integer.");
@@ -9193,9 +9141,6 @@ class LettaAgentClientBase {
9193
9141
  if (options.requestTimeoutMs !== undefined && (!Number.isInteger(options.requestTimeoutMs) || options.requestTimeoutMs <= 0)) {
9194
9142
  throw new Error("Invalid requestTimeoutMs. Expected a positive integer.");
9195
9143
  }
9196
- if (options.idleLingerMs !== undefined && (!Number.isInteger(options.idleLingerMs) || options.idleLingerMs < 0)) {
9197
- throw new Error("Invalid idleLingerMs. Expected a non-negative integer.");
9198
- }
9199
9144
  }
9200
9145
  if (this.backend === "cloud") {
9201
9146
  validateCloudClientOptions(options);
@@ -9223,40 +9168,35 @@ class LettaAgentClientBase {
9223
9168
  }
9224
9169
  return this.createLocalAgent(options);
9225
9170
  }
9226
- createSession(agentIdOrOptions, options = {}) {
9227
- const agentId = typeof agentIdOrOptions === "string" ? agentIdOrOptions : undefined;
9228
- const resolvedOptions = typeof agentIdOrOptions === "string" ? options : agentIdOrOptions ?? {};
9229
- this.assertSessionBackend("createSession", resolvedOptions);
9230
- const sessionOptions = stripCloudExecutionOptions(resolvedOptions);
9171
+ createSession(agentId, options = {}) {
9172
+ if (typeof agentId !== "string" || agentId.length === 0) {
9173
+ throw new Error("createSession() requires a non-empty agent id.");
9174
+ }
9175
+ const sessionOptions = stripCloudExecutionOptions(options);
9231
9176
  validateCreateSessionOptions(sessionOptions);
9177
+ this.assertSessionBackend("createSession", options);
9232
9178
  if (this.backend === "remote") {
9233
- if (!agentId) {
9234
- throw new Error("App-server createSession() requires an agent id. Call createAgent() first or pass an agent id.");
9235
- }
9236
9179
  return new AppServerSession(this.appServerSessionOptions(), {
9237
9180
  kind: "session",
9238
9181
  agentId,
9239
9182
  newConversation: true,
9240
- options: resolvedOptions
9183
+ options
9241
9184
  });
9242
9185
  }
9243
9186
  if (this.backend === "cloud") {
9244
- if (!agentId) {
9245
- throw new Error("Letta Cloud createSession() requires an agent id. Call createAgent() first or pass an agent id.");
9246
- }
9247
9187
  return new CloudEnvironmentSession(this.cloudOptions(), {
9248
9188
  kind: "session",
9249
9189
  agentId,
9250
9190
  newConversation: true,
9251
- options: resolvedOptions
9191
+ options
9252
9192
  });
9253
9193
  }
9254
- return this.createLocalSession(agentId, resolvedOptions, sessionOptions);
9194
+ return this.createLocalSession(agentId, options);
9255
9195
  }
9256
9196
  resumeSession(id, options = {}) {
9257
- this.assertSessionBackend("resumeSession", options);
9258
9197
  const sessionOptions = stripCloudExecutionOptions(options);
9259
9198
  validateCreateSessionOptions(sessionOptions);
9199
+ this.assertSessionBackend("resumeSession", options);
9260
9200
  if (this.backend === "remote") {
9261
9201
  if (looksLikeConversationId(id)) {
9262
9202
  return new AppServerSession(this.appServerSessionOptions(), {
@@ -9287,10 +9227,10 @@ class LettaAgentClientBase {
9287
9227
  options
9288
9228
  });
9289
9229
  }
9290
- return this.resumeLocalSession(id, options, sessionOptions);
9230
+ return this.resumeLocalSession(id, options);
9291
9231
  }
9292
9232
  async prompt(message, agentId, options = {}) {
9293
- const session = agentId ? this.createSession(agentId, options) : this.createSession(options);
9233
+ const session = this.createSession(agentId, options);
9294
9234
  try {
9295
9235
  return await session.runTurn(message);
9296
9236
  } finally {
@@ -9298,6 +9238,9 @@ class LettaAgentClientBase {
9298
9238
  }
9299
9239
  }
9300
9240
  assertSessionBackend(action, options) {
9241
+ if (options.filesystemConfinement !== undefined && options.filesystemConfinement !== "memory") {
9242
+ throw new Error(`Invalid filesystemConfinement '${String(options.filesystemConfinement)}'. Valid value: memory.`);
9243
+ }
9301
9244
  const effectiveEnvironment = options.environment ?? this.environment;
9302
9245
  if (this.backend === "local") {
9303
9246
  if (effectiveEnvironment !== undefined) {
@@ -9309,12 +9252,18 @@ class LettaAgentClientBase {
9309
9252
  if (hasRepositoryResources(options)) {
9310
9253
  throw new Error(`${action}() repository resources are only valid with backend: "cloud".`);
9311
9254
  }
9312
- if (!this.useLegacyLocalStdio()) {
9313
- assertRemoteSessionOptionsSupported(action, options);
9255
+ if (options.filesystemConfinement !== undefined) {
9256
+ const localOptions = this.options;
9257
+ if (localOptions.appServer?.url !== undefined) {
9258
+ throw new Error(`${action}() filesystemConfinement requires an SDK-owned local app-server process.`);
9259
+ }
9314
9260
  }
9315
9261
  return;
9316
9262
  }
9317
9263
  if (this.backend === "remote") {
9264
+ if (options.filesystemConfinement !== undefined) {
9265
+ throw new Error(`${action}() filesystemConfinement requires an SDK-owned local app-server process.`);
9266
+ }
9318
9267
  if (options.environment !== undefined) {
9319
9268
  throw new Error(`${action}() environment overrides are only valid with backend: "cloud"; remote url selects the app-server runtime.`);
9320
9269
  }
@@ -9324,10 +9273,12 @@ class LettaAgentClientBase {
9324
9273
  if (hasRepositoryResources(options)) {
9325
9274
  throw new Error(`${action}() repository resources are only valid with backend: "cloud".`);
9326
9275
  }
9327
- assertRemoteSessionOptionsSupported(action, options);
9328
9276
  return;
9329
9277
  }
9330
9278
  if (this.backend === "cloud") {
9279
+ if (options.filesystemConfinement !== undefined) {
9280
+ throw new Error(`${action}() filesystemConfinement is only supported with backend: "local".`);
9281
+ }
9331
9282
  const cloudOptions = this.cloudOptions();
9332
9283
  if (cloudOptions.environment !== undefined && options.sandbox !== undefined) {
9333
9284
  throw new Error(`Letta Cloud ${action}() cannot specify sandbox options when the client has a default environment.`);
@@ -9340,16 +9291,13 @@ class LettaAgentClientBase {
9340
9291
  }
9341
9292
  throw new Error(`LettaAgentClient backend '${this.backend}' is not implemented yet. ${action} currently supports backend 'local' only.`);
9342
9293
  }
9343
- useLegacyLocalStdio() {
9344
- return this.options.transport === "stdio";
9345
- }
9346
9294
  createLocalAgent(_options) {
9347
9295
  throw this.localBackendUnavailableError();
9348
9296
  }
9349
- createLocalSession(_agentId, _options, _sessionOptions) {
9297
+ createLocalSession(_agentId, _options) {
9350
9298
  throw this.localBackendUnavailableError();
9351
9299
  }
9352
- resumeLocalSession(_id, _options, _sessionOptions) {
9300
+ resumeLocalSession(_id, _options) {
9353
9301
  throw this.localBackendUnavailableError();
9354
9302
  }
9355
9303
  localBackendUnavailableError() {
@@ -9365,10 +9313,7 @@ class LettaAgentClientBase {
9365
9313
  return this.options;
9366
9314
  }
9367
9315
  appServerSessionOptions() {
9368
- return {
9369
- ...this.remoteOptions(),
9370
- beforeConnect: () => this.releaseIdleManagementConnection()
9371
- };
9316
+ return this.remoteOptions();
9372
9317
  }
9373
9318
  getRepositoriesClient() {
9374
9319
  if (this.backend !== "cloud") {
@@ -9377,9 +9322,6 @@ class LettaAgentClientBase {
9377
9322
  this.repositoriesClient ??= new RepositoriesClient(this.cloudOptions());
9378
9323
  return this.repositoriesClient;
9379
9324
  }
9380
- async releaseIdleManagementConnection() {
9381
- await this.managementTransport?.releaseIdleConnection?.();
9382
- }
9383
9325
  getManagementTransport() {
9384
9326
  if (this.managementTransport)
9385
9327
  return this.managementTransport;
@@ -9487,4 +9429,4 @@ export {
9487
9429
  CloudManagedSandboxExpiredError
9488
9430
  };
9489
9431
 
9490
- //# debugId=0186899B952456A764756E2164756E21
9432
+ //# debugId=C4A49597208F4A6464756E2164756E21