@letta-ai/letta-agent-sdk 0.3.0 → 0.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -701,18 +701,101 @@ function createAppServerClient(options) {
701
701
  return new AppServerClient(options);
702
702
  }
703
703
 
704
+ // src/request-ids.ts
705
+ var processRequestCounter = 0;
706
+ function createRequestIdGenerator() {
707
+ const nonce = Math.random().toString(36).slice(2, 10);
708
+ return (prefix = "req") => `${prefix}-${nonce}-${++processRequestCounter}`;
709
+ }
710
+ function applyUniqueRequestIds(client) {
711
+ client.nextRequestId = createRequestIdGenerator();
712
+ return client;
713
+ }
714
+
704
715
  // src/app-server-management.ts
716
+ var DEFAULT_IDLE_LINGER_MS = 250;
717
+ var registeredTransportsByUrl = new Map;
718
+ function appServerUrlKey(value) {
719
+ try {
720
+ const url = new URL(value);
721
+ if (url.protocol === "http:")
722
+ url.protocol = "ws:";
723
+ if (url.protocol === "https:")
724
+ url.protocol = "wss:";
725
+ url.hash = "";
726
+ url.searchParams.delete("channel");
727
+ return url.toString();
728
+ } catch {
729
+ return value;
730
+ }
731
+ }
732
+ function registerManagementTransport(url, transport) {
733
+ const key = appServerUrlKey(url);
734
+ const registration = { transport };
735
+ const transports = registeredTransportsByUrl.get(key) ?? new Set;
736
+ transports.add(registration);
737
+ registeredTransportsByUrl.set(key, transports);
738
+ return () => {
739
+ transports.delete(registration);
740
+ if (transports.size === 0) {
741
+ registeredTransportsByUrl.delete(key);
742
+ }
743
+ };
744
+ }
745
+ async function releaseAppServerManagementConnections(url) {
746
+ const key = appServerUrlKey(url);
747
+ while (true) {
748
+ const transports = [...registeredTransportsByUrl.get(key) ?? []];
749
+ if (transports.length === 0)
750
+ return;
751
+ await Promise.all(transports.map(({ transport }) => transport.releaseIdleConnection()));
752
+ }
753
+ }
705
754
  function ensureResponse(response, value, fallback) {
706
755
  if (!response.success || value == null) {
707
756
  throw new Error(response.error ?? fallback);
708
757
  }
709
758
  return value;
710
759
  }
760
+ function modelEntries(value) {
761
+ if (!Array.isArray(value))
762
+ return [];
763
+ return value.flatMap((entry) => {
764
+ if (!entry || typeof entry !== "object" || Array.isArray(entry))
765
+ return [];
766
+ const record = entry;
767
+ if (typeof record.id !== "string" || typeof record.handle !== "string" || typeof record.label !== "string" || typeof record.description !== "string") {
768
+ return [];
769
+ }
770
+ return [{
771
+ ...record,
772
+ id: record.id,
773
+ handle: record.handle,
774
+ label: record.label,
775
+ description: record.description
776
+ }];
777
+ });
778
+ }
779
+ function stringRecord(value) {
780
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
781
+ return;
782
+ }
783
+ const entries = Object.entries(value).filter((entry) => typeof entry[1] === "string");
784
+ return entries.length > 0 ? Object.fromEntries(entries) : undefined;
785
+ }
711
786
 
