@letta-ai/letta-code 0.19.9 → 0.19.10

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 +253 -116
  2. package/package.json +1 -1
package/letta.js CHANGED
@@ -3255,7 +3255,7 @@ var package_default;
3255
3255
  var init_package = __esm(() => {
3256
3256
  package_default = {
3257
3257
  name: "@letta-ai/letta-code",
3258
- version: "0.19.9",
3258
+ version: "0.19.10",
3259
3259
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3260
3260
  type: "module",
3261
3261
  bin: {
@@ -48039,6 +48039,7 @@ __export(exports_runtime, {
48039
48039
  setActiveRuntime: () => setActiveRuntime,
48040
48040
  safeEmitWsEvent: () => safeEmitWsEvent,
48041
48041
  nextEventSeq: () => nextEventSeq,
48042
+ hasInterruptedCacheForScope: () => hasInterruptedCacheForScope,
48042
48043
  getRecoveredApprovalStateForScope: () => getRecoveredApprovalStateForScope,
48043
48044
  getPendingControlRequests: () => getPendingControlRequests,
48044
48045
  getPendingControlRequestCount: () => getPendingControlRequestCount,
@@ -48253,6 +48254,19 @@ function getPendingControlRequests(runtime, params) {
48253
48254
  }
48254
48255
  return requests;
48255
48256
  }
48257
+ function hasInterruptedCacheForScope(runtime, params) {
48258
+ const scopedAgentId = resolveScopedAgentId(runtime, params);
48259
+ const scopedConversationId = resolveScopedConversationId(runtime, params);
48260
+ const conversationRuntime = getConversationRuntime(runtime, scopedAgentId, scopedConversationId);
48261
+ if (!conversationRuntime) {
48262
+ return false;
48263
+ }
48264
+ const context3 = conversationRuntime.pendingInterruptedContext;
48265
+ if (context3 && context3.agentId === (scopedAgentId ?? "") && context3.conversationId === scopedConversationId && context3.continuationEpoch === conversationRuntime.continuationEpoch && conversationRuntime.pendingInterruptedResults && conversationRuntime.pendingInterruptedResults.length > 0) {
48266
+ return true;
48267
+ }
48268
+ return false;
48269
+ }
48256
48270
  function getPendingControlRequestCount(runtime, params) {
48257
48271
  return getPendingControlRequests(runtime, params).length;
48258
48272
  }
@@ -70022,6 +70036,7 @@ function buildDeviceStatus(runtime, params) {
70022
70036
  }
70023
70037
  })();
70024
70038
  const conversationPermissionModeState = getConversationPermissionModeState(listener, scopedAgentId, scopedConversationId);
70039
+ const interruptedCacheActive = hasInterruptedCacheForScope(listener, scope);
70025
70040
  return {
70026
70041
  current_connection_id: listener.connectionId,
70027
70042
  connection_name: listener.connectionName,
@@ -70035,7 +70050,7 @@ function buildDeviceStatus(runtime, params) {
70035
70050
  current_loaded_tools: getToolNames(),
70036
70051
  current_available_skills: [],
70037
70052
  background_processes: [],
70038
- pending_control_requests: getPendingControlRequests(listener, scope),
70053
+ pending_control_requests: interruptedCacheActive ? [] : getPendingControlRequests(listener, scope),
70039
70054
  memory_directory: scopedAgentId ? getMemoryFilesystemRoot(scopedAgentId) : null
70040
70055
  };
70041
70056
  }
@@ -70048,11 +70063,12 @@ function buildLoopStatus(runtime, params) {
70048
70063
  const scopedAgentId = resolveScopedAgentId(listener, scope);
70049
70064
  const scopedConversationId = resolveScopedConversationId(listener, scope);
70050
70065
  const conversationRuntime = getConversationRuntime(listener, scopedAgentId, scopedConversationId);
70066
+ const interruptedCacheActive = hasInterruptedCacheForScope(listener, scope);
70051
70067
  const recovered = getRecoveredApprovalStateForScope(listener, scope);
70052
- const status = recovered && recovered.pendingRequestIds.size > 0 && conversationRuntime?.loopStatus === "WAITING_ON_INPUT" ? "WAITING_ON_APPROVAL" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT";
70068
+ const status = interruptedCacheActive ? !conversationRuntime?.isProcessing ? "WAITING_ON_INPUT" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT" : recovered && recovered.pendingRequestIds.size > 0 && conversationRuntime?.loopStatus === "WAITING_ON_INPUT" ? "WAITING_ON_APPROVAL" : conversationRuntime?.loopStatus ?? "WAITING_ON_INPUT";
70053
70069
  return {
70054
70070
  status,
70055
- active_run_ids: conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : []
70071
+ active_run_ids: interruptedCacheActive && !conversationRuntime?.isProcessing ? [] : conversationRuntime?.activeRunId ? [conversationRuntime.activeRunId] : []
70056
70072
  };
70057
70073
  }
70058
70074
  function buildQueueSnapshot(runtime, params) {
@@ -70423,7 +70439,7 @@ function resolvePendingApprovalResolver(runtime, response) {
70423
70439
  }
70424
70440
  runtime.pendingApprovalResolvers.delete(requestId);
70425
70441
  runtime.listener.approvalRuntimeKeyByRequestId.delete(requestId);
70426
- if (runtime.pendingApprovalResolvers.size === 0) {
70442
+ if (runtime.pendingApprovalResolvers.size === 0 && !runtime.isProcessing) {
70427
70443
  setLoopStatus(runtime, "WAITING_ON_INPUT");
70428
70444
  }
70429
70445
  pending.resolve(response);
@@ -75999,6 +76015,10 @@ async function debugLogApprovalResumeState(runtime, params) {
75999
76015
  }
76000
76016
  }
76001
76017
  async function recoverApprovalStateForSync(runtime, scope) {
76018
+ if (hasInterruptedCacheForScope(runtime.listener, scope)) {
76019
+ clearRecoveredApprovalState(runtime);
76020
+ return;
76021
+ }
76002
76022
  const sameActiveScope = runtime.agentId === scope.agent_id && runtime.conversationId === scope.conversation_id;
76003
76023
  if (sameActiveScope && (runtime.isProcessing || runtime.loopStatus !== "WAITING_ON_INPUT")) {
76004
76024
  clearRecoveredApprovalState(runtime);
@@ -76122,6 +76142,11 @@ async function resolveRecoveredApprovalResponse(runtime, socket, response, proce
76122
76142
  agent_id: recovered.agentId,
76123
76143
  conversation_id: recovered.conversationId
76124
76144
  };
76145
+ if (hasInterruptedCacheForScope(runtime.listener, scope)) {
76146
+ clearRecoveredApprovalState(runtime);
76147
+ emitRuntimeStateUpdates(runtime, scope);
76148
+ return true;
76149
+ }
76125
76150
  const approvedToolCallIds = decisions.filter((decision) => decision.type === "approve").map((decision) => decision.approval.toolCallId);
76126
76151
  recovered.pendingRequestIds.clear();
76127
76152
  emitRuntimeStateUpdates(runtime, scope);
@@ -77664,6 +77689,29 @@ async function handleApprovalStop(params) {
77664
77689
  });
77665
77690
  const lastNeedsUserInputToolCallIds = needsUserInput.map((ac) => ac.approval.toolCallId);
77666
77691
  let lastExecutionResults = null;
77692
+ let lastExecutingToolCallIds = [];
77693
+ const shouldInterrupt = () => abortController.signal.aborted || runtime.cancelRequested;
77694
+ const interruptTermination = (interruptedInput = currentInput, interruptedBatchId = dequeuedBatchId) => {
77695
+ populateInterruptQueue(runtime, {
77696
+ lastExecutionResults,
77697
+ lastExecutingToolCallIds,
77698
+ lastNeedsUserInputToolCallIds,
77699
+ agentId: agentId || "",
77700
+ conversationId
77701
+ });
77702
+ return {
77703
+ terminated: true,
77704
+ stream: null,
77705
+ currentInput: interruptedInput,
77706
+ dequeuedBatchId: interruptedBatchId,
77707
+ pendingNormalizationInterruptedToolCallIds: [],
77708
+ turnToolContextId,
77709
+ lastExecutionResults,
77710
+ lastExecutingToolCallIds,
77711
+ lastNeedsUserInputToolCallIds,
77712
+ lastApprovalContinuationAccepted: false
77713
+ };
77714
+ };
77667
77715
  const decisions = [
77668
77716
  ...autoAllowed.map((ac) => ({
77669
77717
  type: "approve",
@@ -77675,15 +77723,27 @@ async function handleApprovalStop(params) {
77675
77723
  reason: ac.denyReason || ac.permission.reason || "Permission denied"
77676
77724
  }))
77677
77725
  ];
77726
+ if (shouldInterrupt()) {
77727
+ return interruptTermination();
77728
+ }
77678
77729
  if (needsUserInput.length > 0) {
77730
+ if (shouldInterrupt()) {
77731
+ return interruptTermination();
77732
+ }
77679
77733
  runtime.lastStopReason = "requires_approval";
77680
77734
  setLoopStatus(runtime, "WAITING_ON_APPROVAL", {
77681
77735
  agent_id: agentId,
77682
77736
  conversation_id: conversationId
77683
77737
  });
77684
77738
  for (const ac of needsUserInput) {
77739
+ if (shouldInterrupt()) {
77740
+ return interruptTermination();
77741
+ }
77685
77742
  const requestId = `perm-${ac.approval.toolCallId}`;
77686
77743
  const diffs = await computeDiffPreviews(ac.approval.toolName, ac.parsedArgs, turnWorkingDirectory);
77744
+ if (shouldInterrupt()) {
77745
+ return interruptTermination();
77746
+ }
77687
77747
  const controlRequest = {
77688
77748
  type: "control_request",
77689
77749
  request_id: requestId,
@@ -77699,7 +77759,18 @@ async function handleApprovalStop(params) {
77699
77759
  agent_id: agentId,
77700
77760
  conversation_id: conversationId
77701
77761
  };
77702
- const responseBody = await requestApprovalOverWS(runtime, socket, requestId, controlRequest);
77762
+ let responseBody;
77763
+ try {
77764
+ responseBody = await requestApprovalOverWS(runtime, socket, requestId, controlRequest);
77765
+ } catch (error) {
77766
+ if (shouldInterrupt()) {
77767
+ return interruptTermination();
77768
+ }
77769
+ throw error;
77770
+ }
77771
+ if (shouldInterrupt()) {
77772
+ return interruptTermination();
77773
+ }
77703
77774
  if ("decision" in responseBody) {
77704
77775
  const response = responseBody.decision;
77705
77776
  if (response.behavior === "allow") {
@@ -77728,7 +77799,10 @@ async function handleApprovalStop(params) {
77728
77799
  }
77729
77800
  }
77730
77801
  }
77731
- const lastExecutingToolCallIds = decisions.filter((decision) => decision.type === "approve").map((decision) => decision.approval.toolCallId);
77802
+ if (shouldInterrupt()) {
77803
+ return interruptTermination();
77804
+ }
77805
+ lastExecutingToolCallIds = decisions.filter((decision) => decision.type === "approve").map((decision) => decision.approval.toolCallId);
77732
77806
  runtime.activeExecutingToolCallIds = [...lastExecutingToolCallIds];
77733
77807
  setLoopStatus(runtime, "EXECUTING_CLIENT_SIDE_TOOL", {
77734
77808
  agent_id: agentId,
@@ -77745,6 +77819,9 @@ async function handleApprovalStop(params) {
77745
77819
  agentId,
77746
77820
  conversationId
77747
77821
  });
77822
+ if (shouldInterrupt()) {
77823
+ return interruptTermination();
77824
+ }
77748
77825
  const executionResults = await executeApprovalBatch(decisions, undefined, {
77749
77826
  toolContextId: turnToolContextId ?? undefined,
77750
77827
  abortSignal: abortController.signal,
@@ -77765,6 +77842,9 @@ async function handleApprovalStop(params) {
77765
77842
  });
77766
77843
  lastExecutionResults = persistedExecutionResults;
77767
77844
  emitInterruptToolReturnMessage(socket, runtime, persistedExecutionResults, runtime.activeRunId || runId || msgRunIds[msgRunIds.length - 1] || undefined, "tool-return");
77845
+ if (shouldInterrupt()) {
77846
+ return interruptTermination();
77847
+ }
77768
77848
  const nextInput = [
77769
77849
  {
77770
77850
  type: "approval",
@@ -77780,11 +77860,22 @@ async function handleApprovalStop(params) {
77780
77860
  emitDequeuedUserMessage(socket, runtime, queuedTurn, dequeuedBatch);
77781
77861
  }
77782
77862
  const nextInputWithSkillContent = injectQueuedSkillContent(nextInput);
77863
+ if (shouldInterrupt()) {
77864
+ return interruptTermination(nextInputWithSkillContent, continuationBatchId);
77865
+ }
77783
77866
  setLoopStatus(runtime, "SENDING_API_REQUEST", {
77784
77867
  agent_id: agentId,
77785
77868
  conversation_id: conversationId
77786
77869
  });
77787
- const stream2 = await sendApprovalContinuationWithRetry(conversationId, nextInputWithSkillContent, buildSendOptions(), socket, runtime, abortController.signal);
77870
+ let stream2;
77871
+ try {
77872
+ stream2 = await sendApprovalContinuationWithRetry(conversationId, nextInputWithSkillContent, buildSendOptions(), socket, runtime, abortController.signal);
77873
+ } catch (error) {
77874
+ if (shouldInterrupt()) {
77875
+ return interruptTermination(nextInputWithSkillContent, continuationBatchId);
77876
+ }
77877
+ throw error;
77878
+ }
77788
77879
  if (!stream2) {
77789
77880
  return {
77790
77881
  terminated: true,
@@ -77850,6 +77941,25 @@ var init_turn_approval = __esm(async () => {
77850
77941
  });
77851
77942
 
77852
77943
  // src/websocket/listener/turn.ts
77944
+ function finalizeInterruptedTurn(socket, runtime, params) {
77945
+ const scope = {
77946
+ agent_id: params.agentId ?? null,
77947
+ conversation_id: params.conversationId
77948
+ };
77949
+ const alreadyProjected = runtime.cancelRequested && !runtime.isProcessing && runtime.loopStatus === "WAITING_ON_INPUT" && runtime.activeRunId === null && runtime.activeAbortController === null;
77950
+ runtime.lastStopReason = "cancelled";
77951
+ runtime.isProcessing = false;
77952
+ if (!alreadyProjected) {
77953
+ emitInterruptedStatusDelta(socket, runtime, {
77954
+ runId: params.runId,
77955
+ agentId: params.agentId,
77956
+ conversationId: params.conversationId
77957
+ });
77958
+ clearActiveRunState(runtime);
77959
+ setLoopStatus(runtime, "WAITING_ON_INPUT", scope);
77960
+ emitRuntimeStateUpdates(runtime, scope);
77961
+ }
77962
+ }
77853
77963
  async function handleIncomingMessage(msg, socket, runtime, onStatusChange, connectionId, dequeuedBatchId = `batch-direct-${crypto.randomUUID()}`) {
77854
77964
  const agentId = msg.agentId;
77855
77965
  const requestedConversationId = msg.conversationId || undefined;
@@ -77868,7 +77978,9 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
77868
77978
  let lastNeedsUserInputToolCallIds = [];
77869
77979
  runtime.isProcessing = true;
77870
77980
  runtime.cancelRequested = false;
77871
- runtime.activeAbortController = new AbortController;
77981
+ const turnAbortController = new AbortController;
77982
+ runtime.activeAbortController = turnAbortController;
77983
+ const turnAbortSignal = turnAbortController.signal;
77872
77984
  runtime.activeWorkingDirectory = turnWorkingDirectory;
77873
77985
  runtime.activeRunId = null;
77874
77986
  runtime.activeRunStartedAt = new Date().toISOString();
@@ -77888,10 +78000,10 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
77888
78000
  try {
77889
78001
  if (!agentId) {
77890
78002
  runtime.isProcessing = false;
78003
+ clearActiveRunState(runtime);
77891
78004
  setLoopStatus(runtime, "WAITING_ON_INPUT", {
77892
78005
  conversation_id: conversationId
77893
78006
  });
77894
- clearActiveRunState(runtime);
77895
78007
  emitRuntimeStateUpdates(runtime, {
77896
78008
  conversation_id: conversationId
77897
78009
  });
@@ -77951,7 +78063,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
77951
78063
  });
77952
78064
  const isPureApprovalContinuation = isApprovalOnlyInput(currentInput);
77953
78065
  const currentInputWithSkillContent = injectQueuedSkillContent(currentInput);
77954
- let stream2 = isPureApprovalContinuation ? await sendApprovalContinuationWithRetry(conversationId, currentInputWithSkillContent, buildSendOptions(), socket, runtime, runtime.activeAbortController.signal) : await sendMessageStreamWithRetry(conversationId, currentInputWithSkillContent, buildSendOptions(), socket, runtime, runtime.activeAbortController.signal);
78066
+ let stream2 = isPureApprovalContinuation ? await sendApprovalContinuationWithRetry(conversationId, currentInputWithSkillContent, buildSendOptions(), socket, runtime, turnAbortSignal) : await sendMessageStreamWithRetry(conversationId, currentInputWithSkillContent, buildSendOptions(), socket, runtime, turnAbortSignal);
77955
78067
  currentInput = currentInputWithSkillContent;
77956
78068
  if (!stream2) {
77957
78069
  return;
@@ -77969,7 +78081,10 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
77969
78081
  while (true) {
77970
78082
  runIdSent = false;
77971
78083
  let latestErrorText = null;
77972
- const result = await drainStreamWithResume(stream2, buffers, () => {}, runtime.activeAbortController.signal, undefined, ({ chunk, shouldOutput, errorInfo }) => {
78084
+ const result = await drainStreamWithResume(stream2, buffers, () => {}, turnAbortSignal, undefined, ({ chunk, shouldOutput, errorInfo }) => {
78085
+ if (runtime.cancelRequested) {
78086
+ return;
78087
+ }
77973
78088
  const maybeRunId = chunk.run_id;
77974
78089
  if (typeof maybeRunId === "string") {
77975
78090
  runId = maybeRunId;
@@ -78013,14 +78128,22 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78013
78128
  const stopReason = result.stopReason;
78014
78129
  const approvals = result.approvals || [];
78015
78130
  lastApprovalContinuationAccepted = false;
78131
+ if (stopReason === "end_turn" && runtime.cancelRequested) {
78132
+ finalizeInterruptedTurn(socket, runtime, {
78133
+ runId: runId || runtime.activeRunId,
78134
+ agentId: agentId ?? null,
78135
+ conversationId
78136
+ });
78137
+ break;
78138
+ }
78016
78139
  if (stopReason === "end_turn") {
78017
78140
  runtime.lastStopReason = "end_turn";
78018
78141
  runtime.isProcessing = false;
78142
+ clearActiveRunState(runtime);
78019
78143
  setLoopStatus(runtime, "WAITING_ON_INPUT", {
78020
78144
  agent_id: agentId,
78021
78145
  conversation_id: conversationId
78022
78146
  });
78023
- clearActiveRunState(runtime);
78024
78147
  emitRuntimeStateUpdates(runtime, {
78025
78148
  agent_id: agentId,
78026
78149
  conversation_id: conversationId
@@ -78028,22 +78151,11 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78028
78151
  break;
78029
78152
  }
78030
78153
  if (stopReason === "cancelled") {
78031
- runtime.lastStopReason = "cancelled";
78032
- runtime.isProcessing = false;
78033
- emitInterruptedStatusDelta(socket, runtime, {
78154
+ finalizeInterruptedTurn(socket, runtime, {
78034
78155
  runId: runId || runtime.activeRunId,
78035
- agentId,
78156
+ agentId: agentId ?? null,
78036
78157
  conversationId
78037
78158
  });
78038
- setLoopStatus(runtime, "WAITING_ON_INPUT", {
78039
- agent_id: agentId,
78040
- conversation_id: conversationId
78041
- });
78042
- clearActiveRunState(runtime);
78043
- emitRuntimeStateUpdates(runtime, {
78044
- agent_id: agentId,
78045
- conversation_id: conversationId
78046
- });
78047
78159
  break;
78048
78160
  }
78049
78161
  if (stopReason !== "requires_approval") {
@@ -78078,7 +78190,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78078
78190
  });
78079
78191
  const isPureApprovalContinuationRetry = isApprovalOnlyInput(currentInput);
78080
78192
  const retryInputWithSkillContent = injectQueuedSkillContent(currentInput);
78081
- stream2 = isPureApprovalContinuationRetry ? await sendApprovalContinuationWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, runtime.activeAbortController.signal) : await sendMessageStreamWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, runtime.activeAbortController.signal);
78193
+ stream2 = isPureApprovalContinuationRetry ? await sendApprovalContinuationWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, turnAbortSignal) : await sendMessageStreamWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, turnAbortSignal);
78082
78194
  currentInput = retryInputWithSkillContent;
78083
78195
  if (!stream2) {
78084
78196
  return;
@@ -78120,7 +78232,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78120
78232
  conversationId
78121
78233
  });
78122
78234
  await new Promise((resolve23) => setTimeout(resolve23, delayMs));
78123
- if (runtime.activeAbortController.signal.aborted) {
78235
+ if (turnAbortSignal.aborted) {
78124
78236
  throw new Error("Cancelled by user");
78125
78237
  }
78126
78238
  setLoopStatus(runtime, "SENDING_API_REQUEST", {
@@ -78129,7 +78241,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78129
78241
  });
78130
78242
  const isPureApprovalContinuationRetry = isApprovalOnlyInput(currentInput);
78131
78243
  const retryInputWithSkillContent = injectQueuedSkillContent(currentInput);
78132
- stream2 = isPureApprovalContinuationRetry ? await sendApprovalContinuationWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, runtime.activeAbortController.signal) : await sendMessageStreamWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, runtime.activeAbortController.signal);
78244
+ stream2 = isPureApprovalContinuationRetry ? await sendApprovalContinuationWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, turnAbortSignal) : await sendMessageStreamWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, turnAbortSignal);
78133
78245
  currentInput = retryInputWithSkillContent;
78134
78246
  if (!stream2) {
78135
78247
  return;
@@ -78164,7 +78276,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78164
78276
  conversationId
78165
78277
  });
78166
78278
  await new Promise((resolve23) => setTimeout(resolve23, delayMs));
78167
- if (runtime.activeAbortController.signal.aborted) {
78279
+ if (turnAbortSignal.aborted) {
78168
78280
  throw new Error("Cancelled by user");
78169
78281
  }
78170
78282
  setLoopStatus(runtime, "SENDING_API_REQUEST", {
@@ -78173,7 +78285,7 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78173
78285
  });
78174
78286
  const isPureApprovalContinuationRetry = isApprovalOnlyInput(currentInput);
78175
78287
  const retryInputWithSkillContent = injectQueuedSkillContent(currentInput);
78176
- stream2 = isPureApprovalContinuationRetry ? await sendApprovalContinuationWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, runtime.activeAbortController.signal) : await sendMessageStreamWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, runtime.activeAbortController.signal);
78288
+ stream2 = isPureApprovalContinuationRetry ? await sendApprovalContinuationWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, turnAbortSignal) : await sendMessageStreamWithRetry(conversationId, retryInputWithSkillContent, buildSendOptions(), socket, runtime, turnAbortSignal);
78177
78289
  currentInput = retryInputWithSkillContent;
78178
78290
  if (!stream2) {
78179
78291
  return;
@@ -78189,31 +78301,20 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78189
78301
  }
78190
78302
  const effectiveStopReason = runtime.cancelRequested ? "cancelled" : stopReason || "error";
78191
78303
  if (effectiveStopReason === "cancelled") {
78192
- runtime.lastStopReason = "cancelled";
78193
- runtime.isProcessing = false;
78194
- emitInterruptedStatusDelta(socket, runtime, {
78304
+ finalizeInterruptedTurn(socket, runtime, {
78195
78305
  runId: runId || runtime.activeRunId,
78196
- agentId,
78306
+ agentId: agentId ?? null,
78197
78307
  conversationId
78198
78308
  });
78199
- setLoopStatus(runtime, "WAITING_ON_INPUT", {
78200
- agent_id: agentId,
78201
- conversation_id: conversationId
78202
- });
78203
- clearActiveRunState(runtime);
78204
- emitRuntimeStateUpdates(runtime, {
78205
- agent_id: agentId,
78206
- conversation_id: conversationId
78207
- });
78208
78309
  break;
78209
78310
  }
78210
78311
  runtime.lastStopReason = effectiveStopReason;
78211
78312
  runtime.isProcessing = false;
78313
+ clearActiveRunState(runtime);
78212
78314
  setLoopStatus(runtime, "WAITING_ON_INPUT", {
78213
78315
  agent_id: agentId,
78214
78316
  conversation_id: conversationId
78215
78317
  });
78216
- clearActiveRunState(runtime);
78217
78318
  emitRuntimeStateUpdates(runtime, {
78218
78319
  agent_id: agentId,
78219
78320
  conversation_id: conversationId
@@ -78284,31 +78385,20 @@ async function handleIncomingMessage(msg, socket, runtime, onStatusChange, conne
78284
78385
  emitInterruptToolReturnMessage(socket, runtime, approvalsForEmission, runtime.activeRunId || msgRunIds[msgRunIds.length - 1] || undefined);
78285
78386
  }
78286
78387
  }
78287
- runtime.lastStopReason = "cancelled";
78288
- runtime.isProcessing = false;
78289
- emitInterruptedStatusDelta(socket, runtime, {
78388
+ finalizeInterruptedTurn(socket, runtime, {
78290
78389
  runId: runtime.activeRunId || msgRunIds[msgRunIds.length - 1],
78291
78390
  agentId: agentId || null,
78292
78391
  conversationId
78293
78392
  });
78294
- setLoopStatus(runtime, "WAITING_ON_INPUT", {
78295
- agent_id: agentId || null,
78296
- conversation_id: conversationId
78297
- });
78298
- clearActiveRunState(runtime);
78299
- emitRuntimeStateUpdates(runtime, {
78300
- agent_id: agentId || null,
78301
- conversation_id: conversationId
78302
- });
78303
78393
  return;
78304
78394
  }
78305
78395
  runtime.lastStopReason = "error";
78306
78396
  runtime.isProcessing = false;
78397
+ clearActiveRunState(runtime);
78307
78398
  setLoopStatus(runtime, "WAITING_ON_INPUT", {
78308
78399
  agent_id: agentId || null,
78309
78400
  conversation_id: conversationId
78310
78401
  });
78311
- clearActiveRunState(runtime);
78312
78402
  emitRuntimeStateUpdates(runtime, {
78313
78403
  agent_id: agentId || null,
78314
78404
  conversation_id: conversationId
@@ -78552,6 +78642,11 @@ async function handleApprovalResponseInput(listener, params, deps = {
78552
78642
  return true;
78553
78643
  }
78554
78644
  const targetRuntime = approvalRuntime ?? deps.getOrCreateScopedRuntime(listener, params.runtime.agent_id, params.runtime.conversation_id);
78645
+ if (targetRuntime.cancelRequested && !targetRuntime.isProcessing) {
78646
+ targetRuntime.cancelRequested = false;
78647
+ deps.scheduleQueuePump(targetRuntime, params.socket, params.opts, params.processQueuedTurn);
78648
+ return false;
78649
+ }
78555
78650
  if (await deps.resolveRecoveredApprovalResponse(targetRuntime, params.socket, params.response, handleIncomingMessage, {
78556
78651
  onStatusChange: params.opts.onStatusChange,
78557
78652
  connectionId: params.opts.connectionId
@@ -78606,6 +78701,96 @@ async function handleChangeDeviceStateInput(listener, params, deps = {}) {
78606
78701
  }
78607
78702
  return true;
78608
78703
  }
78704
+ async function handleAbortMessageInput(listener, params, deps = {}) {
78705
+ const resolvedDeps = {
78706
+ getActiveRuntime,
78707
+ getPendingControlRequestCount,
78708
+ getOrCreateScopedRuntime,
78709
+ getRecoveredApprovalStateForScope,
78710
+ stashRecoveredApprovalInterrupts,
78711
+ rejectPendingApprovalResolvers,
78712
+ setLoopStatus,
78713
+ clearActiveRunState,
78714
+ emitRuntimeStateUpdates,
78715
+ emitInterruptedStatusDelta,
78716
+ scheduleQueuePump,
78717
+ cancelConversation: async (agentId, conversationId) => {
78718
+ const client = await getClient2();
78719
+ const cancelId = conversationId === "default" || !conversationId ? agentId : conversationId;
78720
+ await client.conversations.cancel(cancelId);
78721
+ },
78722
+ ...deps
78723
+ };
78724
+ if (listener !== resolvedDeps.getActiveRuntime() || listener.intentionallyClosed) {
78725
+ return false;
78726
+ }
78727
+ const scope = {
78728
+ agent_id: params.command.runtime.agent_id,
78729
+ conversation_id: params.command.runtime.conversation_id
78730
+ };
78731
+ const hasPendingApprovals = resolvedDeps.getPendingControlRequestCount(listener, scope) > 0;
78732
+ const scopedRuntime = resolvedDeps.getOrCreateScopedRuntime(listener, scope.agent_id, scope.conversation_id);
78733
+ const hasActiveTurn = scopedRuntime.isProcessing;
78734
+ if (!hasActiveTurn && !hasPendingApprovals) {
78735
+ return false;
78736
+ }
78737
+ const interruptedRunId = scopedRuntime.activeRunId;
78738
+ scopedRuntime.cancelRequested = true;
78739
+ if (scopedRuntime.activeExecutingToolCallIds.length > 0 && (!scopedRuntime.pendingInterruptedResults || scopedRuntime.pendingInterruptedResults.length === 0)) {
78740
+ scopedRuntime.pendingInterruptedResults = scopedRuntime.activeExecutingToolCallIds.map((toolCallId) => ({
78741
+ type: "tool",
78742
+ tool_call_id: toolCallId,
78743
+ tool_return: INTERRUPTED_BY_USER,
78744
+ status: "error"
78745
+ }));
78746
+ scopedRuntime.pendingInterruptedContext = {
78747
+ agentId: scopedRuntime.agentId || "",
78748
+ conversationId: scopedRuntime.conversationId,
78749
+ continuationEpoch: scopedRuntime.continuationEpoch
78750
+ };
78751
+ scopedRuntime.pendingInterruptedToolCallIds = [
78752
+ ...scopedRuntime.activeExecutingToolCallIds
78753
+ ];
78754
+ }
78755
+ if (scopedRuntime.activeAbortController && !scopedRuntime.activeAbortController.signal.aborted) {
78756
+ scopedRuntime.activeAbortController.abort();
78757
+ }
78758
+ const recoveredApprovalState = resolvedDeps.getRecoveredApprovalStateForScope(listener, scope);
78759
+ if (recoveredApprovalState && !hasActiveTurn) {
78760
+ resolvedDeps.stashRecoveredApprovalInterrupts(scopedRuntime, recoveredApprovalState);
78761
+ }
78762
+ if (hasPendingApprovals) {
78763
+ resolvedDeps.rejectPendingApprovalResolvers(scopedRuntime, "Cancelled by user");
78764
+ }
78765
+ if (hasActiveTurn) {
78766
+ scopedRuntime.lastStopReason = "cancelled";
78767
+ scopedRuntime.isProcessing = false;
78768
+ resolvedDeps.clearActiveRunState(scopedRuntime);
78769
+ resolvedDeps.setLoopStatus(scopedRuntime, "WAITING_ON_INPUT", scope);
78770
+ resolvedDeps.emitRuntimeStateUpdates(scopedRuntime, scope);
78771
+ resolvedDeps.emitInterruptedStatusDelta(params.socket, scopedRuntime, {
78772
+ runId: interruptedRunId,
78773
+ agentId: scope.agent_id,
78774
+ conversationId: scope.conversation_id
78775
+ });
78776
+ } else if (hasPendingApprovals) {
78777
+ resolvedDeps.emitInterruptedStatusDelta(params.socket, scopedRuntime, {
78778
+ runId: interruptedRunId,
78779
+ agentId: scope.agent_id,
78780
+ conversationId: scope.conversation_id
78781
+ });
78782
+ }
78783
+ if (!hasActiveTurn) {
78784
+ scopedRuntime.cancelRequested = false;
78785
+ }
78786
+ const cancelConversationId = scopedRuntime.conversationId;
78787
+ const cancelAgentId = scopedRuntime.agentId;
78788
+ if (cancelAgentId) {
78789
+ resolvedDeps.cancelConversation(cancelAgentId, cancelConversationId).catch(() => {});
78790
+ }
78791
+ resolvedDeps.scheduleQueuePump(scopedRuntime, params.socket, params.opts, params.processQueuedTurn);
78792
+ return true;
78793
+ }
78609
78794
  async function handleCwdChange(msg, socket, runtime) {
78610
78795
  const conversationId = normalizeConversationId(msg.conversationId);
78611
78796
  const agentId = normalizeCwdAgentId(msg.agentId);
@@ -78927,64 +79112,15 @@ async function connectWithRetry(runtime, opts, attempt = 0, startTime = Date.now
78927
79112
  return;
78928
79113
  }
78929
79114
  if (parsed.type === "abort_message") {
78930
- if (runtime !== getActiveRuntime() || runtime.intentionallyClosed) {
78931
- return;
78932
- }
78933
- const hasPendingApprovals = getPendingControlRequestCount(runtime, {
78934
- agent_id: parsed.runtime.agent_id,
78935
- conversation_id: parsed.runtime.conversation_id
78936
- }) > 0;
78937
- const scopedRuntime = getOrCreateScopedRuntime(runtime, parsed.runtime.agent_id, parsed.runtime.conversation_id);
78938
- const hasActiveTurn = scopedRuntime.isProcessing;
78939
- if (!hasActiveTurn && !hasPendingApprovals) {
78940
- return;
78941
- }
78942
- scopedRuntime.cancelRequested = true;
78943
- if (scopedRuntime.activeExecutingToolCallIds.length > 0 && (!scopedRuntime.pendingInterruptedResults || scopedRuntime.pendingInterruptedResults.length === 0)) {
78944
- scopedRuntime.pendingInterruptedResults = scopedRuntime.activeExecutingToolCallIds.map((toolCallId) => ({
78945
- type: "tool",
78946
- tool_call_id: toolCallId,
78947
- tool_return: INTERRUPTED_BY_USER,
78948
- status: "error"
78949
- }));
78950
- scopedRuntime.pendingInterruptedContext = {
78951
- agentId: scopedRuntime.agentId || "",
78952
- conversationId: scopedRuntime.conversationId,
78953
- continuationEpoch: scopedRuntime.continuationEpoch
78954
- };
78955
- scopedRuntime.pendingInterruptedToolCallIds = [
78956
- ...scopedRuntime.activeExecutingToolCallIds
78957
- ];
78958
- }
78959
- if (scopedRuntime.activeAbortController && !scopedRuntime.activeAbortController.signal.aborted) {
78960
- scopedRuntime.activeAbortController.abort();
78961
- }
78962
- const recoveredApprovalState = getRecoveredApprovalStateForScope(runtime, {
78963
- agent_id: parsed.runtime.agent_id,
78964
- conversation_id: parsed.runtime.conversation_id
79115
+ await handleAbortMessageInput(runtime, {
79116
+ command: parsed,
79117
+ socket,
79118
+ opts: {
79119
+ onStatusChange: opts.onStatusChange,
79120
+ connectionId: opts.connectionId
79121
+ },
79122
+ processQueuedTurn
78965
79123
  });
78966
- if (recoveredApprovalState && !hasActiveTurn) {
78967
- stashRecoveredApprovalInterrupts(scopedRuntime, recoveredApprovalState);
78968
- }
78969
- if (hasPendingApprovals) {
78970
- rejectPendingApprovalResolvers(scopedRuntime, "Cancelled by user");
78971
- }
78972
- if (!hasActiveTurn && hasPendingApprovals) {
78973
- emitInterruptedStatusDelta(socket, scopedRuntime, {
78974
- runId: scopedRuntime.activeRunId,
78975
- agentId: parsed.runtime.agent_id,
78976
- conversationId: parsed.runtime.conversation_id
78977
- });
78978
- }
78979
- const cancelConversationId = scopedRuntime.conversationId;
78980
- const cancelAgentId = scopedRuntime.agentId;
78981
- if (cancelAgentId) {
78982
- getClient2().then((client) => {
78983
- const cancelId = cancelConversationId === "default" || !cancelConversationId ? cancelAgentId : cancelConversationId;
78984
- return client.conversations.cancel(cancelId);
78985
- }).catch(() => {});
78986
- }
78987
- scheduleQueuePump(scopedRuntime, socket, opts, processQueuedTurn);
78988
79124
  return;
78989
79125
  }
78990
79126
  if (isSearchFilesCommand(parsed)) {
@@ -79499,6 +79635,7 @@ var init_client4 = __esm(async () => {
79499
79635
  consumeQueuedTurn,
79500
79636
  handleIncomingMessage,
79501
79637
  handleApprovalResponseInput,
79638
+ handleAbortMessageInput,
79502
79639
  handleChangeDeviceStateInput,
79503
79640
  scheduleQueuePump,
79504
79641
  recoverApprovalStateForSync,
@@ -145316,4 +145453,4 @@ Error during initialization: ${message}`);
145316
145453
  }
145317
145454
  main();
145318
145455
 
145319
- //# debugId=773D326804039A9D64756E2164756E21
145456
+ //# debugId=8449D2836CF8EFF864756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.19.9",
3
+ "version": "0.19.10",
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": {