@letta-ai/letta-code 0.29.10 → 0.29.11

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/letta.js CHANGED
@@ -5462,7 +5462,7 @@ var package_default;
5462
5462
  var init_package = __esm(() => {
5463
5463
  package_default = {
5464
5464
  name: "@letta-ai/letta-code",
5465
- version: "0.29.10",
5465
+ version: "0.29.11",
5466
5466
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5467
5467
  type: "module",
5468
5468
  packageManager: "bun@1.3.0",
@@ -173413,7 +173413,7 @@ function socketForTransport(transport) {
173413
173413
  function refreshLegacySingleConnection(runtime) {
173414
173414
  const live = [...runtime.connections.values()].filter((connection) => isListenerTransportOpen(connection.writer));
173415
173415
  const only = live.length === 1 ? live[0] : null;
173416
- runtime.transport = only?.writer ?? runtime.processTransport;
173416
+ runtime.transport = runtime.processTransport ?? only?.writer ?? null;
173417
173417
  runtime.streamTransport = only?.streamWriter ?? null;
173418
173418
  runtime.socket = only ? socketForTransport(only.writer) : null;
173419
173419
  runtime.streamSocket = only?.streamWriter ? socketForTransport(only.streamWriter) : null;
@@ -173617,9 +173617,7 @@ class ProcessRuntimeTransport {
173617
173617
  }
173618
173618
  function getOrCreateProcessTransport(runtime) {
173619
173619
  runtime.processTransport ??= new ProcessRuntimeTransport(runtime);
173620
- if (runtime.connections.size !== 1) {
173621
- refreshLegacySingleConnection(runtime);
173622
- }
173620
+ refreshLegacySingleConnection(runtime);
173623
173621
  return runtime.processTransport;
173624
173622
  }
173625
173623
  var TO_SUBSCRIBERS, BROADCAST, resumeStateByRuntime;
@@ -174044,12 +174042,6 @@ var init_channel_turn_session = __esm(() => {
174044
174042
  });
174045
174043
 
174046
174044
  // src/websocket/listener/constants.ts
174047
- function isListenerPongStale(lastPongAt, now, timeoutMs) {
174048
- if (lastPongAt === null) {
174049
- return false;
174050
- }
174051
- return now - lastPongAt > timeoutMs;
174052
- }
174053
174045
  var MAX_RETRY_DURATION_MS, INITIAL_RETRY_DELAY_MS2 = 1000, MAX_RETRY_DELAY_MS = 30000, LISTENER_HEARTBEAT_INTERVAL_MS = 30000, LISTENER_PONG_TIMEOUT_MS = 90000, SYSTEM_REMINDER_RE, LLM_API_ERROR_MAX_RETRIES = 3, EMPTY_RESPONSE_MAX_RETRIES = 2, MAX_PRE_STREAM_RECOVERY = 2, MAX_POST_STOP_APPROVAL_RECOVERY = 2, PROVIDER_FALLBACK_MAP, PROVIDER_FALLBACK_NOTICE = "Anthropic API error; falling back to Bedrock...";
174054
174046
  var init_constants3 = __esm(() => {
174055
174047
  MAX_RETRY_DURATION_MS = 5 * 60 * 1000;
@@ -382751,6 +382743,7 @@ var init_backend2 = __esm(() => {
382751
382743
  var exports_check_approval = {};
382752
382744
  __export(exports_check_approval, {
382753
382745
  prepareMessageHistory: () => prepareMessageHistory,
382746
+ isResumedConversation: () => isResumedConversation,
382754
382747
  getResumeDataFromBackend: () => getResumeDataFromBackend,
382755
382748
  extractApprovals: () => extractApprovals
382756
382749
  });
@@ -382758,6 +382751,11 @@ function isBackfillEnabled() {
382758
382751
  const val = process.env.LETTA_BACKFILL;
382759
382752
  return val !== "0" && val !== "false";
382760
382753
  }
382754
+ function isResumedConversation(conversation) {
382755
+ if (!conversation)
382756
+ return true;
382757
+ return Boolean(conversation.summary?.trim() || (conversation.in_context_message_ids?.length ?? 0) > 0);
382758
+ }
382761
382759
  function approvalRequestsFromMessage(messageToCheck) {
382762
382760
  const approvalMsg = messageToCheck;
382763
382761
  const toolCalls = Array.isArray(approvalMsg.tool_calls) ? approvalMsg.tool_calls : approvalMsg.tool_call ? [approvalMsg.tool_call] : [];
@@ -383011,17 +383009,19 @@ async function getResumeDataFromBackend(agent2, conversationId, options3 = {}) {
383011
383009
  let inContextMessageIds;
383012
383010
  let messages = [];
383013
383011
  if (useConversationsApi) {
383012
+ let conversation;
383014
383013
  if (shouldFetchTail) {
383015
383014
  try {
383016
383015
  const tail = await fetchResumeTail(agent2.id, activeConversationId);
383017
383016
  messages = tail.messages;
383018
- inContextMessageIds = tail.conversation?.in_context_message_ids;
383017
+ conversation = tail.conversation;
383018
+ inContextMessageIds = conversation?.in_context_message_ids;
383019
383019
  } catch (backfillError) {
383020
383020
  debugWarn("check-approval", `Failed to load message history: ${backfillError instanceof Error ? backfillError.message : String(backfillError)}`);
383021
383021
  }
383022
383022
  }
383023
- if (!inContextMessageIds) {
383024
- const conversation = await getBackend().retrieveConversation(activeConversationId);
383023
+ if (!conversation) {
383024
+ conversation = await getBackend().retrieveConversation(activeConversationId);
383025
383025
  inContextMessageIds = conversation.in_context_message_ids;
383026
383026
  }
383027
383027
  const lastInContextId2 = inContextMessageIds?.at(-1);
@@ -383030,14 +383030,16 @@ async function getResumeDataFromBackend(agent2, conversationId, options3 = {}) {
383030
383030
  return {
383031
383031
  pendingApproval: null,
383032
383032
  pendingApprovals: [],
383033
- messageHistory: prepareMessageHistory(messages)
383033
+ messageHistory: prepareMessageHistory(messages),
383034
+ conversation
383034
383035
  };
383035
383036
  }
383036
383037
  const { pendingApproval: pendingApproval2, pendingApprovals: pendingApprovals2 } = await pendingApprovalsFromTailOrRetrieve(messages, lastInContextId2);
383037
383038
  return {
383038
383039
  pendingApproval: pendingApproval2,
383039
383040
  pendingApprovals: pendingApprovals2,
383040
- messageHistory: prepareMessageHistory(messages)
383041
+ messageHistory: prepareMessageHistory(messages),
383042
+ conversation
383041
383043
  };
383042
383044
  }
383043
383045
  inContextMessageIds = agentWithInContext.in_context_message_ids ?? agent2.message_ids;
@@ -436549,7 +436551,7 @@ function scheduleQueuePump(runtime, socket, opts, processQueuedTurn) {
436549
436551
  runtime.queuePumpScheduled = true;
436550
436552
  runtime.messageQueue = runtime.messageQueue.then(async () => {
436551
436553
  runtime.queuePumpScheduled = false;
436552
- if (runtime.listener !== getActiveRuntime() || runtime.listener.intentionallyClosed) {
436554
+ if (runtime.listener !== getActiveRuntime() || runtime.listener.intentionallyClosed || !isListenerTransportOpen(socket)) {
436553
436555
  return;
436554
436556
  }
436555
436557
  await drainQueuedMessages(runtime, socket, opts, processQueuedTurn);
@@ -436572,6 +436574,7 @@ var init_queue = __esm(async () => {
436572
436574
  init_error_reporting();
436573
436575
  init_channel_turn_session();
436574
436576
  init_runtime6();
436577
+ init_transport();
436575
436578
  await __promiseAll([
436576
436579
  init_image_policy(),
436577
436580
  init_protocol_outbound()
@@ -446311,6 +446314,9 @@ function replayPendingApprovalRequestsToConnection(runtime, connectionId) {
446311
446314
  }
446312
446315
  }
446313
446316
  function requestApprovalOverWS(runtime, socket, turnLease, requestId, controlRequest) {
446317
+ if (runtime.listener.intentionallyClosed) {
446318
+ return Promise.reject(new Error("Listener runtime stopped"));
446319
+ }
446314
446320
  const scope = {
446315
446321
  agent_id: runtime.agentId,
446316
446322
  conversation_id: runtime.conversationId
@@ -446324,9 +446330,6 @@ function requestApprovalOverWS(runtime, socket, turnLease, requestId, controlReq
446324
446330
  if (connectionIds.size === 0 && runtime.listener.connections.size === 0 && isListenerTransportOpen(socket)) {
446325
446331
  connectionIds.add(runtime.listener.connectionId ?? "legacy");
446326
446332
  }
446327
- if (connectionIds.size === 0 && !isListenerTransportOpen(socket) && runtime.listener.connections.size === 0) {
446328
- return Promise.reject(new Error("WebSocket not open"));
446329
- }
446330
446333
  const abortSignal = turnLease.signal;
446331
446334
  const isInterrupted = () => !runtime.turnLifecycle.isCurrent(turnLease) || abortSignal.aborted;
446332
446335
  if (isInterrupted()) {
@@ -457883,6 +457886,18 @@ function resolveChannelApprovalSource(runtime) {
457883
457886
  }
457884
457887
  return [...sourcesByScope.values()].at(-1) ?? null;
457885
457888
  }
457889
+ async function waitForApprovalTransportOpen(socket, shouldInterrupt) {
457890
+ if (isListenerTransportOpen(socket)) {
457891
+ return "open";
457892
+ }
457893
+ while (!shouldInterrupt()) {
457894
+ await new Promise((resolve31) => setTimeout(resolve31, APPROVAL_TRANSPORT_REOPEN_POLL_MS));
457895
+ if (isListenerTransportOpen(socket)) {
457896
+ return "open";
457897
+ }
457898
+ }
457899
+ return "interrupted";
457900
+ }
457886
457901
  async function handleApprovalStop(params) {
457887
457902
  const {
457888
457903
  approvals,
@@ -457906,6 +457921,8 @@ async function handleApprovalStop(params) {
457906
457921
  const classifyApprovals2 = dependencies4?.classifyApprovals ?? classifyApprovalsWithSuggestions;
457907
457922
  const executeApprovals = dependencies4?.executeApprovalBatch ?? executeApprovalBatch;
457908
457923
  const ensureSecretsHydrated = dependencies4?.ensureSecretsHydrated ?? ensureSecretsHydratedForAgent;
457924
+ const sendApprovalContinuation = dependencies4?.sendApprovalContinuation ?? sendApprovalContinuationWithRetry;
457925
+ const waitForTransportOpen = dependencies4?.waitForApprovalTransportOpen ?? waitForApprovalTransportOpen;
457909
457926
  if (approvals.length === 0) {
457910
457927
  return {
457911
457928
  kind: "error",
@@ -458077,6 +458094,17 @@ async function handleApprovalStop(params) {
458077
458094
  return interruptTermination();
458078
458095
  }
458079
458096
  const approvedDecisions = decisions.filter((decision) => decision.type === "approve");
458097
+ const executionRunId = runId || runtime.activeRunId || msgRunIds[msgRunIds.length - 1];
458098
+ let persistedExecutionResults;
458099
+ if (approvedDecisions.length > 0 && !isListenerTransportOpen(socket)) {
458100
+ const transportOpenResult = await waitForTransportOpen(socket, shouldInterrupt);
458101
+ if (transportOpenResult === "interrupted") {
458102
+ return interruptTermination();
458103
+ }
458104
+ }
458105
+ if (shouldInterrupt()) {
458106
+ return interruptTermination();
458107
+ }
458080
458108
  lastExecutingToolCallIds = approvedDecisions.map((decision) => decision.approval.toolCallId);
458081
458109
  runtime.turnLifecycle.setExecutingToolCallIds(turnLease, lastExecutingToolCallIds);
458082
458110
  setTurnLoopStatus(runtime, turnLease, "EXECUTING_CLIENT_SIDE_TOOL", {
@@ -458087,7 +458115,6 @@ async function handleApprovalStop(params) {
458087
458115
  agent_id: agentId,
458088
458116
  conversation_id: conversationId
458089
458117
  });
458090
- const executionRunId = runId || runtime.activeRunId || msgRunIds[msgRunIds.length - 1];
458091
458118
  emitToolExecutionStartedEvents(socket, runtime, {
458092
458119
  toolCalls: approvedDecisions.map((decision) => ({
458093
458120
  toolCallId: decision.approval.toolCallId,
@@ -458156,7 +458183,7 @@ async function handleApprovalStop(params) {
458156
458183
  if (!runtime.turnLifecycle.isCurrent(turnLease)) {
458157
458184
  return interruptTermination();
458158
458185
  }
458159
- const persistedExecutionResults = normalizeExecutionResultsForInterruptParity(runtime, executionResults, lastExecutingToolCallIds);
458186
+ persistedExecutionResults = normalizeExecutionResultsForInterruptParity(runtime, executionResults, lastExecutingToolCallIds);
458160
458187
  validateApprovalResultIds(decisions.map((decision) => ({
458161
458188
  approval: {
458162
458189
  toolCallId: decision.approval.toolCallId
@@ -458201,7 +458228,7 @@ async function handleApprovalStop(params) {
458201
458228
  try {
458202
458229
  const sendOptions = buildSendOptions() ?? {};
458203
458230
  const imageFailureModesByMessageOtid = mergeImageFailureModesByMessageOtid(sendOptions.imageFailureModesByMessageOtid, nextTurnInput.imageFailureModesByMessageOtid);
458204
- sendResult = await sendApprovalContinuationWithRetry(conversationId, nextInputWithSkillContent, {
458231
+ sendResult = await sendApprovalContinuation(conversationId, nextInputWithSkillContent, {
458205
458232
  ...sendOptions,
458206
458233
  ...imageFailureModesByMessageOtid ? { imageFailureModesByMessageOtid } : {},
458207
458234
  ...continuationWasFullyAutoHandled ? { allowResponseStateReuse: true } : {}
@@ -458261,6 +458288,7 @@ async function handleApprovalStop(params) {
458261
458288
  lastApprovalContinuationAccepted: true
458262
458289
  };
458263
458290
  }
458291
+ var APPROVAL_TRANSPORT_REOPEN_POLL_MS = 50;
458264
458292
  var init_turn_approval = __esm(async () => {
458265
458293
  init_registry();
458266
458294
  init_diff_preview();
@@ -458269,6 +458297,7 @@ var init_turn_approval = __esm(async () => {
458269
458297
  init_connection();
458270
458298
  init_secrets_sync();
458271
458299
  init_skill_injection();
458300
+ init_transport();
458272
458301
  init_turn_input_state();
458273
458302
  await __promiseAll([
458274
458303
  init_approval_execution(),
@@ -462247,7 +462276,7 @@ function createConnectionTurnProcessor(runtime) {
462247
462276
  if (!connection || connection.cancellation.signal.aborted) {
462248
462277
  return;
462249
462278
  }
462250
- await handleIncomingMessage(queuedTurn, connection.writer, scopedRuntime, connection.options.onStatusChange, connection.id, dequeuedBatch.batchId);
462279
+ await handleIncomingMessage(queuedTurn, getOrCreateProcessTransport(runtime), scopedRuntime, connection.options.onStatusChange, connection.id, dequeuedBatch.batchId);
462251
462280
  };
462252
462281
  }
462253
462282
  function cleanupListenerConnection(runtime, connectionId) {
@@ -463223,6 +463252,43 @@ var init_file_commands = __esm(async () => {
463223
463252
  ignoreConfigCache = new Map;
463224
463253
  });
463225
463254
 
463255
+ // src/websocket/listener/heartbeat.ts
463256
+ function createMissedPongWatchdog(maxUnansweredPings) {
463257
+ let lastPingAt = null;
463258
+ let unansweredPings = 0;
463259
+ return {
463260
+ shouldTerminate(lastPongAt) {
463261
+ if (lastPingAt !== null && lastPongAt !== null && lastPongAt >= lastPingAt) {
463262
+ unansweredPings = 0;
463263
+ }
463264
+ return unansweredPings >= maxUnansweredPings;
463265
+ },
463266
+ recordPing(sentAt) {
463267
+ lastPingAt = sentAt;
463268
+ unansweredPings += 1;
463269
+ }
463270
+ };
463271
+ }
463272
+ function startConnectionHeartbeat(runtime, transport, onStale, sendPing) {
463273
+ runtime.lastPongAt = Date.now();
463274
+ const maxUnansweredPings = Math.max(1, Math.ceil(LISTENER_PONG_TIMEOUT_MS / LISTENER_HEARTBEAT_INTERVAL_MS));
463275
+ const watchdog = createMissedPongWatchdog(maxUnansweredPings);
463276
+ runtime.heartbeatInterval = setInterval(() => {
463277
+ if (getListenerTransportKind(transport) === "websocket" && watchdog.shouldTerminate(runtime.lastPongAt)) {
463278
+ onStale();
463279
+ return;
463280
+ }
463281
+ const sentAt = Date.now();
463282
+ if (sendPing()) {
463283
+ watchdog.recordPing(sentAt);
463284
+ }
463285
+ }, LISTENER_HEARTBEAT_INTERVAL_MS);
463286
+ }
463287
+ var init_heartbeat = __esm(() => {
463288
+ init_constants3();
463289
+ init_transport();
463290
+ });
463291
+
463226
463292
  // src/types/app-server-info.ts
463227
463293
  var APP_SERVER_PROTOCOL_VERSION = 1;
463228
463294
 
@@ -465290,10 +465356,13 @@ function getParsedRuntimeScope(parsed) {
465290
465356
  conversation_id: typeof runtime.conversation_id === "string" ? runtime.conversation_id : "default"
465291
465357
  };
465292
465358
  }
465293
- function terminateControlAfterStreamClose(runtime, streamSocket) {
465359
+ function terminateControlAfterStreamClose(runtime, streamSocket, code2, reason) {
465294
465360
  if (runtime.streamSocket !== streamSocket) {
465295
465361
  return;
465296
465362
  }
465363
+ if (code2 === 1000 && reason.toString() === "Replaced by new connection") {
465364
+ runtime.intentionallyClosed = true;
465365
+ }
465297
465366
  runtime.streamSocket = null;
465298
465367
  runtime.streamTransport = null;
465299
465368
  const controlSocket = runtime.socket;
@@ -465702,6 +465771,23 @@ async function startConnectedListenerRuntime(runtime, transport, opts, processQu
465702
465771
  runtime.everConnected = true;
465703
465772
  opts.onConnected(opts.connectionId);
465704
465773
  emitInitialConnectionState(runtime, transport, opts.connectionId, options3);
465774
+ for (const conversationRuntime of runtime.conversationRuntimes.values()) {
465775
+ replayPendingApprovalRequestsToConnection(conversationRuntime, opts.connectionId);
465776
+ }
465777
+ if (shouldStartHeartbeat) {
465778
+ startConnectionHeartbeat(runtime, transport, () => {
465779
+ trackListenerError4("listener_pong_timeout", new Error(`No relay pong within ${LISTENER_PONG_TIMEOUT_MS}ms; terminating half-open socket to force reconnect`), "listener_heartbeat");
465780
+ runtime.socket?.terminate();
465781
+ }, () => {
465782
+ return safeTransportSend(transport, { type: "ping" }, "listener_ping_send_failed", "listener_heartbeat");
465783
+ });
465784
+ }
465785
+ const processTransport = getOrCreateProcessTransport(runtime);
465786
+ for (const conversationRuntime of runtime.conversationRuntimes.values()) {
465787
+ if (conversationRuntime.queueRuntime?.isEmpty === false) {
465788
+ scheduleQueuePump(conversationRuntime, processTransport, opts, processQueuedTurn);
465789
+ }
465790
+ }
465705
465791
  if (runtime.processServicesStarted) {
465706
465792
  return;
465707
465793
  }
@@ -465710,28 +465796,17 @@ async function startConnectedListenerRuntime(runtime, transport, opts, processQu
465710
465796
  const processServicesGeneration = runtime.processServicesGeneration + 1;
465711
465797
  runtime.processServicesGeneration = processServicesGeneration;
465712
465798
  const processServicesReady = (async () => {
465713
- const processTransport = getOrCreateProcessTransport(runtime);
465799
+ const processTransport2 = getOrCreateProcessTransport(runtime);
465714
465800
  installProcessEventRouting({
465715
465801
  runtime,
465716
- processTransport,
465802
+ processTransport: processTransport2,
465717
465803
  opts,
465718
465804
  processQueuedTurn
465719
465805
  });
465720
- if (shouldStartHeartbeat) {
465721
- runtime.lastPongAt = Date.now();
465722
- runtime.heartbeatInterval = setInterval(() => {
465723
- if (getListenerTransportKind(transport) === "websocket" && isListenerPongStale(runtime.lastPongAt, Date.now(), LISTENER_PONG_TIMEOUT_MS)) {
465724
- trackListenerError4("listener_pong_timeout", new Error(`No relay pong within ${LISTENER_PONG_TIMEOUT_MS}ms; terminating half-open socket to force reconnect`), "listener_heartbeat");
465725
- runtime.socket?.terminate();
465726
- return;
465727
- }
465728
- safeTransportSend(transport, { type: "ping" }, "listener_ping_send_failed", "listener_heartbeat");
465729
- }, LISTENER_HEARTBEAT_INTERVAL_MS);
465730
- }
465731
465806
  if (shouldStartCronScheduler) {
465732
- startScheduler(processTransport, opts, processQueuedTurn);
465807
+ startScheduler(processTransport2, opts, processQueuedTurn);
465733
465808
  }
465734
- await (options3.wireChannelIngress ?? wireChannelIngress)(runtime, processTransport, opts, processQueuedTurn);
465809
+ await (options3.wireChannelIngress ?? wireChannelIngress)(runtime, processTransport2, opts, processQueuedTurn);
465735
465810
  if (runtime.processServicesGeneration === processServicesGeneration) {
465736
465811
  runtime.processServicesStarted = true;
465737
465812
  }
@@ -465838,7 +465913,7 @@ async function attachOpenListenerSocket(runtime, socket, opts, options3 = {}) {
465838
465913
  if (isDebugEnabled()) {
465839
465914
  console.log(`[Listen] Stream WebSocket closed (code: ${code2}, reason: ${reason.toString()})`);
465840
465915
  }
465841
- terminateControlAfterStreamClose(runtime, streamSocket);
465916
+ terminateControlAfterStreamClose(runtime, streamSocket, code2, reason);
465842
465917
  });
465843
465918
  }
465844
465919
  await options3.startupReady;
@@ -466021,20 +466096,17 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
466021
466096
  reason: reason.toString()
466022
466097
  });
466023
466098
  fileCommandSession.dispose();
466024
- invalidateProcessServices(runtime);
466025
- const channelRegistry = getChannelRegistry();
466026
- if (channelRegistry) {
466027
- channelRegistry.pause();
466028
- }
466029
- for (const conversationRuntime of runtime.conversationRuntimes.values()) {
466030
- conversationRuntime.queuedMessagesByItemId.clear();
466031
- if (conversationRuntime.queueRuntime) {
466032
- conversationRuntime.queueRuntime.clear("shutdown");
466033
- }
466034
- }
466099
+ const reasonText = reason.toString();
466100
+ const terminalClose = runtime.intentionallyClosed || code2 === 1008 || code2 === 1000 && reasonText === "Replaced by new connection";
466101
+ clearRuntimeTimers(runtime);
466035
466102
  if (isDebugEnabled()) {
466036
466103
  console.log(`[Listen] WebSocket disconnected (code: ${code2}, reason: ${reason.toString()})`);
466037
466104
  }
466105
+ if (!terminalClose) {
466106
+ for (const conversationRuntime of runtime.conversationRuntimes.values()) {
466107
+ rejectPendingApprovalResolversForConnection(conversationRuntime, opts.connectionId, "Listener connection closed");
466108
+ }
466109
+ }
466038
466110
  suspendListenerConnection(runtime, opts.connectionId);
466039
466111
  killAllTerminals();
466040
466112
  clearListenerWarmState(runtime);
@@ -466047,24 +466119,23 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
466047
466119
  runtime.socket = null;
466048
466120
  runtime.streamSocket = null;
466049
466121
  runtime.streamTransport = null;
466050
- for (const conversationRuntime of runtime.conversationRuntimes.values()) {
466051
- rejectPendingApprovalResolvers(conversationRuntime, "WebSocket disconnected");
466052
- clearConversationRuntimeState(conversationRuntime);
466053
- evictConversationRuntimeIfIdle(conversationRuntime);
466054
- }
466055
- if (runtime.intentionallyClosed) {
466056
- opts.onDisconnected();
466057
- return;
466058
- }
466059
- if (code2 === 1008) {
466060
- if (isDebugEnabled()) {
466061
- console.log("[Listen] Environment not found, re-registering...");
466122
+ if (terminalClose) {
466123
+ if (getActiveRuntime() === runtime) {
466124
+ setActiveRuntime(null);
466062
466125
  }
466063
- if (opts.onNeedsReregister) {
466064
- opts.onNeedsReregister();
466065
- } else {
466066
- opts.onDisconnected();
466126
+ stopRuntime(runtime, true);
466127
+ if (code2 === 1008) {
466128
+ if (isDebugEnabled()) {
466129
+ console.log("[Listen] Environment not found, re-registering...");
466130
+ }
466131
+ if (opts.onNeedsReregister) {
466132
+ opts.onNeedsReregister();
466133
+ } else {
466134
+ opts.onDisconnected();
466135
+ }
466136
+ return;
466067
466137
  }
466138
+ opts.onDisconnected();
466068
466139
  return;
466069
466140
  }
466070
466141
  const nextAttempt = runtime.hasSuccessfulConnection ? 0 : attempt + 1;
@@ -466095,7 +466166,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
466095
466166
  if (isDebugEnabled()) {
466096
466167
  console.log(`[Listen] Stream WebSocket closed (code: ${code2}, reason: ${reason.toString()})`);
466097
466168
  }
466098
- terminateControlAfterStreamClose(runtime, streamSocket);
466169
+ terminateControlAfterStreamClose(runtime, streamSocket, code2, reason);
466099
466170
  });
466100
466171
  }
466101
466172
  }
@@ -466128,6 +466199,7 @@ var init_lifecycle = __esm(async () => {
466128
466199
  init_connection();
466129
466200
  init_constants3();
466130
466201
  init_cwd();
466202
+ init_heartbeat();
466131
466203
  init_permission_mode();
466132
466204
  init_runtime6();
466133
466205
  init_transport();
@@ -536552,6 +536624,7 @@ function App2({
536552
536624
  startupApprovals = [],
536553
536625
  messageHistory = [],
536554
536626
  resumedExistingConversation = false,
536627
+ startupConversationTitleEligible = false,
536555
536628
  tokenStreaming = false,
536556
536629
  reasoningTabCycleEnabled: initialReasoningTabCycleEnabled = false,
536557
536630
  showCompactions = false,
@@ -537035,7 +537108,7 @@ function App2({
537035
537108
  })());
537036
537109
  const _systemPromptRecompileByConversationRef = import_react120.useRef(new Map);
537037
537110
  const _queuedSystemPromptRecompileByConversationRef = import_react120.useRef(new Set);
537038
- const shouldAutoGenerateConversationTitleRef = import_react120.useRef(!resumedExistingConversation);
537111
+ const shouldAutoGenerateConversationTitleRef = import_react120.useRef(!resumedExistingConversation || startupConversationTitleEligible);
537039
537112
  const isAutoConversationTitleInFlightRef = import_react120.useRef(false);
537040
537113
  const shouldAutoGenerateConversationDescriptionRef = import_react120.useRef(!resumedExistingConversation);
537041
537114
  const isAutoConversationDescriptionInFlightRef = import_react120.useRef(false);
@@ -541467,11 +541540,11 @@ Error: ${message}`);
541467
541540
  }
541468
541541
  if (specifiedConversationId) {
541469
541542
  conversationIdToUse = specifiedConversationId;
541470
- setResumedExistingConversation(true);
541471
541543
  try {
541472
541544
  setLoadingState("checking");
541473
541545
  const data = await getResumeDataFromBackend(agent2, specifiedConversationId);
541474
541546
  setResumeData(data);
541547
+ setResumedExistingConversation(true);
541475
541548
  } catch (error54) {
541476
541549
  if (isBackendNotFoundError3(error54)) {
541477
541550
  console.error(`Conversation ${specifiedConversationId} not found`);
@@ -541637,6 +541710,7 @@ Error during initialization: ${message}`);
541637
541710
  });
541638
541711
  }
541639
541712
  const appLoadingState = loadingState;
541713
+ const startupConversationTitleEligible = !isResumedConversation(resumeData?.conversation);
541640
541714
  if (!agentId || !conversationId) {
541641
541715
  return React14.createElement(App3, {
541642
541716
  agentId: "loading",
@@ -541647,6 +541721,7 @@ Error during initialization: ${message}`);
541647
541721
  startupApprovals: resumeData?.pendingApprovals ?? EMPTY_APPROVAL_ARRAY,
541648
541722
  messageHistory: resumeData?.messageHistory ?? EMPTY_MESSAGE_ARRAY,
541649
541723
  resumedExistingConversation,
541724
+ startupConversationTitleEligible,
541650
541725
  tokenStreaming: settings3.tokenStreaming,
541651
541726
  reasoningTabCycleEnabled: settings3.reasoningTabCycleEnabled === true,
541652
541727
  showCompactions: settings3.showCompactions,
@@ -541670,6 +541745,7 @@ Error during initialization: ${message}`);
541670
541745
  startupApprovals: resumeData?.pendingApprovals ?? EMPTY_APPROVAL_ARRAY,
541671
541746
  messageHistory: resumeData?.messageHistory ?? EMPTY_MESSAGE_ARRAY,
541672
541747
  resumedExistingConversation,
541748
+ startupConversationTitleEligible,
541673
541749
  tokenStreaming: settings3.tokenStreaming,
541674
541750
  reasoningTabCycleEnabled: settings3.reasoningTabCycleEnabled === true,
541675
541751
  showCompactions: settings3.showCompactions,
@@ -543363,4 +543439,4 @@ function registerBunOAuthFlows() {
543363
543439
  registerBunOAuthFlows();
543364
543440
  await init_src5().then(() => exports_src2);
543365
543441
 
543366
- //# debugId=5CF9067B1645DBD964756E2164756E21
543442
+ //# debugId=B4730B9E51AC5C6E64756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.29.10",
3
+ "version": "0.29.11",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.0",
@@ -5,7 +5,7 @@
5
5
  "src/backend/local/local-backend.ts": 1014,
6
6
  "src/backend/local/local-store.ts": 3594,
7
7
  "src/backend/pi-stream-adapter.test.ts": 1304,
8
- "src/cli/app/AppCoordinator.tsx": 5206,
8
+ "src/cli/app/AppCoordinator.tsx": 5203,
9
9
  "src/cli/app/AppView.tsx": 1753,
10
10
  "src/cli/app/use-approval-flow.ts": 1163,
11
11
  "src/cli/app/use-configuration-handlers.ts": 1433,
@@ -24,7 +24,7 @@
24
24
  "src/cli/subcommands/skills.ts": 1264,
25
25
  "src/headless.ts": 5244,
26
26
  "src/hooks/integration.test.ts": 1147,
27
- "src/index.ts": 2802,
27
+ "src/index.ts": 2801,
28
28
  "src/mods/learning-harness.ts": 2434,
29
29
  "src/mods/mod-engine.test.ts": 2153,
30
30
  "src/mods/mod-engine.ts": 1847,
@@ -47,7 +47,7 @@
47
47
  "src/websocket/listener/commands/channels.ts": 1315,
48
48
  "src/websocket/listener/commands/memory.ts": 1114,
49
49
  "src/websocket/listener/file-commands.ts": 1030,
50
- "src/websocket/listener/lifecycle.ts": 1658,
50
+ "src/websocket/listener/lifecycle.ts": 1656,
51
51
  "src/websocket/listener/protocol-inbound.ts": 2293,
52
52
  "src/websocket/listener/protocol-outbound.ts": 1100
53
53
  }