@letta-ai/letta-code 0.21.13 → 0.21.15

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 (2) hide show
  1. package/letta.js +205 -45
  2. package/package.json +1 -1
package/letta.js CHANGED
@@ -3269,7 +3269,7 @@ var package_default;
3269
3269
  var init_package = __esm(() => {
3270
3270
  package_default = {
3271
3271
  name: "@letta-ai/letta-code",
3272
- version: "0.21.13",
3272
+ version: "0.21.15",
3273
3273
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3274
3274
  type: "module",
3275
3275
  bin: {
@@ -73286,6 +73286,9 @@ function isCloudflareEdge52xHtmlError2(text) {
73286
73286
  const has52xCode = CLOUDFLARE_EDGE_5XX_MARKER_PATTERN2.test(text) || CLOUDFLARE_EDGE_5XX_TITLE_PATTERN2.test(text);
73287
73287
  return hasCloudflare && hasHtml && has52xCode;
73288
73288
  }
73289
+ function isCloudflareEdge52xErrorText(text) {
73290
+ return CLOUDFLARE_EDGE_5XX_FORMATTED_PATTERN.test(text) || isCloudflareEdge52xHtmlError2(text);
73291
+ }
73289
73292
  function parseCloudflareEdgeError2(text) {
73290
73293
  if (!isCloudflareEdge52xHtmlError2(text))
73291
73294
  return;
@@ -73638,7 +73641,7 @@ Upgrade at: ${LETTA_USAGE_URL2}
73638
73641
  Delete ${resourceType} at: ${LETTA_AGENTS_URL2}`;
73639
73642
  }
73640
73643
  if (isCreditExhaustedError2(e, reasons)) {
73641
- return `Your account is out of credits for hosted inference. Add credits, enable auto-recharge, or upgrade at ${LETTA_USAGE_URL2}. You can also connect your own provider keys with /connect.`;
73644
+ return `Your account does not have credits for this model. Add your own API keys or upgrade your plan to purchase credits.`;
73642
73645
  }
73643
73646
  const tierUsageLimitMsg = getTierUsageLimitMessage2(reasons);
73644
73647
  if (tierUsageLimitMsg)
@@ -73704,7 +73707,7 @@ ${createAgentLink2(runId, agentId, conversationId)}` : baseError;
73704
73707
  function getRetryStatusMessage(errorDetail) {
73705
73708
  if (!errorDetail)
73706
73709
  return DEFAULT_RETRY_MESSAGE;
73707
- if (parseCloudflareEdgeError2(errorDetail))
73710
+ if (isCloudflareEdge52xErrorText(errorDetail))
73708
73711
  return null;
73709
73712
  if (checkZaiError(errorDetail))
73710
73713
  return "Z.ai API error, retrying...";
@@ -73731,7 +73734,7 @@ function createAgentLink2(runId, agentId, conversationId) {
73731
73734
  const url = buildChatUrl(agentId, { conversationId });
73732
73735
  return `View agent: \x1B]8;;${url}\x1B\\${agentId}\x1B]8;;\x1B\\ (run: ${runId})`;
73733
73736
  }
73734
- var LETTA_USAGE_URL2, LETTA_AGENTS_URL2, CLOUDFLARE_EDGE_5XX_MARKER_PATTERN2, CLOUDFLARE_EDGE_5XX_TITLE_PATTERN2, CHATGPT_USAGE_LIMIT_HINT2 = "Switch models with /model, or connect your own provider keys with /connect.", ENCRYPTED_CONTENT_HINT2, DEFAULT_RETRY_MESSAGE = "Unexpected downstream LLM API error, retrying...", ENDPOINT_TYPE_DISPLAY_NAMES;
73737
+ var LETTA_USAGE_URL2, LETTA_AGENTS_URL2, CLOUDFLARE_EDGE_5XX_MARKER_PATTERN2, CLOUDFLARE_EDGE_5XX_TITLE_PATTERN2, CLOUDFLARE_EDGE_5XX_FORMATTED_PATTERN, CHATGPT_USAGE_LIMIT_HINT2 = "Switch models with /model, or connect your own provider keys with /connect.", ENCRYPTED_CONTENT_HINT2, DEFAULT_RETRY_MESSAGE = "Unexpected downstream LLM API error, retrying...", ENDPOINT_TYPE_DISPLAY_NAMES;
73735
73738
  var init_errorFormatter = __esm(() => {
73736
73739
  init_error();
73737
73740
  init_errorContext();
@@ -73740,6 +73743,7 @@ var init_errorFormatter = __esm(() => {
73740
73743
  LETTA_AGENTS_URL2 = buildAppUrl("/projects/default-project/agents");
73741
73744
  CLOUDFLARE_EDGE_5XX_MARKER_PATTERN2 = /(^|\s)(502|52[0-6])\s*<!doctype html|error code\s*(502|52[0-6])/i;
73742
73745
  CLOUDFLARE_EDGE_5XX_TITLE_PATTERN2 = /\|\s*(502|52[0-6])\s*:/i;
73746
+ CLOUDFLARE_EDGE_5XX_FORMATTED_PATTERN = /\bCloudflare\s+(502|52[0-6])\b/i;
73743
73747
  ENCRYPTED_CONTENT_HINT2 = [
73744
73748
  "",
73745
73749
  "This occurs when the conversation contains messages with encrypted",
@@ -73766,7 +73770,7 @@ import { randomUUID as randomUUID3 } from "node:crypto";
73766
73770
  function isCloudflareEdge52xDetail(detail) {
73767
73771
  if (typeof detail !== "string")
73768
73772
  return false;
73769
- return isCloudflareEdge52xHtmlError2(detail);
73773
+ return isCloudflareEdge52xErrorText(detail);
73770
73774
  }
73771
73775
  function isQuotaLimitErrorDetail(detail) {
73772
73776
  return hasNonRetryableQuotaDetail(detail);
@@ -74338,6 +74342,84 @@ var init_check_approval = __esm(() => {
74338
74342
  ];
74339
74343
  });
74340
74344
 
74345
+ // src/utils/createRelayedAbortController.ts
74346
+ function createRelayedAbortController(parentSignal) {
74347
+ const controller = new AbortController;
74348
+ if (!parentSignal) {
74349
+ return {
74350
+ controller,
74351
+ signal: controller.signal,
74352
+ cleanup: () => {}
74353
+ };
74354
+ }
74355
+ if (parentSignal.aborted) {
74356
+ controller.abort(parentSignal.reason);
74357
+ return {
74358
+ controller,
74359
+ signal: controller.signal,
74360
+ cleanup: () => {}
74361
+ };
74362
+ }
74363
+ const relayAbort = () => {
74364
+ controller.abort(parentSignal.reason);
74365
+ };
74366
+ parentSignal.addEventListener("abort", relayAbort, { once: true });
74367
+ return {
74368
+ controller,
74369
+ signal: controller.signal,
74370
+ cleanup: () => {
74371
+ parentSignal.removeEventListener("abort", relayAbort);
74372
+ }
74373
+ };
74374
+ }
74375
+
74376
+ // src/utils/streamAbortRelay.ts
74377
+ function composeCleanups(first, second) {
74378
+ let cleaned = false;
74379
+ return () => {
74380
+ if (cleaned) {
74381
+ return;
74382
+ }
74383
+ cleaned = true;
74384
+ first();
74385
+ second();
74386
+ };
74387
+ }
74388
+ function createStreamAbortRelay(parentSignal) {
74389
+ if (!parentSignal) {
74390
+ return null;
74391
+ }
74392
+ const requestAbort = createRelayedAbortController(parentSignal);
74393
+ let cleaned = false;
74394
+ const cleanup = () => {
74395
+ if (cleaned) {
74396
+ return;
74397
+ }
74398
+ cleaned = true;
74399
+ requestAbort.cleanup();
74400
+ };
74401
+ return {
74402
+ signal: requestAbort.signal,
74403
+ attach(stream2) {
74404
+ const existingCleanup = streamAbortRelayCleanupByStream.get(stream2);
74405
+ streamAbortRelayCleanupByStream.set(stream2, existingCleanup ? composeCleanups(existingCleanup, cleanup) : cleanup);
74406
+ },
74407
+ cleanup
74408
+ };
74409
+ }
74410
+ function cleanupStreamAbortRelay(stream2) {
74411
+ const cleanup = streamAbortRelayCleanupByStream.get(stream2);
74412
+ if (!cleanup) {
74413
+ return;
74414
+ }
74415
+ streamAbortRelayCleanupByStream.delete(stream2);
74416
+ cleanup();
74417
+ }
74418
+ var streamAbortRelayCleanupByStream;
74419
+ var init_streamAbortRelay = __esm(() => {
74420
+ streamAbortRelayCleanupByStream = new WeakMap;
74421
+ });
74422
+
74341
74423
  // src/agent/approval-result-normalization.ts
74342
74424
  function normalizeToolReturnText(value) {
74343
74425
  if (typeof value === "string")
@@ -74582,19 +74664,23 @@ async function sendMessageStream(conversationId, messages, opts = { streamTokens
74582
74664
  const firstOtid = messages[0]?.otid;
74583
74665
  debugLog("send-message-stream", "request_start conversation_id=%s agent_id=%s messages=%s otid=%s stream_tokens=%s background=%s max_retries=%s", resolvedConversationId, opts.agentId ?? "none", messageSummary || "(empty)", firstOtid ?? "none", opts.streamTokens ?? true, opts.background ?? true, requestOptions.maxRetries ?? "default");
74584
74666
  let stream2;
74667
+ const abortRelay = createStreamAbortRelay(requestOptions.signal);
74585
74668
  try {
74586
74669
  stream2 = await client.conversations.messages.create(resolvedConversationId, requestBody, {
74587
74670
  ...requestOptions,
74671
+ ...abortRelay ? { signal: abortRelay.signal } : {},
74588
74672
  headers: {
74589
74673
  ...requestOptions.headers ?? {},
74590
74674
  ...extraHeaders
74591
74675
  }
74592
74676
  });
74593
74677
  } catch (error) {
74678
+ abortRelay?.cleanup();
74594
74679
  debugWarn("send-message-stream", "request_error conversation_id=%s otid=%s status=%s error=%s", resolvedConversationId, firstOtid ?? "none", error?.status ?? "none", error instanceof Error ? error.message : String(error));
74595
74680
  throw error;
74596
74681
  }
74597
74682
  debugLog("send-message-stream", "request_ok conversation_id=%s otid=%s", resolvedConversationId, firstOtid ?? "none");
74683
+ abortRelay?.attach(stream2);
74598
74684
  if (requestStartTime !== undefined) {
74599
74685
  streamRequestStartTimes.set(stream2, requestStartTime);
74600
74686
  }
@@ -74611,6 +74697,7 @@ async function sendMessageStream(conversationId, messages, opts = { streamTokens
74611
74697
  var streamRequestStartTimes, streamToolContextIds, streamRequestContexts;
74612
74698
  var init_message = __esm(async () => {
74613
74699
  init_debug();
74700
+ init_streamAbortRelay();
74614
74701
  init_timing();
74615
74702
  init_approval_result_normalization();
74616
74703
  init_clientSkills();
@@ -76701,6 +76788,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
76701
76788
  if (abortSignal) {
76702
76789
  abortSignal.removeEventListener("abort", abortHandler);
76703
76790
  }
76791
+ cleanupStreamAbortRelay(stream2);
76704
76792
  clearLastSDKDiagnostic();
76705
76793
  }
76706
76794
  if (!stopReason && streamProcessor.stopReason) {
@@ -76792,15 +76880,23 @@ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onF
76792
76880
  const client = await lazyClient();
76793
76881
  buffers.commitGeneration = (buffers.commitGeneration || 0) + 1;
76794
76882
  buffers.interrupted = false;
76795
- const resumeStream = runIdSource === "otid" && streamOtid && streamRequestContext ? await client.conversations.messages.stream(streamRequestContext.resolvedConversationId, {
76796
- agent_id: streamRequestContext.conversationId === "default" ? streamRequestContext.agentId ?? undefined : undefined,
76797
- otid: streamOtid,
76798
- starting_after: result.lastSeqId ?? 0,
76799
- batch_size: 1000
76800
- }) : await client.runs.messages.stream(runIdToResume, {
76801
- starting_after: result.lastSeqId ?? 0,
76802
- batch_size: 1000
76803
- });
76883
+ const resumeAbortRelay = createStreamAbortRelay(abortSignal);
76884
+ let resumeStream;
76885
+ try {
76886
+ resumeStream = runIdSource === "otid" && streamOtid && streamRequestContext ? await client.conversations.messages.stream(streamRequestContext.resolvedConversationId, {
76887
+ agent_id: streamRequestContext.conversationId === "default" ? streamRequestContext.agentId ?? undefined : undefined,
76888
+ otid: streamOtid,
76889
+ starting_after: result.lastSeqId ?? 0,
76890
+ batch_size: 1000
76891
+ }, resumeAbortRelay ? { signal: resumeAbortRelay.signal } : undefined) : await client.runs.messages.stream(runIdToResume, {
76892
+ starting_after: result.lastSeqId ?? 0,
76893
+ batch_size: 1000
76894
+ }, resumeAbortRelay ? { signal: resumeAbortRelay.signal } : undefined);
76895
+ } catch (resumeError) {
76896
+ resumeAbortRelay?.cleanup();
76897
+ throw resumeError;
76898
+ }
76899
+ resumeAbortRelay?.attach(resumeStream);
76804
76900
  const resumeResult = await drainStream(resumeStream, buffers, refresh, abortSignal, undefined, onChunkProcessed, contextTracker, seenSeqIdThreshold, true);
76805
76901
  if (resumeResult.stopReason !== "error") {
76806
76902
  debugWarn("stream", "[MID-STREAM RESUME] ✅ Success (runId=%s, stopReason=%s)", runIdToResume, resumeResult.stopReason);
@@ -76865,6 +76961,7 @@ var FALLBACK_RUN_DISCOVERY_TIMEOUT_MS = 5000;
76865
76961
  var init_stream = __esm(async () => {
76866
76962
  init_error();
76867
76963
  init_debug();
76964
+ init_streamAbortRelay();
76868
76965
  init_timing();
76869
76966
  init_chunkLog();
76870
76967
  await __promiseAll([
@@ -80013,7 +80110,7 @@ function shouldAttemptPostStopApprovalRecovery(params) {
80013
80110
  maxRetries: MAX_POST_STOP_APPROVAL_RECOVERY
80014
80111
  });
80015
80112
  }
80016
- async function isRetriablePostStopError(stopReason, lastRunId) {
80113
+ async function isRetriablePostStopError(stopReason, lastRunId, fallbackDetail) {
80017
80114
  if (stopReason === "llm_api_error") {
80018
80115
  return true;
80019
80116
  }
@@ -80031,7 +80128,7 @@ async function isRetriablePostStopError(stopReason, lastRunId) {
80031
80128
  return false;
80032
80129
  }
80033
80130
  if (!lastRunId) {
80034
- return false;
80131
+ return shouldRetryRunMetadataError(undefined, fallbackDetail);
80035
80132
  }
80036
80133
  try {
80037
80134
  const client = await getClient();
@@ -80041,7 +80138,7 @@ async function isRetriablePostStopError(stopReason, lastRunId) {
80041
80138
  const detail = metaError?.detail ?? metaError?.error?.detail ?? "";
80042
80139
  return shouldRetryRunMetadataError(errorType, detail);
80043
80140
  } catch {
80044
- return false;
80141
+ return shouldRetryRunMetadataError(undefined, fallbackDetail);
80045
80142
  }
80046
80143
  }
80047
80144
  async function drainRecoveryStreamWithEmission(recoveryStream, socket, runtime, params) {
@@ -80804,15 +80901,23 @@ async function sendMessageStreamWithRetry(conversationId, messages, opts, socket
80804
80901
  try {
80805
80902
  const client = await getClient();
80806
80903
  const messageOtid = messages.map((item) => item.otid).find((value) => typeof value === "string");
80904
+ const resumeAbortRelay = createStreamAbortRelay(abortSignal);
80807
80905
  if (abortSignal?.aborted) {
80808
80906
  throw new Error("Cancelled by user");
80809
80907
  }
80810
- return await client.conversations.messages.stream(conversationId, {
80811
- agent_id: conversationId === "default" ? runtime.agentId ?? undefined : undefined,
80812
- otid: messageOtid ?? undefined,
80813
- starting_after: 0,
80814
- batch_size: 1000
80815
- });
80908
+ try {
80909
+ const resumeStream = await client.conversations.messages.stream(conversationId, {
80910
+ agent_id: conversationId === "default" ? runtime.agentId ?? undefined : undefined,
80911
+ otid: messageOtid ?? undefined,
80912
+ starting_after: 0,
80913
+ batch_size: 1000
80914
+ }, resumeAbortRelay ? { signal: resumeAbortRelay.signal } : undefined);
80915
+ resumeAbortRelay?.attach(resumeStream);
80916
+ return resumeStream;
80917
+ } catch (resumeError) {
80918
+ resumeAbortRelay?.cleanup();
80919
+ throw resumeError;
80920
+ }
80816
80921
  } catch (resumeError) {
80817
80922
  if (abortSignal?.aborted) {
80818
80923
  throw new Error("Cancelled by user");
@@ -80927,15 +81032,23 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
80927
81032
  try {
80928
81033
  const client = await getClient();
80929
81034
  const messageOtid = messages.map((item) => item.otid).find((value) => typeof value === "string");
81035
+ const resumeAbortRelay = createStreamAbortRelay(abortSignal);
80930
81036
  if (abortSignal?.aborted) {
80931
81037
  throw new Error("Cancelled by user");
80932
81038
  }
80933
- return await client.conversations.messages.stream(conversationId, {
80934
- agent_id: conversationId === "default" ? runtime.agentId ?? undefined : undefined,
80935
- otid: messageOtid ?? undefined,
80936
- starting_after: 0,
80937
- batch_size: 1000
80938
- });
81039
+ try {
81040
+ const resumeStream = await client.conversations.messages.stream(conversationId, {
81041
+ agent_id: conversationId === "default" ? runtime.agentId ?? undefined : undefined,
81042
+ otid: messageOtid ?? undefined,
81043
+ starting_after: 0,
81044
+ batch_size: 1000
81045
+ }, resumeAbortRelay ? { signal: resumeAbortRelay.signal } : undefined);
81046
+ resumeAbortRelay?.attach(resumeStream);
81047
+ return resumeStream;
81048
+ } catch (resumeError) {
81049
+ resumeAbortRelay?.cleanup();
81050
+ throw resumeError;
81051
+ }
80939
81052
  } catch (resumeError) {
80940
81053
  if (abortSignal?.aborted) {
80941
81054
  throw new Error("Cancelled by user");
@@ -80965,6 +81078,7 @@ var init_send = __esm(async () => {
80965
81078
  init_errorFormatter();
80966
81079
  init_diffPreview();
80967
81080
  init_interactivePolicy();
81081
+ init_streamAbortRelay();
80968
81082
  init_constants2();
80969
81083
  init_cwd();
80970
81084
  init_permissionMode();
@@ -81821,7 +81935,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
81821
81935
  turnToolContextId = getStreamToolContextId(stream2);
81822
81936
  continue;
81823
81937
  }
81824
- const retriable = await isRetriablePostStopError(stopReason || "error", lastRunId);
81938
+ const retriable = await isRetriablePostStopError(stopReason || "error", lastRunId, errorDetail);
81825
81939
  if (retriable && llmApiErrorRetries < LLM_API_ERROR_MAX_RETRIES) {
81826
81940
  llmApiErrorRetries += 1;
81827
81941
  const attempt = llmApiErrorRetries;
@@ -83809,6 +83923,19 @@ function runDetachedListenerTask(commandName, task2) {
83809
83923
  }
83810
83924
  });
83811
83925
  }
83926
+ async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
83927
+ const syncScopedRuntime = getOrCreateScopedRuntime(listenerRuntime, scope.agent_id, scope.conversation_id);
83928
+ const recoverFn = opts?.recoverApprovalStateForSync ?? recoverApprovalStateForSync;
83929
+ try {
83930
+ await recoverFn(syncScopedRuntime, scope);
83931
+ } catch (error) {
83932
+ trackListenerError("listener_sync_recovery_failed", error, "listener_sync_recovery");
83933
+ if (isDebugEnabled()) {
83934
+ console.warn("[Listen] Sync approval recovery failed:", error);
83935
+ }
83936
+ }
83937
+ emitStateSync(socket, listenerRuntime, scope);
83938
+ }
83812
83939
  function getParsedRuntimeScope(parsed) {
83813
83940
  if (!parsed || typeof parsed !== "object" || !("runtime" in parsed)) {
83814
83941
  return null;
@@ -84944,9 +85071,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
84944
85071
  console.log(`[Listen V2] Dropping sync: runtime mismatch or closed`);
84945
85072
  return;
84946
85073
  }
84947
- const syncScopedRuntime = getOrCreateScopedRuntime(runtime, parsed.runtime.agent_id, parsed.runtime.conversation_id);
84948
- await recoverApprovalStateForSync(syncScopedRuntime, parsed.runtime);
84949
- emitStateSync(socket, runtime, parsed.runtime);
85074
+ await replaySyncStateForRuntime(runtime, socket, parsed.runtime);
84950
85075
  return;
84951
85076
  }
84952
85077
  if (parsed.type === "input") {
@@ -85911,6 +86036,7 @@ var init_client4 = __esm(async () => {
85911
86036
  handleCreateAgentCommand,
85912
86037
  handleReflectionSettingsCommand,
85913
86038
  scheduleQueuePump,
86039
+ replaySyncStateForRuntime,
85914
86040
  recoverApprovalStateForSync,
85915
86041
  clearRecoveredApprovalStateForScope: (runtime, scope) => clearRecoveredApprovalStateForScope(asListenerRuntimeForTests(runtime), scope),
85916
86042
  emitStateSync
@@ -89774,12 +89900,16 @@ ${loadedContents.join(`
89774
89900
  "tool_rule",
89775
89901
  "no_tool_call"
89776
89902
  ];
89777
- if (nonRetriableReasons.includes(stopReason)) {} else if (lastRunId && llmApiErrorRetries < LLM_API_ERROR_MAX_RETRIES2) {
89903
+ if (nonRetriableReasons.includes(stopReason)) {} else if (llmApiErrorRetries < LLM_API_ERROR_MAX_RETRIES2) {
89778
89904
  try {
89779
- const run = await client.runs.retrieve(lastRunId);
89780
- const metaError = run.metadata?.error;
89781
- const errorType = metaError?.error_type ?? metaError?.error?.error_type;
89782
- const detail = metaError?.detail ?? metaError?.error?.detail ?? "";
89905
+ let errorType;
89906
+ let detail = detailFromRun ?? latestErrorText ?? "";
89907
+ if (lastRunId) {
89908
+ const run = await client.runs.retrieve(lastRunId);
89909
+ const metaError = run.metadata?.error;
89910
+ errorType = metaError?.error_type ?? metaError?.error?.error_type;
89911
+ detail = metaError?.detail ?? metaError?.error?.detail ?? detail;
89912
+ }
89783
89913
  if (isEmptyResponseRetryable(errorType, detail, emptyResponseRetries, EMPTY_RESPONSE_MAX_RETRIES2)) {
89784
89914
  const attempt = emptyResponseRetries + 1;
89785
89915
  const delayMs = getRetryDelayMs({
@@ -89842,7 +89972,37 @@ ${loadedContents.join(`
89842
89972
  refreshCurrentInputOtids();
89843
89973
  continue;
89844
89974
  }
89845
- } catch (_e) {}
89975
+ } catch (_e) {
89976
+ if (shouldRetryRunMetadataError(undefined, detailFromRun ?? latestErrorText)) {
89977
+ const attempt = llmApiErrorRetries + 1;
89978
+ const detail = detailFromRun ?? latestErrorText;
89979
+ const delayMs = getRetryDelayMs({
89980
+ category: "transient_provider",
89981
+ attempt,
89982
+ detail
89983
+ });
89984
+ llmApiErrorRetries = attempt;
89985
+ if (outputFormat === "stream-json") {
89986
+ const retryMsg = {
89987
+ type: "retry",
89988
+ reason: "llm_api_error",
89989
+ attempt,
89990
+ max_attempts: LLM_API_ERROR_MAX_RETRIES2,
89991
+ delay_ms: delayMs,
89992
+ run_id: lastRunId ?? undefined,
89993
+ session_id: sessionId,
89994
+ uuid: `retry-${lastRunId || randomUUID8()}`
89995
+ };
89996
+ console.log(JSON.stringify(retryMsg));
89997
+ } else {
89998
+ const delaySeconds = Math.round(delayMs / 1000);
89999
+ console.error(`LLM API error encountered (attempt ${attempt} of ${LLM_API_ERROR_MAX_RETRIES2}), retrying in ${delaySeconds}s...`);
90000
+ }
90001
+ await new Promise((resolve27) => setTimeout(resolve27, delayMs));
90002
+ refreshCurrentInputOtids();
90003
+ continue;
90004
+ }
90005
+ }
89846
90006
  }
89847
90007
  markIncompleteToolsAsCancelled(buffers, true, "stream_error");
89848
90008
  const errorLines = toLines(buffers).filter((line) => line.kind === "error");
@@ -134367,7 +134527,7 @@ function sendDesktopNotification(message = "Awaiting your input", level = "info"
134367
134527
  debugLog("hooks", "Notification hook error", error);
134368
134528
  });
134369
134529
  }
134370
- async function isRetriableError(stopReason, lastRunId) {
134530
+ async function isRetriableError(stopReason, lastRunId, fallbackDetail) {
134371
134531
  if (stopReason === "llm_api_error")
134372
134532
  return true;
134373
134533
  const nonRetriableReasons = [
@@ -134394,10 +134554,10 @@ async function isRetriableError(stopReason, lastRunId) {
134394
134554
  }
134395
134555
  return false;
134396
134556
  } catch {
134397
- return false;
134557
+ return shouldRetryRunMetadataError(undefined, fallbackDetail);
134398
134558
  }
134399
134559
  }
134400
- return false;
134560
+ return shouldRetryRunMetadataError(undefined, fallbackDetail);
134401
134561
  }
134402
134562
  function saveLastSessionBeforeExit(conversationId) {
134403
134563
  try {
@@ -137328,7 +137488,7 @@ ${feedback}
137328
137488
  buffersRef.current.interrupted = false;
137329
137489
  continue;
137330
137490
  }
137331
- const retriable = await isRetriableError(stopReasonToHandle, lastRunId);
137491
+ const retriable = await isRetriableError(stopReasonToHandle, lastRunId, detailFromRun ?? latestErrorText ?? fallbackError);
137332
137492
  if (retriable && llmApiErrorRetriesRef.current < LLM_API_ERROR_MAX_RETRIES3) {
137333
137493
  llmApiErrorRetriesRef.current += 1;
137334
137494
  const attempt = llmApiErrorRetriesRef.current;
@@ -147126,7 +147286,7 @@ Upgrade at: ${LETTA_USAGE_URL}
147126
147286
  Delete ${resourceType} at: ${LETTA_AGENTS_URL}`;
147127
147287
  }
147128
147288
  if (isCreditExhaustedError(e, reasons)) {
147129
- return `Your account is out of credits for hosted inference. Add credits, enable auto-recharge, or upgrade at ${LETTA_USAGE_URL}. You can also connect your own provider keys with /connect.`;
147289
+ return `Your account does not have credits for this model. Add your own API keys or upgrade your plan to purchase credits.`;
147130
147290
  }
147131
147291
  const tierUsageLimitMsg = getTierUsageLimitMessage(reasons);
147132
147292
  if (tierUsageLimitMsg)
@@ -153424,4 +153584,4 @@ Error during initialization: ${message}`);
153424
153584
  }
153425
153585
  main();
153426
153586
 
153427
- //# debugId=9A1A4B9B002D45C364756E2164756E21
153587
+ //# debugId=5769741D74E7325364756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.21.13",
3
+ "version": "0.21.15",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {