@letta-ai/letta-code 0.25.6 → 0.25.7

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 +315 -72
  2. package/package.json +1 -1
package/letta.js CHANGED
@@ -3143,7 +3143,7 @@ var package_default;
3143
3143
  var init_package = __esm(() => {
3144
3144
  package_default = {
3145
3145
  name: "@letta-ai/letta-code",
3146
- version: "0.25.6",
3146
+ version: "0.25.7",
3147
3147
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3148
3148
  type: "module",
3149
3149
  bin: {
@@ -70864,6 +70864,11 @@ function createProviderLettaStream(events) {
70864
70864
  }
70865
70865
  if (pendingStopReason) {
70866
70866
  yield pendingStopReason;
70867
+ } else if (sawToolCall) {
70868
+ yield {
70869
+ message_type: "stop_reason",
70870
+ stop_reason: "requires_approval"
70871
+ };
70867
70872
  }
70868
70873
  } catch (error51) {
70869
70874
  yield* createProviderErrorChunks(error51);
@@ -72960,6 +72965,29 @@ function chunkSeqId(chunk) {
72960
72965
  function cloneStreamingChunk(chunk) {
72961
72966
  return JSON.parse(JSON.stringify(chunk));
72962
72967
  }
72968
+ function localStreamProtocolError(message) {
72969
+ return {
72970
+ message,
72971
+ detail: message,
72972
+ error_type: "local_backend_error",
72973
+ retryable: false
72974
+ };
72975
+ }
72976
+ function localErrorChunk(info) {
72977
+ return {
72978
+ message_type: "error_message",
72979
+ message: info.message,
72980
+ detail: info.detail,
72981
+ error_type: info.error_type,
72982
+ retryable: info.retryable
72983
+ };
72984
+ }
72985
+ function localStopReasonChunk(stopReason) {
72986
+ return {
72987
+ message_type: "stop_reason",
72988
+ stop_reason: stopReason
72989
+ };
72990
+ }
72963
72991
  function createReplayStream(chunks) {
72964
72992
  const controller = new AbortController;
72965
72993
  return {
@@ -73247,10 +73275,14 @@ class HeadlessBackend {
73247
73275
  controller: stream.controller,
73248
73276
  async* [Symbol.asyncIterator]() {
73249
73277
  let sawStopReason = false;
73278
+ let sawApprovalRequest = false;
73250
73279
  let pendingErrorInfo;
73251
73280
  try {
73252
73281
  for await (const rawChunk of stream) {
73253
73282
  const chunk = attachRunId(rawChunk, runId);
73283
+ if (chunk.message_type === "approval_request_message") {
73284
+ sawApprovalRequest = true;
73285
+ }
73254
73286
  const errorInfo = runErrorInfo(chunk);
73255
73287
  if (errorInfo) {
73256
73288
  pendingErrorInfo = errorInfo;
@@ -73268,8 +73300,23 @@ class HeadlessBackend {
73268
73300
  if (!sawStopReason) {
73269
73301
  if (pendingErrorInfo) {
73270
73302
  backend.completeRun(runId, "error", pendingErrorInfo);
73303
+ yield backend.recordRunChunk(runId, attachRunId(localStopReasonChunk("error"), runId));
73304
+ } else if (sawApprovalRequest) {
73305
+ backend.completeRun(runId, "requires_approval");
73306
+ yield backend.recordRunChunk(runId, attachRunId(localStopReasonChunk("requires_approval"), runId));
73271
73307
  } else {
73272
- backend.completeRun(runId, "end_turn");
73308
+ const info = localStreamProtocolError("Local provider stream ended without a stop reason.");
73309
+ backend.completeRun(runId, "error", info);
73310
+ for (const terminalChunk of [
73311
+ localErrorChunk(info),
73312
+ localStopReasonChunk("error")
73313
+ ]) {
73314
+ const chunk = attachRunId(terminalChunk, runId);
73315
+ const persisted = store.appendStreamChunk(conversationId, agentId, chunk);
73316
+ if (!isLocalStateChunkOnly(persisted)) {
73317
+ yield backend.recordRunChunk(runId, attachRunId(persisted, runId));
73318
+ }
73319
+ }
73273
73320
  }
73274
73321
  }
73275
73322
  } catch (error51) {
@@ -73313,49 +73360,177 @@ function stringifyUnknown(value) {
73313
73360
  return String(value);
73314
73361
  }
73315
73362
  }
73316
- function truncate(value, maxChars) {
73317
- if (value.length <= maxChars)
73318
- return value;
73319
- return `${value.slice(0, maxChars)} [truncated ${value.length - maxChars} chars]`;
73363
+ function truncateToolReturn(content, limit2) {
73364
+ if (limit2 === undefined || content.length <= limit2)
73365
+ return content;
73366
+ return `${content.slice(0, limit2)}... [truncated ${content.length - limit2} chars]`;
73320
73367
  }
73321
- function toolPartSummary(part, truncationChars) {
73322
- const toolName = String(part.type).slice("tool-".length);
73323
- const input = truncate(stringifyUnknown(part.input), truncationChars ?? Number.POSITIVE_INFINITY);
73324
- const state = typeof part.state === "string" ? part.state : "unknown";
73325
- const output = part.output !== undefined || part.errorText !== undefined ? `
73326
- output: ${truncate(stringifyUnknown(part.output ?? part.errorText), truncationChars ?? Number.POSITIVE_INFINITY)}` : "";
73327
- return `[tool ${toolName} state=${state}]
73328
- input: ${input}${output}`;
73368
+ function middleTruncateText(text2, budgetChars, headFrac = 0.3, tailFrac = 0.3) {
73369
+ if (budgetChars <= 0 || text2.length <= budgetChars)
73370
+ return text2;
73371
+ const headLength = Math.max(0, Math.floor(budgetChars * headFrac));
73372
+ let tailLength = Math.max(0, Math.floor(budgetChars * tailFrac));
73373
+ if (headLength + tailLength > budgetChars) {
73374
+ tailLength = Math.max(0, budgetChars - headLength);
73375
+ }
73376
+ const head = text2.slice(0, headLength);
73377
+ const tail = tailLength > 0 ? text2.slice(-tailLength) : "";
73378
+ const dropped = Math.max(0, text2.length - (head.length + tail.length));
73379
+ const marker25 = `
73380
+ [TRUNCATED: dropped ${dropped} middle chars due to context budget]
73381
+ `;
73382
+ return `${head}${marker25}${tail}`;
73329
73383
  }
73330
- function partText(part, truncationChars) {
73331
- if (!isRecord5(part))
73332
- return stringifyUnknown(part);
73333
- if ((part.type === "text" || part.type === "reasoning") && typeof part.text === "string") {
73334
- return part.type === "reasoning" ? `[reasoning]
73335
- ${part.text}` : part.text;
73384
+ function textFromContentParts(parts) {
73385
+ const textParts = [];
73386
+ let imageCount = 0;
73387
+ for (const part of parts) {
73388
+ if (!isRecord5(part))
73389
+ continue;
73390
+ if ((part.type === "text" || part.type === "reasoning") && typeof part.text === "string") {
73391
+ textParts.push(part.text);
73392
+ continue;
73393
+ }
73394
+ if (part.type === "file" && typeof part.mediaType === "string") {
73395
+ if (part.mediaType.startsWith("image/"))
73396
+ imageCount += 1;
73397
+ continue;
73398
+ }
73399
+ if (part.type === "source-url" || part.type === "source-document") {
73400
+ textParts.push(stringifyUnknown(part));
73401
+ }
73336
73402
  }
73337
- if (typeof part.type === "string" && part.type.startsWith("tool-")) {
73338
- return toolPartSummary(part, truncationChars);
73403
+ let textContent2 = textParts.join(`
73404
+
73405
+ `);
73406
+ if (imageCount > 0) {
73407
+ const placeholder = imageCount === 1 ? "[Image omitted]" : `[${imageCount} images omitted]`;
73408
+ textContent2 = `${textContent2}${textContent2 ? " " : ""}${placeholder}`;
73339
73409
  }
73340
- return truncate(stringifyUnknown(part), truncationChars ?? Number.POSITIVE_INFINITY);
73410
+ return textContent2 || undefined;
73341
73411
  }
73342
- function formatLocalMessagesForSummary(messages, options = {}) {
73343
- const transcript = messages.map((message, index) => {
73344
- const compactionSummary = message.metadata?.compaction?.summary;
73345
- const body = typeof compactionSummary === "string" ? compactionSummary : message.parts.map((part) => partText(part, options.truncationChars)).filter((text2) => text2.length > 0).join(`
73346
- `);
73347
- return `<message index="${index + 1}" role="${message.role}">
73348
- ${body}
73349
- </message>`;
73350
- }).join(`
73351
-
73412
+ function toolNameFromPart(part) {
73413
+ return typeof part.type === "string" && part.type.startsWith("tool-") ? part.type.slice("tool-".length) : "?";
73414
+ }
73415
+ function localToolCallArguments(input) {
73416
+ return typeof input === "string" ? input : stringifyUnknown(input ?? {});
73417
+ }
73418
+ function toolReturnToText(value) {
73419
+ if (value === undefined || value === null)
73420
+ return;
73421
+ if (typeof value === "string")
73422
+ return value;
73423
+ if (!Array.isArray(value))
73424
+ return stringifyUnknown(value);
73425
+ const textParts = [];
73426
+ let imageCount = 0;
73427
+ for (const item of value) {
73428
+ if (isRecord5(item) && item.type === "text" && typeof item.text === "string") {
73429
+ textParts.push(item.text);
73430
+ continue;
73431
+ }
73432
+ if (isRecord5(item) && item.type === "image") {
73433
+ imageCount += 1;
73434
+ }
73435
+ }
73436
+ let result = textParts.join(`
73352
73437
  `);
73353
- if (options.maxChars && transcript.length > options.maxChars) {
73354
- return `${transcript.slice(transcript.length - options.maxChars)}
73355
-
73356
- [Earlier transcript content was truncated to fit the summarizer context window.]`;
73438
+ if (imageCount > 0) {
73439
+ const placeholder = imageCount === 1 ? "[Image omitted]" : `[${imageCount} images omitted]`;
73440
+ result = `${result}${result ? " " : ""}${placeholder}`;
73357
73441
  }
73358
- return transcript;
73442
+ return result || undefined;
73443
+ }
73444
+ function isLocalToolOutputState(state) {
73445
+ return state === "output-available" || state === "output-error" || state === "output-denied";
73446
+ }
73447
+ function localMessagesToSummaryOpenAIDicts(messages, options = {}) {
73448
+ const result = [];
73449
+ for (const message of messages) {
73450
+ if (message.role === "system")
73451
+ continue;
73452
+ const compactionSummary = message.metadata?.compaction?.summary;
73453
+ if (typeof compactionSummary === "string") {
73454
+ result.push({ role: "user", content: compactionSummary });
73455
+ continue;
73456
+ }
73457
+ const toolParts = [];
73458
+ for (const part of message.parts) {
73459
+ if (isRecord5(part) && typeof part.type === "string" && part.type.startsWith("tool-")) {
73460
+ toolParts.push(part);
73461
+ }
73462
+ }
73463
+ if (message.role === "assistant") {
73464
+ const content2 = textFromContentParts(message.parts) ?? null;
73465
+ if (content2 !== null || toolParts.length > 0) {
73466
+ result.push({
73467
+ role: "assistant",
73468
+ content: content2,
73469
+ ...toolParts.length > 0 ? {
73470
+ tool_calls: toolParts.map((part) => ({
73471
+ function: {
73472
+ name: toolNameFromPart(part),
73473
+ arguments: localToolCallArguments(part.input)
73474
+ }
73475
+ }))
73476
+ } : {}
73477
+ });
73478
+ }
73479
+ for (const part of toolParts) {
73480
+ if (!isLocalToolOutputState(part.state))
73481
+ continue;
73482
+ const returnText = toolReturnToText(part.output ?? part.errorText);
73483
+ result.push({
73484
+ role: "tool",
73485
+ content: returnText ? truncateToolReturn(returnText, options.toolReturnTruncationChars) : null
73486
+ });
73487
+ }
73488
+ continue;
73489
+ }
73490
+ const content = textFromContentParts(message.parts);
73491
+ if (content !== undefined) {
73492
+ result.push({
73493
+ role: message.role === "user" ? "user" : "developer",
73494
+ content
73495
+ });
73496
+ }
73497
+ }
73498
+ return result;
73499
+ }
73500
+ function simpleFormatter(messages) {
73501
+ const lines = [];
73502
+ for (const message of messages) {
73503
+ try {
73504
+ const role = message.role ?? "?";
73505
+ let content = message.content;
73506
+ if (Array.isArray(content)) {
73507
+ content = content.map((block) => isRecord5(block) && typeof block.text === "string" ? block.text : String(block)).join(" ");
73508
+ }
73509
+ let text2 = typeof content === "string" ? content : "";
73510
+ if (Array.isArray(message.tool_calls) && message.tool_calls.length > 0) {
73511
+ const callParts = message.tool_calls.map((toolCall) => {
73512
+ const fn = toolCall.function ?? {};
73513
+ return `${fn.name ?? "?"}(${fn.arguments ?? ""})`;
73514
+ });
73515
+ text2 = `${text2}${text2 ? " " : ""}-> ${callParts.join(", ")}`;
73516
+ }
73517
+ if (text2)
73518
+ lines.push(`[${role}] ${text2}`);
73519
+ } catch {
73520
+ lines.push(JSON.stringify(message));
73521
+ }
73522
+ }
73523
+ return `
73524
+ ${lines.join(`
73525
+ `)}
73526
+
73527
+ . Generate the summary.`;
73528
+ }
73529
+ function formatLocalMessagesForSummary(messages, options = {}) {
73530
+ const transcript = simpleFormatter(localMessagesToSummaryOpenAIDicts(messages, {
73531
+ toolReturnTruncationChars: options.truncationChars
73532
+ }));
73533
+ return options.maxChars ? middleTruncateText(transcript, options.maxChars) : transcript;
73359
73534
  }
73360
73535
  async function runGenerateText(input, transcript, defaultPrompt) {
73361
73536
  const systemPrompt = input.prompt ?? defaultPrompt;
@@ -73418,7 +73593,7 @@ async function summarizeLocalMessagesWithPrompt(input, defaultPrompt) {
73418
73593
  let previousTranscript;
73419
73594
  for (const maxChars of TRANSCRIPT_FALLBACK_MAX_CHAR_STEPS) {
73420
73595
  const fallbackTranscript = formatLocalMessagesForSummary(input.messages, {
73421
- truncationChars: TOOL_TRANSCRIPT_TRUNCATION_CHARS,
73596
+ truncationChars: TOOL_RETURN_TRUNCATION_CHARS,
73422
73597
  maxChars
73423
73598
  });
73424
73599
  if (fallbackTranscript === previousTranscript)
@@ -73526,9 +73701,24 @@ function estimateLocalMessageTokens(messages) {
73526
73701
  const chars = messages.reduce((total, message) => total + JSON.stringify(message).length, 0);
73527
73702
  return Math.ceil(chars / 4);
73528
73703
  }
73529
- function packageLocalSummaryMessage(summary, stats) {
73530
- const message = `Note: prior messages with the user are available in external context. Messages are a record of the conversation history, or "events," containing user or system/automated inputs, reasoning traces, agent outputs, tool calls, and tool responses. As a Letta agent, your conversation history is automatically managed by the system — old messages will be periodically evicted from the conversation history and replaced with a recursive summary ("compaction"), yet all messages are persisted and remain retrievable through active tool calling.
73531
- The following is an in-context recursive summary of the prior messages: ${summary}`;
73704
+ function packageLocalSummaryMessage(summary, stats, mode) {
73705
+ let message;
73706
+ if (mode?.includes("sliding_window")) {
73707
+ if (stats?.messages_count_before !== undefined && stats.messages_count_after !== undefined) {
73708
+ const numEvicted = stats.messages_count_before - stats.messages_count_after;
73709
+ message = `Note: ${numEvicted} messages from the beginning of the conversation have been hidden from view due to memory constraints.
73710
+ The following is a summary of the previous messages:
73711
+ ${summary}`;
73712
+ } else {
73713
+ message = `Note: prior messages from the beginning of the conversation have been hidden from view due to conversation memory constraints.
73714
+ The following is a summary of the previous messages:
73715
+ ${summary}`;
73716
+ }
73717
+ } else {
73718
+ message = `Note: prior messages have been hidden from view due to conversation memory constraints.
73719
+ The following is a summary of the previous messages:
73720
+ ${summary}`;
73721
+ }
73532
73722
  return JSON.stringify({
73533
73723
  type: "system_alert",
73534
73724
  message,
@@ -73536,7 +73726,7 @@ The following is an in-context recursive summary of the prior messages: ${summar
73536
73726
  ...stats ? { compaction_stats: stats } : {}
73537
73727
  });
73538
73728
  }
73539
- var ALL_WORD_LIMIT = 500, SLIDING_WORD_LIMIT = 300, SUMMARY_TRUNCATION_SUFFIX = "... [summary truncated to fit]", TOOL_TRANSCRIPT_TRUNCATION_CHARS = 4000, TRANSCRIPT_FALLBACK_MAX_CHARS = 120000, TRANSCRIPT_FALLBACK_MAX_CHAR_STEPS, LOCAL_DEFAULT_COMPACTION_MODE = "sliding_window", LOCAL_DEFAULT_SLIDING_WINDOW_PERCENTAGE = 0.3, LocalSlidingWindowCompactionPlanningError, LOCAL_ALL_COMPACTION_PROMPT, LOCAL_SLIDING_WINDOW_COMPACTION_PROMPT;
73729
+ var ALL_WORD_LIMIT = 500, SLIDING_WORD_LIMIT = 300, SUMMARY_TRUNCATION_SUFFIX = "... [summary truncated to fit]", TOOL_RETURN_TRUNCATION_CHARS = 5000, TRANSCRIPT_FALLBACK_MAX_CHARS = 120000, TRANSCRIPT_FALLBACK_MAX_CHAR_STEPS, LOCAL_DEFAULT_COMPACTION_MODE = "sliding_window", LOCAL_DEFAULT_SLIDING_WINDOW_PERCENTAGE = 0.3, LocalSlidingWindowCompactionPlanningError, LOCAL_ALL_COMPACTION_PROMPT, LOCAL_SLIDING_WINDOW_COMPACTION_PROMPT;
73540
73730
  var init_compaction = __esm(() => {
73541
73731
  init_dist6();
73542
73732
  init_AISDKModelFactory();
@@ -76536,7 +76726,7 @@ var init_init_local_memfs = () => {};
76536
76726
  var memory_default = `---
76537
76727
  name: memory
76538
76728
  description: Decompose and reorganize memory files into focused, single-purpose files using \`/\` naming
76539
- tools: Read, Edit, Write, Glob, Grep, Bash, TaskOutput
76729
+ tools: Bash, TaskOutput
76540
76730
  model: auto
76541
76731
  memoryBlocks: none
76542
76732
  permissionMode: memory
@@ -76640,10 +76830,10 @@ ls $WORK/
76640
76830
 
76641
76831
  Then read relevant memory files:
76642
76832
 
76643
- \`\`\`
76644
- Read({ file_path: "$WORK/project.md" })
76645
- Read({ file_path: "$WORK/persona.md" })
76646
- Read({ file_path: "$WORK/human.md" })
76833
+ \`\`\`bash
76834
+ sed -n '1,240p' "$WORK/project.md"
76835
+ sed -n '1,240p' "$WORK/persona.md"
76836
+ sed -n '1,240p' "$WORK/human.md"
76647
76837
  \`\`\`
76648
76838
 
76649
76839
  ### Step 2: Identify system-managed files (skip)
@@ -76865,7 +77055,7 @@ var init_memory2 = () => {};
76865
77055
  var memory_local_memfs_default = `---
76866
77056
  name: memory
76867
77057
  description: Decompose and reorganize memory files into focused, single-purpose files using \`/\` naming
76868
- tools: Read, Edit, Write, Glob, Grep, Bash, TaskOutput
77058
+ tools: Bash, TaskOutput
76869
77059
  model: auto
76870
77060
  memoryBlocks: none
76871
77061
  permissionMode: memory
@@ -76967,10 +77157,10 @@ ls $WORK/
76967
77157
 
76968
77158
  Then read relevant memory files:
76969
77159
 
76970
- \`\`\`
76971
- Read({ file_path: "$WORK/project.md" })
76972
- Read({ file_path: "$WORK/persona.md" })
76973
- Read({ file_path: "$WORK/human.md" })
77160
+ \`\`\`bash
77161
+ sed -n '1,240p' "$WORK/project.md"
77162
+ sed -n '1,240p' "$WORK/persona.md"
77163
+ sed -n '1,240p' "$WORK/human.md"
76974
77164
  \`\`\`
76975
77165
 
76976
77166
  ### Step 2: Identify system-managed files (skip)
@@ -77183,7 +77373,7 @@ var init_recall = () => {};
77183
77373
  var reflection_default = `---
77184
77374
  name: reflection
77185
77375
  description: Background agent that reflects on recent conversations and updates memory files
77186
- tools: Read, Edit, Write, Bash
77376
+ tools: Bash
77187
77377
  model: inherit
77188
77378
  memoryBlocks: none
77189
77379
  mode: stateless
@@ -77319,7 +77509,7 @@ var init_reflection = () => {};
77319
77509
  var reflection_local_memfs_default = `---
77320
77510
  name: reflection
77321
77511
  description: Background agent that reflects on recent conversations and updates memory files
77322
- tools: Read, Edit, Write, Bash
77512
+ tools: Bash
77323
77513
  model: inherit
77324
77514
  memoryBlocks: none
77325
77515
  mode: stateless
@@ -121949,7 +122139,7 @@ var init_LocalBackend = __esm(() => {
121949
122139
  conversationId,
121950
122140
  agentId,
121951
122141
  summary,
121952
- packedSummary: packageLocalSummaryMessage(summary, stats),
122142
+ packedSummary: packageLocalSummaryMessage(summary, stats, settings.mode),
121953
122143
  stats,
121954
122144
  remainingMessages: plan.messagesToKeep
121955
122145
  });
@@ -121989,7 +122179,7 @@ var init_LocalBackend = __esm(() => {
121989
122179
  conversationId,
121990
122180
  agentId,
121991
122181
  summary,
121992
- packedSummary: packageLocalSummaryMessage(summary, stats),
122182
+ packedSummary: packageLocalSummaryMessage(summary, stats, settings.mode),
121993
122183
  stats,
121994
122184
  remainingMessages: plan.messagesToKeep
121995
122185
  });
@@ -168153,6 +168343,13 @@ async function drainStream(stream2, buffers, refresh, abortSignal, onFirstMessag
168153
168343
  }));
168154
168344
  const approval = approvals[0] || null;
168155
168345
  streamProcessor.pendingApprovals.clear();
168346
+ if (stopReason === "end_turn" && approvals.length > 0) {
168347
+ debugWarn("drainStream", "Coercing end_turn to requires_approval because approval_request_message chunks were received");
168348
+ telemetry.trackError("stream_end_turn_with_pending_approvals", "Stream ended with end_turn after emitting approval_request_message chunks", "stream_drain", {
168349
+ runId: streamProcessor.lastRunId || undefined
168350
+ });
168351
+ stopReason = "requires_approval";
168352
+ }
168156
168353
  if (stopReason === "requires_approval" && approvals.length === 0 && !isResumeStream) {
168157
168354
  debugWarn("drainStream", "No approvals collected despite requires_approval stop reason");
168158
168355
  }
@@ -168259,6 +168456,14 @@ async function drainStreamWithResume(stream2, buffers, refresh, abortSignal, onF
168259
168456
  });
168260
168457
  result.approval = result.approvals[0] ?? null;
168261
168458
  }
168459
+ } else if (result.stopReason === "end_turn" && (originalApprovals?.length ?? 0) > 0) {
168460
+ debugWarn("stream", "[MID-STREAM RESUME] Coercing resumed end_turn to requires_approval because the original stream had approval chunks");
168461
+ telemetry.trackError("stream_resume_end_turn_with_original_approvals", "Resumed stream ended with end_turn after original stream emitted approval_request_message chunks", "stream_resume", {
168462
+ runId: result.lastRunId ?? undefined
168463
+ });
168464
+ result.stopReason = "requires_approval";
168465
+ result.approvals = originalApprovals;
168466
+ result.approval = originalApproval;
168262
168467
  }
168263
168468
  } catch (resumeError) {
168264
168469
  markIncompleteToolsAsCancelled(buffers, false, "stream_error", true);
@@ -168596,7 +168801,7 @@ async function buildReflectionCompactionReminder(context4) {
168596
168801
  function escapeXml2(value) {
168597
168802
  return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
168598
168803
  }
168599
- function truncate2(value, maxChars) {
168804
+ function truncate(value, maxChars) {
168600
168805
  if (value.length <= maxChars) {
168601
168806
  return value;
168602
168807
  }
@@ -168607,7 +168812,7 @@ function formatToolList(tools) {
168607
168812
  if (uniqueTools.length === 0) {
168608
168813
  return "(none)";
168609
168814
  }
168610
- return truncate2(uniqueTools.join(", "), MAX_TOOL_LIST_CHARS);
168815
+ return truncate(uniqueTools.join(", "), MAX_TOOL_LIST_CHARS);
168611
168816
  }
168612
168817
  async function buildCommandIoReminder(context4) {
168613
168818
  if (context4.state.pendingCommandIoReminders.length === 0) {
@@ -168618,8 +168823,8 @@ async function buildCommandIoReminder(context4) {
168618
168823
  const dropped = queued.length - recent.length;
168619
168824
  const commandLines = recent.map((entry) => {
168620
168825
  const status = entry.success ? "success" : "error";
168621
- const safeInput = truncate2(entry.input, MAX_COMMAND_INPUT_CHARS);
168622
- const safeOutput = truncate2(entry.output || "(no output)", MAX_COMMAND_OUTPUT_CHARS);
168826
+ const safeInput = truncate(entry.input, MAX_COMMAND_INPUT_CHARS);
168827
+ const safeOutput = truncate(entry.output || "(no output)", MAX_COMMAND_OUTPUT_CHARS);
168623
168828
  return `- ${safeInput} → ${safeOutput} (${status})`;
168624
168829
  });
168625
168830
  const agentHints = recent.filter((entry) => entry.agentHint).map((entry) => entry.agentHint);
@@ -191381,7 +191586,7 @@ function getHeader(command) {
191381
191586
  function displayPath(path30) {
191382
191587
  return path30.replace(/\.md$/, "");
191383
191588
  }
191384
- function truncate3(text3, max) {
191589
+ function truncate2(text3, max) {
191385
191590
  if (text3.length <= max)
191386
191591
  return text3;
191387
191592
  return `${text3.slice(0, max)}…`;
@@ -191547,7 +191752,7 @@ var init_InlineMemoryApproval = __esm(async () => {
191547
191752
  color: "red",
191548
191753
  children: [
191549
191754
  "- ",
191550
- truncate3(oldString, 200)
191755
+ truncate2(oldString, 200)
191551
191756
  ]
191552
191757
  }, undefined, true, undefined, this)
191553
191758
  }, undefined, false, undefined, this)
@@ -191561,7 +191766,7 @@ var init_InlineMemoryApproval = __esm(async () => {
191561
191766
  color: "green",
191562
191767
  children: [
191563
191768
  "+ ",
191564
- truncate3(newString, 200)
191769
+ truncate2(newString, 200)
191565
191770
  ]
191566
191771
  }, undefined, true, undefined, this)
191567
191772
  }, undefined, false, undefined, this)
@@ -191577,7 +191782,7 @@ var init_InlineMemoryApproval = __esm(async () => {
191577
191782
  dimColor: true,
191578
191783
  children: [
191579
191784
  insertLine != null ? `Line ${insertLine}: ` : "",
191580
- truncate3(insertText, 300)
191785
+ truncate2(insertText, 300)
191581
191786
  ]
191582
191787
  }, undefined, true, undefined, this)
191583
191788
  }, undefined, false, undefined, this),
@@ -191598,7 +191803,7 @@ var init_InlineMemoryApproval = __esm(async () => {
191598
191803
  children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text2, {
191599
191804
  wrap: "wrap",
191600
191805
  dimColor: true,
191601
- children: truncate3(fileText, 300)
191806
+ children: truncate2(fileText, 300)
191602
191807
  }, undefined, false, undefined, this)
191603
191808
  }, undefined, false, undefined, this),
191604
191809
  command === "update_description" && description && /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Box_default, {
@@ -191620,7 +191825,7 @@ var init_InlineMemoryApproval = __esm(async () => {
191620
191825
  children: /* @__PURE__ */ jsx_dev_runtime19.jsxDEV(Text2, {
191621
191826
  wrap: "wrap",
191622
191827
  dimColor: true,
191623
- children: truncate3(patchInput, 500)
191828
+ children: truncate2(patchInput, 500)
191624
191829
  }, undefined, false, undefined, this)
191625
191830
  }, undefined, false, undefined, this)
191626
191831
  }, undefined, false, undefined, this),
@@ -212281,7 +212486,7 @@ function MemoryDiffRenderer({
212281
212486
  dimColor: true,
212282
212487
  children: [
212283
212488
  " - ",
212284
- truncate4(description, 40)
212489
+ truncate3(description, 40)
212285
212490
  ]
212286
212491
  }, undefined, true, undefined, this)
212287
212492
  ]
@@ -212307,7 +212512,7 @@ function MemoryDiffRenderer({
212307
212512
  backgroundColor: colors.diff.addedLineBg,
212308
212513
  color: colors.diff.textOnDark,
212309
212514
  wrap: "wrap",
212310
- children: `+ ${truncate4(line, 60)}`
212515
+ children: `+ ${truncate3(line, 60)}`
212311
212516
  }, undefined, false, undefined, this)
212312
212517
  }, undefined, false, undefined, this)
212313
212518
  ]
@@ -212681,7 +212886,7 @@ function DiffLine2({
212681
212886
  ]
212682
212887
  }, undefined, true, undefined, this);
212683
212888
  }
212684
- function truncate4(str, maxLen) {
212889
+ function truncate3(str, maxLen) {
212685
212890
  if (str.length <= maxLen)
212686
212891
  return str;
212687
212892
  return `${str.slice(0, maxLen - 1)}…`;
@@ -217002,6 +217207,17 @@ function useApprovalFlow(ctx) {
217002
217207
  toolResultsInFlightRef.current = false;
217003
217208
  queueApprovalResults(null);
217004
217209
  }
217210
+ } catch (error51) {
217211
+ markIncompleteToolsAsCancelled(buffersRef.current, true, "stream_error");
217212
+ const errorDetails = formatErrorDetails(error51, agentId);
217213
+ appendError(errorDetails, {
217214
+ ...extractErrorMeta(error51),
217215
+ context: "approval_send"
217216
+ });
217217
+ setStreaming(false);
217218
+ closeTrajectorySegment();
217219
+ syncTrajectoryElapsedBase();
217220
+ refreshDerived();
217005
217221
  } finally {
217006
217222
  clearApprovalToolContext();
217007
217223
  setIsExecutingTool(false);
@@ -217011,6 +217227,7 @@ function useApprovalFlow(ctx) {
217011
217227
  toolResultsInFlightRef.current = false;
217012
217228
  }
217013
217229
  }, [
217230
+ agentId,
217014
217231
  approvalResults,
217015
217232
  autoHandledResults,
217016
217233
  autoDeniedApprovals,
@@ -217068,6 +217285,7 @@ function useApprovalFlow(ctx) {
217068
217285
  setIsExecutingTool(false);
217069
217286
  }
217070
217287
  } catch (e) {
217288
+ markIncompleteToolsAsCancelled(buffersRef.current, true, "stream_error");
217071
217289
  const errorDetails = formatErrorDetails(e, agentId);
217072
217290
  appendError(errorDetails, {
217073
217291
  ...extractErrorMeta(e),
@@ -217075,6 +217293,7 @@ function useApprovalFlow(ctx) {
217075
217293
  });
217076
217294
  setStreaming(false);
217077
217295
  setIsExecutingTool(false);
217296
+ refreshDerived();
217078
217297
  }
217079
217298
  }, [
217080
217299
  agentId,
@@ -217083,6 +217302,7 @@ function useApprovalFlow(ctx) {
217083
217302
  sendAllResults,
217084
217303
  appendError,
217085
217304
  isExecutingTool,
217305
+ refreshDerived,
217086
217306
  setStreaming,
217087
217307
  setApprovalResults,
217088
217308
  setIsExecutingTool
@@ -217196,6 +217416,17 @@ function useApprovalFlow(ctx) {
217196
217416
  otid: randomUUID19()
217197
217417
  }
217198
217418
  ]);
217419
+ } catch (error51) {
217420
+ markIncompleteToolsAsCancelled(buffersRef.current, true, "stream_error");
217421
+ const errorDetails = formatErrorDetails(error51, agentId);
217422
+ appendError(errorDetails, {
217423
+ ...extractErrorMeta(error51),
217424
+ context: "approval_send"
217425
+ });
217426
+ setStreaming(false);
217427
+ closeTrajectorySegment();
217428
+ syncTrajectoryElapsedBase();
217429
+ refreshDerived();
217199
217430
  } finally {
217200
217431
  setIsExecutingTool(false);
217201
217432
  }
@@ -217214,10 +217445,13 @@ function useApprovalFlow(ctx) {
217214
217445
  handleApproveCurrent,
217215
217446
  processConversation,
217216
217447
  refreshDerived,
217448
+ appendError,
217217
217449
  isExecutingTool,
217218
217450
  setStreaming,
217219
217451
  setUiPermissionMode,
217220
217452
  openTrajectorySegment,
217453
+ closeTrajectorySegment,
217454
+ syncTrajectoryElapsedBase,
217221
217455
  prepareScopedToolExecutionContext,
217222
217456
  updateStreamingOutput,
217223
217457
  setApprovalContexts,
@@ -217250,6 +217484,7 @@ function useApprovalFlow(ctx) {
217250
217484
  setIsExecutingTool(false);
217251
217485
  }
217252
217486
  } catch (e) {
217487
+ markIncompleteToolsAsCancelled(buffersRef.current, true, "stream_error");
217253
217488
  const errorDetails = formatErrorDetails(e, agentId);
217254
217489
  appendError(errorDetails, {
217255
217490
  ...extractErrorMeta(e),
@@ -217257,6 +217492,7 @@ function useApprovalFlow(ctx) {
217257
217492
  });
217258
217493
  setStreaming(false);
217259
217494
  setIsExecutingTool(false);
217495
+ refreshDerived();
217260
217496
  }
217261
217497
  }, [
217262
217498
  agentId,
@@ -217265,6 +217501,7 @@ function useApprovalFlow(ctx) {
217265
217501
  sendAllResults,
217266
217502
  appendError,
217267
217503
  isExecutingTool,
217504
+ refreshDerived,
217268
217505
  setStreaming,
217269
217506
  setApprovalResults,
217270
217507
  setIsExecutingTool,
@@ -219325,6 +219562,12 @@ ${newState.originalPrompt}`,
219325
219562
  if (wasInterrupted && stopReasonToHandle === "requires_approval") {
219326
219563
  stopReasonToHandle = "cancelled";
219327
219564
  }
219565
+ const approvalsFromStream = approvals && approvals.length > 0 ? approvals : approval ? [approval] : [];
219566
+ if (stopReasonToHandle === "end_turn" && approvalsFromStream.length > 0) {
219567
+ telemetry.trackError("stream_end_turn_with_pending_approvals_tui_guard", "Stream returned end_turn after emitting approval_request_message chunks; continuing approval flow", "message_stream", { runId: lastRunId ?? undefined });
219568
+ debugWarn("stream", "Coercing end_turn to requires_approval because %d approval chunk(s) were collected", approvalsFromStream.length);
219569
+ stopReasonToHandle = "requires_approval";
219570
+ }
219328
219571
  if (stopReasonToHandle === "end_turn") {
219329
219572
  clearApprovalToolContext();
219330
219573
  setStreaming(false);
@@ -219490,7 +219733,7 @@ ${feedback}
219490
219733
  setAutoDeniedApprovals([]);
219491
219734
  lastSentInputRef.current = null;
219492
219735
  pendingInterruptRecoveryConversationIdRef.current = null;
219493
- const approvalsToProcess = approvals && approvals.length > 0 ? approvals : approval ? [approval] : [];
219736
+ const approvalsToProcess = approvalsFromStream;
219494
219737
  if (approvalsToProcess.length === 0) {
219495
219738
  clearApprovalToolContext();
219496
219739
  appendError(`Unexpected empty approvals with stop reason: ${stopReason}`);
@@ -239287,4 +239530,4 @@ Error during initialization: ${message}`);
239287
239530
  }
239288
239531
  main();
239289
239532
 
239290
- //# debugId=C52918521328FA4064756E2164756E21
239533
+ //# debugId=6524B28DE55262FB64756E2164756E21
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.25.6",
3
+ "version": "0.25.7",
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": {