@letta-ai/letta-code 0.21.13 → 0.21.14

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 +148 -26
  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.14",
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: {
@@ -74338,6 +74338,84 @@ var init_check_approval = __esm(() => {
74338
74338
  ];
74339
74339
  });
74340
74340
 
74341
+ // src/utils/createRelayedAbortController.ts
74342
+ function createRelayedAbortController(parentSignal) {
74343
+ const controller = new AbortController;
74344
+ if (!parentSignal) {
74345
+ return {
74346
+ controller,
74347
+ signal: controller.signal,
74348
+ cleanup: () => {}
74349
+ };
74350
+ }
74351
+ if (parentSignal.aborted) {
74352
+ controller.abort(parentSignal.reason);
74353
+ return {
74354
+ controller,
74355
+ signal: controller.signal,
74356
+ cleanup: () => {}
74357
+ };
74358
+ }
74359
+ const relayAbort = () => {
74360
+ controller.abort(parentSignal.reason);
74361
+ };
74362
+ parentSignal.addEventListener("abort", relayAbort, { once: true });
74363
+ return {
74364
+ controller,
74365
+ signal: controller.signal,
74366
+ cleanup: () => {
74367
+ parentSignal.removeEventListener("abort", relayAbort);
74368
+ }
74369
+ };
74370
+ }
74371
+
74372
+ // src/utils/streamAbortRelay.ts
74373
+ function composeCleanups(first, second) {
74374
+ let cleaned = false;
74375
+ return () => {
74376
+ if (cleaned) {
74377
+ return;
74378
+ }
74379
+ cleaned = true;
74380
+ first();
74381
+ second();
74382
+ };
74383
+ }
74384
+ function createStreamAbortRelay(parentSignal) {
74385
+ if (!parentSignal) {
74386
+ return null;
74387
+ }
74388
+ const requestAbort = createRelayedAbortController(parentSignal);
74389
+ let cleaned = false;
74390
+ const cleanup = () => {
74391
+ if (cleaned) {
74392
+ return;
74393
+ }
74394
+ cleaned = true;
74395
+ requestAbort.cleanup();
74396
+ };
74397
+ return {
74398
+ signal: requestAbort.signal,
74399
+ attach(stream2) {
74400
+ const existingCleanup = streamAbortRelayCleanupByStream.get(stream2);
74401
+ streamAbortRelayCleanupByStream.set(stream2, existingCleanup ? composeCleanups(existingCleanup, cleanup) : cleanup);
74402
+ },
74403
+ cleanup
74404
+ };
74405
+ }
74406
+ function cleanupStreamAbortRelay(stream2) {
74407
+ const cleanup = streamAbortRelayCleanupByStream.get(stream2);
74408
+ if (!cleanup) {
74409
+ return;
74410
+ }
74411
+ streamAbortRelayCleanupByStream.delete(stream2);
74412
+ cleanup();
74413
+ }
74414
+ var streamAbortRelayCleanupByStream;
74415
+ var init_streamAbortRelay = __esm(() => {
74416
+ streamAbortRelayCleanupByStream = new WeakMap;
74417
+ });
74418
+
74341
74419
  // src/agent/approval-result-normalization.ts