712
787
  class AppServerManagementTransport {
713
788
  options;
789
+ connectionPromise = null;
790
+ releasePromise = null;
791
+ closingConnections = new Set;
792
+ inFlightRequests = 0;
793
+ idleWaiters = new Set;
794
+ idleTimer = null;
795
+ idleLingerMs;
714
796
  constructor(options) {
715
797
  this.options = options;
798
+ this.idleLingerMs = options.idleLingerMs ?? DEFAULT_IDLE_LINGER_MS;
716
799
  }
717
800
  async listAgents(query) {
718
801
  const response = await this.request("agent_list", { query }, "agent_list_response");
@@ -729,6 +812,30 @@ class AppServerManagementTransport {
729
812
  const response = await this.request("agent_update", { agent_id: agentId, body }, "agent_update_response");
730
813
  return ensureResponse(response, response.agent, `Failed to update agent ${agentId}.`);
731
814
  }
815
+ async deleteAgent(agentId) {
816
+ const response = await this.request("agent_delete", { agent_id: agentId }, "agent_delete_response");
817
+ if (!response.success) {
818
+ throw new Error(response.error ?? `Failed to delete agent ${agentId}.`);
819
+ }
820
+ }
821
+ async listModels() {
822
+ const response = await this.request("list_models", {}, "list_models_response");
823
+ if (!response.success) {
824
+ throw new Error(response.error ?? "Failed to list models.");
825
+ }
826
+ const result = {
827
+ entries: modelEntries(response.entries)
828
+ };
829
+ if (response.available_handles === null) {
830
+ result.availableHandles = null;
831
+ } else if (Array.isArray(response.available_handles)) {
832
+ result.availableHandles = response.available_handles.filter((handle) => typeof handle === "string");
833
+ }
834
+ const aliases = stringRecord(response.byok_provider_aliases);
835
+ if (aliases)
836
+ result.byokProviderAliases = aliases;
837
+ return result;
838
+ }
732
839
  async listConversations(query) {
733
840
  const response = await this.request("conversation_list", { query }, "conversation_list_response");
734
841
  if (!response.success) {
@@ -755,38 +862,206 @@ class AppServerManagementTransport {
755
862
  }
756
863
  return { messages: response.messages };
757
864
  }
865
+ releaseIdleConnection() {
866
+ this.clearIdleTimer();
867
+ if (this.releasePromise)
868
+ return this.releasePromise;
869
+ const release = this.releaseConnectionWhenIdle();
870
+ this.releasePromise = release;
871
+ release.then(() => {
872
+ if (this.releasePromise === release) {
873
+ this.releasePromise = null;
874
+ }
875
+ }, () => {
876
+ if (this.releasePromise === release) {
877
+ this.releasePromise = null;
878
+ }
879
+ });
880
+ return release;
881
+ }
758
882
  async request(type, body, responseType) {
759
- const ownedConnection = this.options.url ? null : await this.options.connect?.();
883
+ if (this.releasePromise) {
884
+ await this.releasePromise;
885
+ }
886
+ if (this.closingConnections.size > 0) {
887
+ await Promise.all([...this.closingConnections]);
888
+ }
889
+ this.clearIdleTimer();
890
+ this.inFlightRequests += 1;
891
+ try {
892
+ const { client } = await this.acquireConnection();
893
+ const command = {
894
+ type,
895
+ request_id: client.nextRequestId(type),
896
+ ...body
897
+ };
898
+ const response = await client.request(command, {
899
+ predicate: (message) => message.type === responseType
900
+ });
901
+ return response;
902
+ } finally {
903
+ this.inFlightRequests -= 1;
904
+ if (this.inFlightRequests === 0) {
905
+ const waiters = [...this.idleWaiters];
906
+ this.idleWaiters.clear();
907
+ for (const resolve of waiters)
908
+ resolve();
909
+ if (!this.releasePromise) {
910
+ this.scheduleIdleRelease();
911
+ }
912
+ }
913
+ }
914
+ }
915
+ async releaseConnectionWhenIdle() {
916
+ if (this.inFlightRequests > 0) {
917
+ await new Promise((resolve) => {
918
+ this.idleWaiters.add(resolve);
919
+ });
920
+ }
921
+ this.clearIdleTimer();
922
+ const promise = this.connectionPromise;
923
+ try {
924
+ if (promise) {
925
+ this.connectionPromise = null;
926
+ await this.trackClosingConnection(await promise);
927
+ }
928
+ if (this.closingConnections.size > 0) {
929
+ await Promise.all([...this.closingConnections]);
930
+ }
931
+ } catch {}
932
+ }
933
+ acquireConnection() {
934
+ if (this.connectionPromise)
935
+ return this.connectionPromise;
936
+ const promise = this.openConnection().then((connection) => {
937
+ connection.detachDisconnect = connection.client.onDisconnect(() => {
938
+ this.discardConnection(promise, connection);
939
+ });
940
+ return connection;
941
+ }, (error) => {
942
+ if (this.connectionPromise === promise) {
943
+ this.connectionPromise = null;
944
+ }
945
+ throw error;
946
+ });
947
+ this.connectionPromise = promise;
948
+ return promise;
949
+ }
950
+ async openConnection() {
951
+ const ownedConnection = this.options.url ? null : await this.options.connect?.() ?? null;
760
952
  const url = this.options.url ?? ownedConnection?.url;
761
953
  if (!url) {
762
954
  throw new Error("App-server management requires a url or connect hook.");
763
955
  }
956
+ const unregister = registerManagementTransport(url, this);
764
957
  let client = null;
765
958
  try {
766
- client = createAppServerClient({
959
+ client = applyUniqueRequestIds(createAppServerClient({
767
960
  url,
768
961
  ...this.options.authToken !== undefined ? { authToken: this.options.authToken } : {},
769
962
  ...this.options.WebSocket ? {
770
963
  WebSocket: this.options.WebSocket
771
964
  } : {},
772
965
  ...this.options.requestTimeoutMs !== undefined ? { requestTimeoutMs: this.options.requestTimeoutMs } : {}
773
- });
966
+ }));
774
967
  await client.connect();
775
- const command = {
776
- type,
777
- request_id: client.nextRequestId(type),
778
- ...body
779
- };
780
- const response = await client.request(command, {
781
- predicate: (message) => message.type === responseType
782
- });
783
- return response;
784
- } finally {
785
- client?.close();
786
- ownedConnection?.close();
968
+ } catch (error) {
969
+ try {
970
+ if (client) {
971
+ const controlClosed = waitForSocketClose(client.control);
972
+ client.close();
973
+ ownedConnection?.close();
974
+ await controlClosed;
975
+ } else {
976
+ ownedConnection?.close();
977
+ }
978
+ } catch {} finally {
979
+ unregister();
980
+ }
981
+ throw error;
982
+ }
983
+ return {
984
+ client,
985
+ ownedConnection,
986
+ detachDisconnect: () => {},
987
+ unregister
988
+ };
989
+ }
990
+ discardConnection(promise, connection) {
991
+ if (this.connectionPromise === promise) {
992
+ this.connectionPromise = null;
993
+ this.clearIdleTimer();
787
994
  }
995
+ this.trackClosingConnection(connection);
996
+ }
997
+ trackClosingConnection(connection) {
998
+ const closing = closeConnection(connection);
999
+ this.closingConnections.add(closing);
1000
+ closing.then(() => this.closingConnections.delete(closing), () => this.closingConnections.delete(closing));
1001
+ return closing;
1002
+ }
1003
+ scheduleIdleRelease() {
1004
+ if (!this.connectionPromise)
1005
+ return;
1006
+ this.clearIdleTimer();
1007
+ const timer = setTimeout(() => {
1008
+ this.idleTimer = null;
1009
+ this.releaseIdleConnection();
1010
+ }, this.idleLingerMs);
1011
+ this.idleTimer = timer;
1012
+ timer.unref?.();
1013
+ }
1014
+ clearIdleTimer() {
1015
+ if (this.idleTimer === null)
1016
+ return;
1017
+ clearTimeout(this.idleTimer);
1018
+ this.idleTimer = null;
1019
+ }
1020
+ }
1021
+ async function closeConnection(connection) {
1022
+ connection.detachDisconnect();
1023
+ const controlClosed = waitForSocketClose(connection.client.control);
1024
+ try {
1025
+ connection.client.close();
1026
+ connection.ownedConnection?.close();
1027
+ await controlClosed;
1028
+ } finally {
1029
+ connection.unregister();
788
1030
  }
789
1031
  }
1032
+ function waitForSocketClose(socket) {
1033
+ if (socket.readyState === 3)
1034
+ return Promise.resolve();
1035
+ return new Promise((resolve) => {
1036
+ let settled = false;
1037
+ let detach = () => {};
1038
+ const finish = () => {
1039
+ if (settled)
1040
+ return;
1041
+ settled = true;
1042
+ clearTimeout(timeout);
1043
+ detach();
1044
+ resolve();
1045
+ };
1046
+ if (socket.addEventListener) {
1047
+ socket.addEventListener("close", finish);
1048
+ detach = () => socket.removeEventListener?.("close", finish);
1049
+ } else if (socket.once) {
1050
+ socket.once("close", finish);
1051
+ detach = () => socket.off?.("close", finish);
1052
+ } else if (socket.on) {
1053
+ socket.on("close", finish);
1054
+ detach = () => socket.off?.("close", finish);
1055
+ } else {
1056
+ resolve();
1057
+ return;
1058
+ }
1059
+ const timeout = setTimeout(finish, 1000);
1060
+ timeout.unref?.();
1061
+ if (socket.readyState === 3)
1062
+ finish();
1063
+ });
1064
+ }
790
1065
 
791
1066
  // node_modules/@letta-ai/letta-code/dist/agent-presets.js
792
1067
  var LETTA_CODE_ORIGIN_TAG = "origin:letta-code";
@@ -5424,6 +5699,36 @@ function requiresRuntimeUserInput(toolName) {
5424
5699
  function isHeadlessAutoAllowTool(toolName) {
5425
5700
  return HEADLESS_AUTO_ALLOW_TOOLS.has(toolName);
5426
5701
  }
5702
+ function normalizePermissionSuggestions(value) {
5703
+ if (!Array.isArray(value))
5704
+ return;
5705
+ const suggestions = [];
5706
+ for (const entry of value) {
5707
+ if (!entry || typeof entry !== "object")
5708
+ continue;
5709
+ const record = entry;
5710
+ if (typeof record.id === "string" && typeof record.text === "string") {
5711
+ suggestions.push({ id: record.id, text: record.text });
5712
+ }
5713
+ }
5714
+ return suggestions;
5715
+ }
5716
+ function buildCanUseToolContext(request, requestId) {
5717
+ const context = {};
5718
+ if (typeof requestId === "string")
5719
+ context.requestId = requestId;
5720
+ if (typeof request.tool_call_id === "string")
5721
+ context.toolCallId = request.tool_call_id;
5722
+ const suggestions = normalizePermissionSuggestions(request.permission_suggestions);
5723
+ if (suggestions !== undefined)
5724
+ context.permissionSuggestions = suggestions;
5725
+ if (typeof request.blocked_path === "string" || request.blocked_path === null) {
5726
+ context.blockedPath = request.blocked_path;
5727
+ }
5728
+ if (Array.isArray(request.diffs))
5729
+ context.diffs = request.diffs;
5730
+ return context;
5731
+ }
5427
5732
 
5428
5733
  // src/remote-client-session-core.ts
5429
5734
  var FAILURE_STOP_REASONS = new Set([
@@ -5701,6 +6006,121 @@ function queueItems(message) {
5701
6006
  ];
5702
6007
  });
5703
6008
  }
6009
+ function deviceStatusRecord(message) {
6010
+ if (message.type !== "update_device_status")
6011
+ return null;
6012
+ const status = message.device_status;
6013
+ return status && typeof status === "object" && !Array.isArray(status) ? status : null;
6014
+ }
6015
+ function pendingControlRequests(status) {
6016
+ const pending = status.pending_control_requests;
6017
+ if (!Array.isArray(pending))
6018
+ return [];
6019
+ return pending.flatMap((item) => {
6020
+ if (!item || typeof item !== "object" || Array.isArray(item))
6021
+ return [];
6022
+ const record = item;
6023
+ if (typeof record.request_id !== "string")
6024
+ return [];
6025
+ const request = record.request && typeof record.request === "object" && !Array.isArray(record.request) ? record.request : null;
6026
+ if (!request || typeof request.tool_name !== "string")
6027
+ return [];
6028
+ const entry = {
6029
+ requestId: record.request_id,
6030
+ toolName: request.tool_name,
6031
+ permissionSuggestions: permissionSuggestions(request.permission_suggestions),
6032
+ blockedPath: typeof request.blocked_path === "string" || request.blocked_path === null ? request.blocked_path : null
6033
+ };
6034
+ if (typeof request.tool_call_id === "string")
6035
+ entry.toolCallId = request.tool_call_id;
6036
+ if (request.input && typeof request.input === "object" && !Array.isArray(request.input)) {
6037
+ entry.toolInput = request.input;
6038
+ }
6039
+ const previews = diffPreviews(request.diffs);
6040
+ if (previews !== undefined)
6041
+ entry.diffs = previews;
6042
+ return [entry];
6043
+ });
6044
+ }
6045
+ function permissionSuggestions(value) {
6046
+ if (!Array.isArray(value))
6047
+ return [];
6048
+ return value.flatMap((item) => {
6049
+ if (!item || typeof item !== "object" || Array.isArray(item))
6050
+ return [];
6051
+ const record = item;
6052
+ return typeof record.id === "string" && typeof record.text === "string" ? [{ id: record.id, text: record.text }] : [];
6053
+ });
6054
+ }
6055
+ function diffPreviews(value) {
6056
+ if (!Array.isArray(value))
6057
+ return;
6058
+ return value.flatMap((item) => {
6059
+ if (!item || typeof item !== "object" || Array.isArray(item))
6060
+ return [];
6061
+ const record = item;
6062
+ if (record.mode === "advanced" && typeof record.fileName === "string" && Array.isArray(record.hunks)) {
6063
+ return [{
6064
+ mode: "advanced",
6065
+ fileName: record.fileName,
6066
+ hunks: diffHunks(record.hunks)
6067
+ }];
6068
+ }
6069
+ if ((record.mode === "fallback" || record.mode === "unpreviewable") && typeof record.fileName === "string" && typeof record.reason === "string") {
6070
+ return [{
6071
+ mode: record.mode,
6072
+ fileName: record.fileName,
6073
+ reason: record.reason
6074
+ }];
6075
+ }
6076
+ return [];
6077
+ });
6078
+ }
6079
+ function diffHunks(value) {
6080
+ return value.flatMap((item) => {
6081
+ if (!item || typeof item !== "object" || Array.isArray(item))
6082
+ return [];
6083
+ const record = item;
6084
+ if (typeof record.oldStart !== "number" || typeof record.oldLines !== "number" || typeof record.newStart !== "number" || typeof record.newLines !== "number" || !Array.isArray(record.lines)) {
6085
+ return [];
6086
+ }
6087
+ const lines = [];
6088
+ for (const line of record.lines) {
6089
+ if (!line || typeof line !== "object" || Array.isArray(line))
6090
+ continue;
6091
+ const lineRecord = line;
6092
+ const type = lineRecord.type;
6093
+ if (type !== "context" && type !== "add" && type !== "remove" || typeof lineRecord.content !== "string") {
6094
+ continue;
6095
+ }
6096
+ lines.push({
6097
+ type,
6098
+ content: lineRecord.content
6099
+ });
6100
+ }
6101
+ return [{
6102
+ oldStart: record.oldStart,
6103
+ oldLines: record.oldLines,
6104
+ newStart: record.newStart,
6105
+ newLines: record.newLines,
6106
+ lines
6107
+ }];
6108
+ });
6109
+ }
6110
+ function toSessionDeviceStatus(status) {
6111
+ const permissionMode = normalizePermissionMode(status.current_permission_mode);
6112
+ if (typeof status.is_online !== "boolean" || typeof status.is_processing !== "boolean" || permissionMode === undefined || !(typeof status.current_working_directory === "string" || status.current_working_directory === null)) {
6113
+ return null;
6114
+ }
6115
+ return {
6116
+ isOnline: status.is_online,
6117
+ isProcessing: status.is_processing,
6118
+ permissionMode,
6119
+ workingDirectory: status.current_working_directory,
6120
+ pendingControlRequests: pendingControlRequests(status),
6121
+ raw: { ...status }
6122
+ };
6123
+ }
5704
6124
  function normalizeSendMessage(message) {
5705
6125
  return message;
5706
6126
  }
@@ -5719,6 +6139,7 @@ class RemoteClientSessionCore {
5719
6139
  _modelSettings = null;
5720
6140
  label;
5721
6141
  requestTimeoutMs;
6142
+ initializePromise = null;
5722
6143
  streamQueue = [];
5723
6144
  streamResolvers = [];
5724
6145
  removeMessageHandler = null;
@@ -5728,49 +6149,80 @@ class RemoteClientSessionCore {
5728
6149
  messageCounter = 0;
5729
6150
  clientMessageCounter = 0;
5730
6151
  toolNames;
6152
+ deviceStatusListeners = new Set;
6153
+ deviceStatusRefreshCancels = new Set;
5731
6154
  constructor(mode, config) {
5732
6155
  this.mode = mode;
5733
6156
  this.label = config.label;
5734
6157
  this.requestTimeoutMs = config.requestTimeoutMs;
5735
6158
  }
5736
6159
  async initialize() {
6160
+ if (this.closed) {
6161
+ throw new Error("Session is closed");
6162
+ }
6163
+ if (this.initializePromise) {
6164
+ return this.initializePromise;
6165
+ }
5737
6166
  if (this.initialized) {
5738
6167
  throw new Error("Session already initialized");
5739
6168
  }
6169
+ const attempt = this.performInitialize();
6170
+ const memo = attempt.catch((error) => {
6171
+ this.cleanupFailedInitialize();
6172
+ throw error;
6173
+ }).finally(() => {
6174
+ if (this.initializePromise === memo) {
6175
+ this.initializePromise = null;
6176
+ }
6177
+ });
6178
+ this.initializePromise = memo;
6179
+ return memo;
6180
+ }
6181
+ async performInitialize() {
6182
+ const init = await this.initializeRuntimeController();
6183
+ this.controller = init.controller;
6184
+ this.runtime = init.runtime;
6185
+ this._agentId = init.runtime.agent_id;
6186
+ this._conversationId = init.runtime.conversation_id;
6187
+ this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}`;
6188
+ this._modelSettings = init.modelSettings ?? null;
6189
+ this._model = typeof init.model === "string" ? init.model : typeof this._modelSettings?.model === "string" ? this._modelSettings.model : "";
6190
+ this.toolNames = init.tools;
6191
+ this.removeMessageHandler = this.controller.onMessage(this.handleProtocolMessage);
6192
+ await this.afterRuntimeInitialized();
6193
+ await this.applyPostInitializeOptions();
5740
6194
  if (this.closed) {
5741
6195
  throw new Error("Session is closed");
5742
6196
  }
5743
- try {
5744
- const init = await this.initializeRuntimeController();
5745
- this.controller = init.controller;
5746
- this.runtime = init.runtime;
5747
- this._agentId = init.runtime.agent_id;
5748
- this._conversationId = init.runtime.conversation_id;
5749
- this._sessionId = `${init.runtime.agent_id}:${init.runtime.conversation_id}`;
5750
- this._modelSettings = init.modelSettings ?? null;
5751
- this._model = typeof init.model === "string" ? init.model : typeof this._modelSettings?.model === "string" ? this._modelSettings.model : "";
5752
- this.toolNames = init.tools;
5753
- this.removeMessageHandler = this.controller.onMessage(this.handleProtocolMessage);
5754
- this.initialized = true;
5755
- await this.afterRuntimeInitialized();
5756
- await this.applyPostInitializeOptions();
5757
- const initMessage = {
5758
- type: "init",
5759
- agentId: init.runtime.agent_id,
5760
- sessionId: this._sessionId,
5761
- conversationId: init.runtime.conversation_id,
5762
- model: this._model
5763
- };
5764
- if (this.toolNames !== undefined)
5765
- initMessage.tools = this.toolNames;
5766
- if (init.skillSources !== undefined) {
5767
- initMessage.skillSources = init.skillSources;
5768
- }
5769
- return initMessage;
5770
- } catch (error) {
5771
- this.close();
5772
- throw error;
6197
+ this.initialized = true;
6198
+ const initMessage = {
6199
+ type: "init",
6200
+ agentId: init.runtime.agent_id,
6201
+ sessionId: this._sessionId,
6202
+ conversationId: init.runtime.conversation_id,
6203
+ model: this._model
6204
+ };
6205
+ if (this.toolNames !== undefined)
6206
+ initMessage.tools = this.toolNames;
6207
+ if (init.skillSources !== undefined) {
6208
+ initMessage.skillSources = init.skillSources;
5773
6209
  }
6210
+ return initMessage;
6211
+ }
6212
+ cleanupFailedInitialize() {
6213
+ this.removeMessageHandler?.();
6214
+ this.removeMessageHandler = null;
6215
+ this.controller?.close();
6216
+ this.controller = null;
6217
+ this.onCoreClose();
6218
+ this.runtime = null;
6219
+ this._agentId = null;
6220
+ this._conversationId = null;
6221
+ this._sessionId = null;
6222
+ this._modelSettings = null;
6223
+ this._model = "";
6224
+ this.toolNames = undefined;
6225
+ this.initialized = false;
5774
6226
  }
5775
6227
  async send(message) {
5776
6228
  if (!this.initialized) {
@@ -5867,6 +6319,12 @@ class RemoteClientSessionCore {
5867
6319
  if (!this.initialized) {
5868
6320
  await this.initialize();
5869
6321
  }
6322
+ if (!this.controller || !this.runtime) {
6323
+ throw new Error("Session is not initialized");
6324
+ }
6325
+ return this.applyModelUpdate(update);
6326
+ }
6327
+ async applyModelUpdate(update) {
5870
6328
  if (!this.controller || !this.runtime) {
5871
6329
  throw new Error("Session is not initialized");
5872
6330
  }
@@ -5918,6 +6376,86 @@ class RemoteClientSessionCore {
5918
6376
  removed: response.success
5919
6377
  };
5920
6378
  }
6379
+ async getDeviceStatus(options = {}) {
6380
+ if (!this.initialized) {
6381
+ await this.initialize();
6382
+ }
6383
+ if (!this.controller || !this.runtime) {
6384
+ throw new Error("Session is not initialized");
6385
+ }
6386
+ const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs ?? 30000;
6387
+ if (!Number.isInteger(timeoutMs) || timeoutMs <= 0) {
6388
+ throw new Error("Invalid device status timeout. Expected a positive integer.");
6389
+ }
6390
+ return this.refreshDeviceStatus(timeoutMs);
6391
+ }
6392
+ onDeviceStatus(listener) {
6393
+ if (typeof listener !== "function") {
6394
+ throw new Error("Invalid device status listener. Expected a function.");
6395
+ }
6396
+ this.deviceStatusListeners.add(listener);
6397
+ return () => {
6398
+ this.deviceStatusListeners.delete(listener);
6399
+ };
6400
+ }
6401
+ refreshDeviceStatus(timeoutMs) {
6402
+ const controller = this.controller;
6403
+ const runtime = this.runtime;
6404
+ if (!controller || !runtime) {
6405
+ return Promise.reject(new Error("Session is not initialized"));
6406
+ }
6407
+ return new Promise((resolve, reject) => {
6408
+ let status = null;
6409
+ let syncAcknowledged = false;
6410
+ let settled = false;
6411
+ const cleanup = () => {
6412
+ clearTimeout(timer);
6413
+ unsubscribe();
6414
+ this.deviceStatusRefreshCancels.delete(cancel);
6415
+ };
6416
+ const rejectOnce = (error) => {
6417
+ if (settled)
6418
+ return;
6419
+ settled = true;
6420
+ cleanup();
6421
+ reject(error);
6422
+ };
6423
+ const resolveIfComplete = () => {
6424
+ if (settled || !syncAcknowledged || !status)
6425
+ return;
6426
+ settled = true;
6427
+ cleanup();
6428
+ resolve(status);
6429
+ };
6430
+ const cancel = (error) => rejectOnce(error);
6431
+ const timer = setTimeout(() => {
6432
+ rejectOnce(new Error(`Timed out waiting for ${this.label} device status`));
6433
+ }, timeoutMs);
6434
+ timer.unref?.();
6435
+ const unsubscribe = this.onDeviceStatus((nextStatus) => {
6436
+ status = nextStatus;
6437
+ resolveIfComplete();
6438
+ });
6439
+ this.deviceStatusRefreshCancels.add(cancel);
6440
+ controller.request("sync", {
6441
+ runtime,
6442
+ recover_approvals: false,
6443
+ force_device_status: true
6444
+ }, {
6445
+ timeoutMs,
6446
+ predicate: (message) => message.type === "sync_response"
6447
+ }).then((response) => {
6448
+ if (response.success === false) {
6449
+ rejectOnce(new Error(typeof response.error === "string" ? response.error : `Failed to refresh ${this.label} device status`));
6450
+ return;
6451
+ }
6452
+ syncAcknowledged = true;
6453
+ resolveIfComplete();
6454
+ }, (error) => {
6455
+ rejectOnce(error instanceof Error ? error : new Error(String(error)));
6456
+ });
6457
+ });
6458
+ }
5921
6459
  async listMessages(options = {}) {
5922
6460
  if (!this.initialized) {
5923
6461
  await this.initialize();
@@ -5970,6 +6508,11 @@ class RemoteClientSessionCore {
5970
6508
  }
5971
6509
  this.activeTurn = null;
5972
6510
  this.pendingTurns.length = 0;
6511
+ for (const cancel of [...this.deviceStatusRefreshCancels]) {
6512
+ cancel(new Error(`Session closed while waiting for ${this.label} device status`));
6513
+ }
6514
+ this.deviceStatusRefreshCancels.clear();
6515
+ this.deviceStatusListeners.clear();
5973
6516
  this.controller?.close();
5974
6517
  this.controller = null;
5975
6518
  this.onCoreClose();
@@ -6198,7 +6741,7 @@ class RemoteClientSessionCore {
6198
6741
  if (this.mode.kind !== "session")
6199
6742
  return;
6200
6743
  if (this.mode.options.model !== undefined || this.mode.options.reasoningEffort !== undefined) {
6201
- await this.updateModel({
6744
+ await this.applyModelUpdate({
6202
6745
  ...this.mode.options.model !== undefined ? { model: this.mode.options.model } : {},
6203
6746
  ...this.mode.options.reasoningEffort !== undefined ? { reasoningEffort: this.mode.options.reasoningEffort } : {}
6204
6747
  });
@@ -6207,6 +6750,18 @@ class RemoteClientSessionCore {
6207
6750
  handleProtocolMessage = (message) => {
6208
6751
  if (!this.runtime || !sameRuntime2(message, this.runtime))
6209
6752
  return;
6753
+ const deviceStatus = deviceStatusRecord(message);
6754
+ if (deviceStatus) {
6755
+ const status = toSessionDeviceStatus(deviceStatus);
6756
+ if (!status)
6757
+ return;
6758
+ for (const listener of [...this.deviceStatusListeners]) {
6759
+ try {
6760
+ listener(status);
6761
+ } catch {}
6762
+ }
6763
+ return;
6764
+ }
6210
6765
  if (message.type === "update_queue") {
6211
6766
  const sdkMessage2 = {
6212
6767
  type: "queue_update",
@@ -6636,7 +7191,7 @@ function createExternalToolCallHandler(externalTools) {
6636
7191
  };
6637
7192
  };
6638
7193
  }
6639
- async function resolveAppServerToolApproval(options, toolName, toolInput) {
7194
+ async function resolveAppServerToolApproval(options, toolName, toolInput, context) {
6640
7195
  const hasCallback = typeof options.canUseTool === "function";
6641
7196
  const toolNeedsRuntimeUserInput = requiresRuntimeUserInput(toolName);
6642
7197
  if (toolNeedsRuntimeUserInput && !hasCallback) {
@@ -6651,7 +7206,7 @@ async function resolveAppServerToolApproval(options, toolName, toolInput) {
6651
7206
  }
6652
7207
  if (hasCallback) {
6653
7208
  try {
6654
- const result = await options.canUseTool(toolName, toolInput);
7209
+ const result = await options.canUseTool(toolName, toolInput, context);
6655
7210
  if (result.behavior === "allow") {
6656
7211
  return {
6657
7212
  behavior: "allow",
@@ -6711,7 +7266,7 @@ function toAppServerApprovalDecision(decision) {
6711
7266
  selected_permission_suggestion_ids: selectedPermissionSuggestionIds
6712
7267
  };
6713
7268
  }
6714
- function stringRecord(value) {
7269
+ function stringRecord2(value) {
6715
7270
  if (!value || typeof value !== "object" || Array.isArray(value))
6716
7271
  return;
6717
7272
  const record = {};
@@ -6757,7 +7312,7 @@ function normalizeListModelsResponse(response) {
6757
7312
  } else if (Array.isArray(response.available_handles)) {
6758
7313
  result.availableHandles = response.available_handles.filter((handle) => typeof handle === "string");
6759
7314
  }
6760
- const aliases = stringRecord(response.byok_provider_aliases);
7315
+ const aliases = stringRecord2(response.byok_provider_aliases);
6761
7316
  if (aliases)
6762
7317
  result.byokProviderAliases = aliases;
6763
7318
  return result;
@@ -6812,7 +7367,7 @@ async function respondToAppServerControlRequest(config, message) {
6812
7367
  return;
6813
7368
  const toolName = typeof requestRecord.tool_name === "string" ? requestRecord.tool_name : "unknown";
6814
7369
  const toolInput = requestRecord.input && typeof requestRecord.input === "object" && !Array.isArray(requestRecord.input) ? requestRecord.input : {};
6815
- const decision = await resolveAppServerToolApproval(config.getOptions(), toolName, toolInput);
7370
+ const decision = await resolveAppServerToolApproval(config.getOptions(), toolName, toolInput, buildCanUseToolContext(requestRecord, requestId));
6816
7371
  config.client.input({
6817
7372
  runtime,
6818
7373
  payload: {
@@ -6949,13 +7504,15 @@ class AppServerSession extends RemoteClientSessionCore {
6949
7504
  return options.memfs !== false;
6950
7505
  }
6951
7506
  async initializeRuntimeController() {
7507
+ await this.remoteOptions.beforeConnect?.();
6952
7508
  const url = await this.resolveAppServerUrl();
6953
- const client = createAppServerClient({
7509
+ await releaseAppServerManagementConnections(url);
7510
+ const client = applyUniqueRequestIds(createAppServerClient({
6954
7511
  url,
6955
7512
  ...this.remoteOptions.authToken !== undefined ? { authToken: this.remoteOptions.authToken } : {},
6956
7513
  ...this.remoteOptions.WebSocket ? { WebSocket: this.remoteOptions.WebSocket } : {},
6957
7514
  ...this.remoteOptions.requestTimeoutMs !== undefined ? { requestTimeoutMs: this.remoteOptions.requestTimeoutMs } : {}
6958
- });
7515
+ }));
6959
7516
  this.removeControlRequestHandler = registerAppServerControlRequestHandler({
6960
7517
  client,
6961
7518
  getRuntime: () => this.runtime,
@@ -7155,6 +7712,19 @@ function asArray(body, action) {
7155
7712
  }
7156
7713
  return body;
7157
7714
  }
7715
+ function cloudModelEntry(raw) {
7716
+ if (typeof raw.handle !== "string" || raw.handle.length === 0) {
7717
+ return null;
7718
+ }
7719
+ const label = typeof raw.display_name === "string" ? raw.display_name : typeof raw.name === "string" ? raw.name : raw.handle;
7720
+ return {
7721
+ ...raw,
7722
+ id: typeof raw.id === "string" && raw.id.length > 0 ? raw.id : raw.handle,
7723
+ handle: raw.handle,
7724
+ label,
7725
+ description: typeof raw.description === "string" ? raw.description : ""
7726
+ };
7727
+ }
7158
7728
 
7159
7729
  class CloudManagementTransport {
7160
7730
  options;
@@ -7170,6 +7740,20 @@ class CloudManagementTransport {
7170
7740
  updateAgent(agentId, body) {
7171
7741
  return this.requestObject(`/v1/agents/${encodeURIComponent(agentId)}`, "PATCH", body, "Cloud update agent");
7172
7742
  }
7743
+ async deleteAgent(agentId) {
7744
+ await this.requestUrl(this.url(`/v1/agents/${encodeURIComponent(agentId)}`), "DELETE", undefined, "Cloud delete agent");
7745
+ }
7746
+ async listModels() {
7747
+ const entries = await this.getArray("/v1/models", {}, "Cloud list models");
7748
+ const normalized = entries.flatMap((entry) => {
7749
+ const model = cloudModelEntry(entry);
7750
+ return model ? [model] : [];
7751
+ });
7752
+ return {
7753
+ entries: normalized,
7754
+ availableHandles: normalized.map((entry) => entry.handle)
7755
+ };
7756
+ }
7173
7757
  listConversations(query) {
7174
7758
  return this.getArray("/v1/conversations/", query, "Cloud list conversations");
7175
7759
  }
@@ -7799,14 +8383,14 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
7799
8383
  apiKey,
7800
8384
  authMode: this.cloudOptions.webSocketAuth ?? "header"
7801
8385
  });
7802
- const client = createAppServerClient({
8386
+ const client = applyUniqueRequestIds(createAppServerClient({
7803
8387
  url,
7804
8388
  WebSocket: createCloudStatusWebSocketConstructor({
7805
8389
  cloudOptions: this.cloudOptions,
7806
8390
  runtime: resolved.runtime
7807
8391
  }),
7808
8392
  requestTimeoutMs: this.cloudOptions.requestTimeoutMs ?? DEFAULT_TURN_TIMEOUT_MS
7809
- });
8393
+ }));
7810
8394
  this.removeControlRequestHandler = registerAppServerControlRequestHandler({
7811
8395
  client,
7812
8396
  getRuntime: () => this.runtime,
@@ -8254,6 +8838,11 @@ class CloudEnvironmentSession extends RemoteClientSessionCore {
8254
8838
  function definedEntries(values) {
8255
8839
  return Object.fromEntries(Object.entries(values).filter(([, value]) => value !== undefined));
8256
8840
  }
8841
+ function assertNonEmptyId(value, name) {
8842
+ if (typeof value !== "string" || value.trim().length === 0) {
8843
+ throw new Error(`Invalid ${name}. Expected a non-empty string.`);
8844
+ }
8845
+ }
8257
8846
  function agentListQuery(options) {
8258
8847
  const orderBy = options.orderBy?.replace(/[A-Z]/g, (character) => `_${character.toLowerCase()}`);
8259
8848
  return {
@@ -8326,7 +8915,16 @@ function createAgentsClient(transport) {
8326
8915
  return {
8327
8916
  list: (options = {}) => transport().listAgents(agentListQuery(options)),
8328
8917
  retrieve: (agentId) => transport().retrieveAgent(agentId),
8329
- update: (agentId, options) => transport().updateAgent(agentId, agentUpdateBody(options))
8918
+ update: (agentId, options) => transport().updateAgent(agentId, agentUpdateBody(options)),
8919
+ delete: async (agentId) => {
8920
+ assertNonEmptyId(agentId, "agent id");
8921
+ await transport().deleteAgent(agentId);
8922
+ }
8923
+ };
8924
+ }
8925
+ function createModelsClient(transport) {
8926
+ return {
8927
+ list: () => transport().listModels()
8330
8928
  };
8331
8929
  }
8332
8930
  function createConversationsClient(transport) {
@@ -8497,6 +9095,7 @@ class LettaAgentClientBase {
8497
9095
  environment;
8498
9096
  agents;
8499
9097
  conversations;
9098
+ models;
8500
9099
  options;
8501
9100
  repositoriesClient = null;
8502
9101
  managementTransport = null;
@@ -8510,6 +9109,7 @@ class LettaAgentClientBase {
8510
9109
  this.options = options;
8511
9110
  this.agents = createAgentsClient(() => this.getManagementTransport());
8512
9111
  this.conversations = createConversationsClient(() => this.getManagementTransport());
9112
+ this.models = createModelsClient(() => this.getManagementTransport());
8513
9113
  if (this.backend === "local" && this.environment !== undefined) {
8514
9114
  throw new Error('LettaAgentClient environment is only valid with backend: "cloud".');
8515
9115
  }
@@ -8528,6 +9128,10 @@ class LettaAgentClientBase {
8528
9128
  if (requestTimeoutMs !== undefined && (!Number.isInteger(requestTimeoutMs) || requestTimeoutMs <= 0)) {
8529
9129
  throw new Error("Invalid appServer.requestTimeoutMs. Expected a positive integer.");
8530
9130
  }
9131
+ const idleLingerMs = localOptions.appServer?.idleLingerMs;
9132
+ if (idleLingerMs !== undefined && (!Number.isInteger(idleLingerMs) || idleLingerMs < 0)) {
9133
+ throw new Error("Invalid appServer.idleLingerMs. Expected a non-negative integer.");
9134
+ }
8531
9135
  const startupTimeoutMs = localOptions.appServer?.startupTimeoutMs;
8532
9136
  if (startupTimeoutMs !== undefined && (!Number.isInteger(startupTimeoutMs) || startupTimeoutMs <= 0)) {
8533
9137
  throw new Error("Invalid appServer.startupTimeoutMs. Expected a positive integer.");
@@ -8540,6 +9144,9 @@ class LettaAgentClientBase {
8540
9144
  if (options.requestTimeoutMs !== undefined && (!Number.isInteger(options.requestTimeoutMs) || options.requestTimeoutMs <= 0)) {
8541
9145
  throw new Error("Invalid requestTimeoutMs. Expected a positive integer.");
8542
9146
  }
9147
+ if (options.idleLingerMs !== undefined && (!Number.isInteger(options.idleLingerMs) || options.idleLingerMs < 0)) {
9148
+ throw new Error("Invalid idleLingerMs. Expected a non-negative integer.");
9149
+ }
8543
9150
  }
8544
9151
  if (this.backend === "cloud") {
8545
9152
  validateCloudClientOptions(options);
@@ -8554,7 +9161,7 @@ class LettaAgentClientBase {
8554
9161
  }
8555
9162
  validateCreateAgentOptions(options);
8556
9163
  if (this.backend === "remote") {
8557
- const session = new AppServerSession(this.remoteOptions(), {
9164
+ const session = new AppServerSession(this.appServerSessionOptions(), {
8558
9165
  kind: "create-agent",
8559
9166
  options
8560
9167
  });
@@ -8577,7 +9184,7 @@ class LettaAgentClientBase {
8577
9184
  if (!agentId) {
8578
9185
  throw new Error("App-server createSession() requires an agent id. Call createAgent() first or pass an agent id.");
8579
9186
  }
8580
- return new AppServerSession(this.remoteOptions(), {
9187
+ return new AppServerSession(this.appServerSessionOptions(), {
8581
9188
  kind: "session",
8582
9189
  agentId,
8583
9190
  newConversation: true,
@@ -8603,13 +9210,13 @@ class LettaAgentClientBase {
8603
9210
  validateCreateSessionOptions(sessionOptions);
8604
9211
  if (this.backend === "remote") {
8605
9212
  if (looksLikeConversationId(id)) {
8606
- return new AppServerSession(this.remoteOptions(), {
9213
+ return new AppServerSession(this.appServerSessionOptions(), {
8607
9214
  kind: "session",
8608
9215
  conversationId: id,
8609
9216
  options
8610
9217
  });
8611
9218
  }
8612
- return new AppServerSession(this.remoteOptions(), {
9219
+ return new AppServerSession(this.appServerSessionOptions(), {
8613
9220
  kind: "session",
8614
9221
  agentId: id,
8615
9222
  defaultConversation: true,
@@ -8708,6 +9315,12 @@ class LettaAgentClientBase {
8708
9315
  }
8709
9316
  return this.options;
8710
9317
  }
9318
+ appServerSessionOptions() {
9319
+ return {
9320
+ ...this.remoteOptions(),
9321
+ beforeConnect: () => this.releaseIdleManagementConnection()
9322
+ };
9323
+ }
8711
9324
  getRepositoriesClient() {
8712
9325
  if (this.backend !== "cloud") {
8713
9326
  throw new Error('client.repositories is only available with backend: "cloud".');
@@ -8715,6 +9328,9 @@ class LettaAgentClientBase {
8715
9328
  this.repositoriesClient ??= new RepositoriesClient(this.cloudOptions());
8716
9329
  return this.repositoriesClient;
8717
9330
  }
9331
+ async releaseIdleManagementConnection() {
9332
+ await this.managementTransport?.releaseIdleConnection?.();
9333
+ }
8718
9334
  getManagementTransport() {
8719
9335
  if (this.managementTransport)
8720
9336
  return this.managementTransport;
@@ -8864,7 +9480,7 @@ ${output.trim()}` : ""}`));
8864
9480
  }
8865
9481
 
8866
9482
  // src/local-app-server-session.ts
8867
- function createLocalAppServerSession(options, mode) {
9483
+ function createLocalAppServerSession(options, mode, beforeConnect) {
8868
9484
  const appServer = options ?? {};
8869
9485
  const sessionOptions = {
8870
9486
  ...appServer.url !== undefined ? { url: appServer.url } : {
@@ -8876,7 +9492,9 @@ function createLocalAppServerSession(options, mode) {
8876
9492
  })
8877
9493
  },
8878
9494
  ...appServer.WebSocket !== undefined ? { WebSocket: appServer.WebSocket } : {},
8879
- ...appServer.requestTimeoutMs !== undefined ? { requestTimeoutMs: appServer.requestTimeoutMs } : {}
9495
+ ...appServer.requestTimeoutMs !== undefined ? { requestTimeoutMs: appServer.requestTimeoutMs } : {},
9496
+ ...appServer.pinGlobalAgent !== undefined ? { pinGlobalAgent: appServer.pinGlobalAgent } : {},
9497
+ ...beforeConnect ? { beforeConnect } : {}
8880
9498
  };
8881
9499
  return new AppServerSession(sessionOptions, mode);
8882
9500
  }
@@ -9276,6 +9894,8 @@ class Session {
9276
9894
  _sessionId = null;
9277
9895
  _conversationId = null;
9278
9896
  initialized = false;
9897
+ closed = false;
9898
+ initializePromise = null;
9279
9899
  externalTools = new Map;
9280
9900
  streamQueue = [];
9281
9901
  streamResolvers = [];
@@ -9298,9 +9918,28 @@ class Session {
9298
9918
  }
9299
9919
  }
9300
9920
  async initialize() {
9921
+ if (this.closed) {
9922
+ throw new Error("Session is closed");
9923
+ }
9924
+ if (this.initializePromise) {
9925
+ return this.initializePromise;
9926
+ }
9301
9927
  if (this.initialized) {
9302
9928
  throw new Error("Session already initialized");
9303
9929
  }
9930
+ const attempt = this.performInitialize();
9931
+ const memo = attempt.catch((error) => {
9932
+ this.cleanupFailedInitialize();
9933
+ throw error;
9934
+ }).finally(() => {
9935
+ if (this.initializePromise === memo) {
9936
+ this.initializePromise = null;
9937
+ }
9938
+ });
9939
+ this.initializePromise = memo;
9940
+ return memo;
9941
+ }
9942
+ async performInitialize() {
9304
9943
  sessionLog("init", "connecting transport...");
9305
9944
  await this.transport.connect();
9306
9945
  sessionLog("init", "transport connected, sending initialize request");
@@ -9325,11 +9964,14 @@ class Session {
9325
9964
  this._agentId = initMsg.agent_id;
9326
9965
  this._sessionId = initMsg.session_id;
9327
9966
  this._conversationId = initMsg.conversation_id;
9328
- this.initialized = true;
9329
- this.startBackgroundPump();
9330
9967
  if (this.externalTools.size > 0) {
9331
9968
  await this.registerExternalTools();
9332
9969
  }
9970
+ if (this.closed) {
9971
+ throw new Error("Session is closed");
9972
+ }
9973
+ this.initialized = true;
9974
+ this.startBackgroundPump();
9333
9975
  const allTools = [
9334
9976
  ...initMsg.tools,
9335
9977
  ...Array.from(this.externalTools.keys())
@@ -9360,6 +10002,16 @@ ${stderr}` : "";
9360
10002
  sessionLog("init", `ERROR: transport closed before init message received${detail}`);
9361
10003
  throw new Error(`Failed to initialize session - no init message received${detail}`);
9362
10004
  }
10005
+ cleanupFailedInitialize() {
10006
+ this.transport.close();
10007
+ this.closed = true;
10008
+ this.initialized = false;
10009
+ this._agentId = null;
10010
+ this._sessionId = null;
10011
+ this._conversationId = null;
10012
+ this.pumpClosed = true;
10013
+ this.resolveAllStreamWaiters(null);
10014
+ }
9363
10015
  async send(message) {
9364
10016
  if (!this.initialized) {
9365
10017
  sessionLog("send", "auto-initializing (not yet initialized)");
@@ -9861,7 +10513,7 @@ ${stderr}` : "";
9861
10513
  };
9862
10514
  } else if (hasCallback) {
9863
10515
  try {
9864
- const result = await this.options.canUseTool(toolName, req.input);
10516
+ const result = await this.options.canUseTool(toolName, req.input, buildCanUseToolContext(req, requestId));
9865
10517
  if (result.behavior === "allow") {
9866
10518
  response = {
9867
10519
  behavior: "allow",
@@ -9993,6 +10645,12 @@ ${stderr}` : "";
9993
10645
  async removeQueuedMessage(_itemId) {
9994
10646
  throw new Error("removeQueuedMessage() is not supported by the legacy stdio transport. Use a Cloud, Remote, or local app-server session.");
9995
10647
  }
10648
+ async getDeviceStatus(_options) {
10649
+ throw new Error("getDeviceStatus() is not supported by the legacy stdio transport. Use a Cloud, Remote, or local app-server session.");
10650
+ }
10651
+ onDeviceStatus(_listener) {
10652
+ throw new Error("onDeviceStatus() is not supported by the legacy stdio transport. Use a Cloud, Remote, or local app-server session.");
10653
+ }
9996
10654
  async bootstrapState(options = {}) {
9997
10655
  if (!this.initialized) {
9998
10656
  throw new Error("Session must be initialized before calling bootstrapState()");
@@ -10030,6 +10688,9 @@ ${stderr}` : "";
10030
10688
  throw new Error("updateToolset() is not supported by this session. Use a Cloud, Remote, or local agent session.");
10031
10689
  }
10032
10690
  close() {
10691
+ if (this.closed)
10692
+ return;
10693
+ this.closed = true;
10033
10694
  sessionLog("close", `closing session (agent=${this._agentId}, conversation=${this._conversationId})`);
10034
10695
  this.transport.close();
10035
10696
  this.pumpClosed = true;
@@ -10227,7 +10888,8 @@ class LettaAgentClient extends LettaAgentClientBase {
10227
10888
  })
10228
10889
  },
10229
10890
  ...localOptions?.WebSocket !== undefined ? { WebSocket: localOptions.WebSocket } : {},
10230
- ...localOptions?.requestTimeoutMs !== undefined ? { requestTimeoutMs: localOptions.requestTimeoutMs } : {}
10891
+ ...localOptions?.requestTimeoutMs !== undefined ? { requestTimeoutMs: localOptions.requestTimeoutMs } : {},
10892
+ ...localOptions?.idleLingerMs !== undefined ? { idleLingerMs: localOptions.idleLingerMs } : {}
10231
10893
  });
10232
10894
  }
10233
10895
  async createLocalAgent(options) {
@@ -10236,7 +10898,7 @@ class LettaAgentClient extends LettaAgentClientBase {
10236
10898
  const session2 = createLocalAppServerSession(localOptions.appServer, {
10237
10899
  kind: "create-agent",
10238
10900
  options
10239
- });
10901
+ }, () => this.releaseIdleManagementConnection());
10240
10902
  const initMsg2 = await session2.initialize();
10241
10903
  session2.close();
10242
10904
  return initMsg2.agentId;
@@ -10254,7 +10916,7 @@ class LettaAgentClient extends LettaAgentClientBase {
10254
10916
  agentId,
10255
10917
  newConversation: true,
10256
10918
  options
10257
- });
10919
+ }, () => this.releaseIdleManagementConnection());
10258
10920
  }
10259
10921
  if (agentId) {
10260
10922
  return new Session({ ...sessionOptions, agentId, newConversation: true });
@@ -10269,14 +10931,14 @@ class LettaAgentClient extends LettaAgentClientBase {
10269
10931
  kind: "session",
10270
10932
  conversationId: id,
10271
10933
  options
10272
- });
10934
+ }, () => this.releaseIdleManagementConnection());
10273
10935
  }
10274
10936
  return createLocalAppServerSession(localOptions.appServer, {
10275
10937
  kind: "session",
10276
10938
  agentId: id,
10277
10939
  defaultConversation: true,
10278
10940
  options
10279
- });
10941
+ }, () => this.releaseIdleManagementConnection());
10280
10942
  }
10281
10943
  if (looksLikeConversationId2(id)) {
10282
10944
  return new Session({ ...sessionOptions, conversationId: id });
@@ -10544,4 +11206,4 @@ export {
10544
11206
  CloudManagedSandboxExpiredError
10545
11207
  };
10546
11208
 
10547
- //# debugId=9E81BF84F1BD3BCB64756E2164756E21
11209
+ //# debugId=98DBA625B202A81364756E2164756E21