74342
74420
  function normalizeToolReturnText(value) {
74343
74421
  if (typeof value === "string")
@@ -74582,19 +74660,23 @@ async function sendMessageStream(conversationId, messages, opts = { streamTokens
74582
74660
  const firstOtid = messages[0]?.otid;
74583
74661
  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
74662
  let stream2;
74663
+ const abortRelay = createStreamAbortRelay(requestOptions.signal);
74585
74664
  try {
74586
74665
  stream2 = await client.conversations.messages.create(resolvedConversationId, requestBody, {
74587
74666
  ...requestOptions,
74667
+ ...abortRelay ? { signal: abortRelay.signal } : {},
74588
74668
  headers: {
74589
74669
  ...requestOptions.headers ?? {},
74590
74670
  ...extraHeaders
74591
74671
  }
74592
74672
  });
74593
74673
  } catch (error) {
74674
+ abortRelay?.cleanup();
74594
74675
  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
74676
  throw error;
74596
74677
  }
74597
74678
  debugLog("send-message-stream", "request_ok conversation_id=%s otid=%s", resolvedConversationId, firstOtid ?? "none");
74679
+ abortRelay?.attach(stream2);
74598
74680
  if (requestStartTime !== undefined) {
74599
74681
  streamRequestStartTimes.set(stream2, requestStartTime);
74600
74682
  }
@@ -74611,6 +74693,7 @@ async function sendMessageStream(conversationId, messages, opts = { streamTokens
74611
74693
  var streamRequestStartTimes, streamToolContextIds, streamRequestContexts;
74612
74694
  var init_message = __esm(async () => {
74613
74695
  init_debug();
74696
+ init_streamAbortRelay();
74614
74697
  init_timing();
74615
74698
  init_approval_result_normalization();
74616
74699
  init_clientSkills();
@@ -76701,6 +76784,7 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
76701
76784
  if (abortSignal) {
76702
76785
  abortSignal.removeEventListener("abort", abortHandler);
76703
76786
  }
76787
+ cleanupStreamAbortRelay(stream2);
76704
76788
  clearLastSDKDiagnostic();
76705
76789
  }
76706
76790
  if (!stopReason && streamProcessor.stopReason) {
@@ -76792,15 +76876,23 @@ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onF
76792
76876
  const client = await lazyClient();
76793
76877
  buffers.commitGeneration = (buffers.commitGeneration || 0) + 1;
76794
76878
  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
- });
76879
+ const resumeAbortRelay = createStreamAbortRelay(abortSignal);
76880
+ let resumeStream;
76881
+ try {
76882
+ resumeStream = runIdSource === "otid" && streamOtid && streamRequestContext ? await client.conversations.messages.stream(streamRequestContext.resolvedConversationId, {
76883
+ agent_id: streamRequestContext.conversationId === "default" ? streamRequestContext.agentId ?? undefined : undefined,
76884
+ otid: streamOtid,
76885
+ starting_after: result.lastSeqId ?? 0,
76886
+ batch_size: 1000
76887
+ }, resumeAbortRelay ? { signal: resumeAbortRelay.signal } : undefined) : await client.runs.messages.stream(runIdToResume, {
76888
+ starting_after: result.lastSeqId ?? 0,
76889
+ batch_size: 1000
76890
+ }, resumeAbortRelay ? { signal: resumeAbortRelay.signal } : undefined);
76891
+ } catch (resumeError) {
76892
+ resumeAbortRelay?.cleanup();
76893
+ throw resumeError;
76894
+ }
76895
+ resumeAbortRelay?.attach(resumeStream);
76804
76896
  const resumeResult = await drainStream(resumeStream, buffers, refresh, abortSignal, undefined, onChunkProcessed, contextTracker, seenSeqIdThreshold, true);
76805
76897
  if (resumeResult.stopReason !== "error") {
76806
76898
  debugWarn("stream", "[MID-STREAM RESUME] ✅ Success (runId=%s, stopReason=%s)", runIdToResume, resumeResult.stopReason);
@@ -76865,6 +76957,7 @@ var FALLBACK_RUN_DISCOVERY_TIMEOUT_MS = 5000;
76865
76957
  var init_stream = __esm(async () => {
76866
76958
  init_error();
76867
76959
  init_debug();
76960
+ init_streamAbortRelay();
76868
76961
  init_timing();
76869
76962
  init_chunkLog();
76870
76963
  await __promiseAll([
@@ -80804,15 +80897,23 @@ async function sendMessageStreamWithRetry(conversationId, messages, opts, socket
80804
80897
  try {
80805
80898
  const client = await getClient();
80806
80899
  const messageOtid = messages.map((item) => item.otid).find((value) => typeof value === "string");
80900
+ const resumeAbortRelay = createStreamAbortRelay(abortSignal);
80807
80901
  if (abortSignal?.aborted) {
80808
80902
  throw new Error("Cancelled by user");
80809
80903
  }
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
- });
80904
+ try {
80905
+ const resumeStream = await client.conversations.messages.stream(conversationId, {
80906
+ agent_id: conversationId === "default" ? runtime.agentId ?? undefined : undefined,
80907
+ otid: messageOtid ?? undefined,
80908
+ starting_after: 0,
80909
+ batch_size: 1000
80910
+ }, resumeAbortRelay ? { signal: resumeAbortRelay.signal } : undefined);
80911
+ resumeAbortRelay?.attach(resumeStream);
80912
+ return resumeStream;
80913
+ } catch (resumeError) {
80914
+ resumeAbortRelay?.cleanup();
80915
+ throw resumeError;
80916
+ }
80816
80917
  } catch (resumeError) {
80817
80918
  if (abortSignal?.aborted) {
80818
80919
  throw new Error("Cancelled by user");
@@ -80927,15 +81028,23 @@ async function sendApprovalContinuationWithRetry(conversationId, messages, opts,
80927
81028
  try {
80928
81029
  const client = await getClient();
80929
81030
  const messageOtid = messages.map((item) => item.otid).find((value) => typeof value === "string");
81031
+ const resumeAbortRelay = createStreamAbortRelay(abortSignal);
80930
81032
  if (abortSignal?.aborted) {
80931
81033
  throw new Error("Cancelled by user");
80932
81034
  }
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
- });
81035
+ try {
81036
+ const resumeStream = await client.conversations.messages.stream(conversationId, {
81037
+ agent_id: conversationId === "default" ? runtime.agentId ?? undefined : undefined,
81038
+ otid: messageOtid ?? undefined,
81039
+ starting_after: 0,
81040
+ batch_size: 1000
81041
+ }, resumeAbortRelay ? { signal: resumeAbortRelay.signal } : undefined);
81042
+ resumeAbortRelay?.attach(resumeStream);
81043
+ return resumeStream;
81044
+ } catch (resumeError) {
81045
+ resumeAbortRelay?.cleanup();
81046
+ throw resumeError;
81047
+ }
80939
81048
  } catch (resumeError) {
80940
81049
  if (abortSignal?.aborted) {
80941
81050
  throw new Error("Cancelled by user");
@@ -80965,6 +81074,7 @@ var init_send = __esm(async () => {
80965
81074
  init_errorFormatter();
80966
81075
  init_diffPreview();
80967
81076
  init_interactivePolicy();
81077
+ init_streamAbortRelay();
80968
81078
  init_constants2();
80969
81079
  init_cwd();
80970
81080
  init_permissionMode();
@@ -83809,6 +83919,19 @@ function runDetachedListenerTask(commandName, task2) {
83809
83919
  }
83810
83920
  });
83811
83921
  }
83922
+ async function replaySyncStateForRuntime(listenerRuntime, socket, scope, opts) {
83923
+ const syncScopedRuntime = getOrCreateScopedRuntime(listenerRuntime, scope.agent_id, scope.conversation_id);
83924
+ const recoverFn = opts?.recoverApprovalStateForSync ?? recoverApprovalStateForSync;
83925
+ try {
83926
+ await recoverFn(syncScopedRuntime, scope);
83927
+ } catch (error) {
83928
+ trackListenerError("listener_sync_recovery_failed", error, "listener_sync_recovery");
83929
+ if (isDebugEnabled()) {
83930
+ console.warn("[Listen] Sync approval recovery failed:", error);
83931
+ }
83932
+ }
83933
+ emitStateSync(socket, listenerRuntime, scope);
83934
+ }
83812
83935
  function getParsedRuntimeScope(parsed) {
83813
83936
  if (!parsed || typeof parsed !== "object" || !("runtime" in parsed)) {
83814
83937
  return null;
@@ -84944,9 +85067,7 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
84944
85067
  console.log(`[Listen V2] Dropping sync: runtime mismatch or closed`);
84945
85068
  return;
84946
85069
  }
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);
85070
+ await replaySyncStateForRuntime(runtime, socket, parsed.runtime);
84950
85071
  return;
84951
85072
  }
84952
85073
  if (parsed.type === "input") {
@@ -85911,6 +86032,7 @@ var init_client4 = __esm(async () => {
85911
86032
  handleCreateAgentCommand,
85912
86033
  handleReflectionSettingsCommand,
85913
86034
  scheduleQueuePump,
86035
+ replaySyncStateForRuntime,
85914
86036
  recoverApprovalStateForSync,
85915
86037
  clearRecoveredApprovalStateForScope: (runtime, scope) => clearRecoveredApprovalStateForScope(asListenerRuntimeForTests(runtime), scope),
85916
86038
  emitStateSync
@@ -153424,4 +153546,4 @@ Error during initialization: ${message}`);
153424
153546
  }
153425
153547
  main();
153426
153548
 
153427
- //# debugId=9A1A4B9B002D45C364756E2164756E21
153549
+ //# debugId=DEDBAA251791817164756E2164756E21
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.14",
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": {