@letta-ai/letta-code 0.14.14 → 0.14.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/letta.js CHANGED
@@ -3122,7 +3122,7 @@ var package_default;
3122
3122
  var init_package = __esm(() => {
3123
3123
  package_default = {
3124
3124
  name: "@letta-ai/letta-code",
3125
- version: "0.14.14",
3125
+ version: "0.14.16",
3126
3126
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
3127
3127
  type: "module",
3128
3128
  bin: {
@@ -5649,7 +5649,6 @@ Your memory consists of memory blocks and external memory:
5649
5649
  - Memory Blocks: Stored as memory blocks, each containing a label (title), description (explaining how this block should influence your behavior), and value (the actual content). Memory blocks have size limits. Memory blocks are embedded within your system instructions and remain constantly available in-context.
5650
5650
  - External memory: Additional memory storage that is accessible and that you can bring into context with tools when needed.
5651
5651
 
5652
- Memory management tools allow you to edit existing memory blocks and query for external memories.
5653
5652
  Memory blocks are used to modulate and augment your base behavior, follow them closely, and maintain them cleanly.
5654
5653
  They are the foundation which makes you *you*.
5655
5654
 
@@ -29235,6 +29234,12 @@ var create = (stream, { showCursor = false } = {}) => {
29235
29234
  let previousLineCount = 0;
29236
29235
  let previousOutput = "";
29237
29236
  let hasHiddenCursor = false;
29237
+ const renderWithClearedLineEnds = (output) => {
29238
+ const lines = output.split(`
29239
+ `);
29240
+ return lines.map((line) => line + exports_base.eraseEndLine).join(`
29241
+ `);
29242
+ };
29238
29243
  const render = (str) => {
29239
29244
  if (!showCursor && !hasHiddenCursor) {
29240
29245
  cli_cursor_default.hide();
@@ -29245,10 +29250,17 @@ var create = (stream, { showCursor = false } = {}) => {
29245
29250
  if (output === previousOutput) {
29246
29251
  return;
29247
29252
  }
29248
- previousOutput = output;
29249
- stream.write(exports_base.eraseLines(previousLineCount) + output);
29250
- previousLineCount = output.split(`
29253
+ const nextLineCount = output.split(`
29251
29254
  `).length;
29255
+ if (previousLineCount > 1) {
29256
+ stream.write(exports_base.cursorUp(previousLineCount - 1));
29257
+ }
29258
+ stream.write(renderWithClearedLineEnds(output));
29259
+ if (nextLineCount < previousLineCount) {
29260
+ stream.write(exports_base.eraseDown);
29261
+ }
29262
+ previousOutput = output;
29263
+ previousLineCount = nextLineCount;
29252
29264
  };
29253
29265
  render.clear = () => {
29254
29266
  stream.write(exports_base.eraseLines(previousLineCount));
@@ -31103,7 +31115,7 @@ var init_colors = __esm(() => {
31103
31115
  inline: "green"
31104
31116
  },
31105
31117
  link: {
31106
- text: "cyan",
31118
+ text: brandColors.primaryAccentLight,
31107
31119
  url: brandColors.primaryAccent
31108
31120
  },
31109
31121
  heading: {
@@ -33160,7 +33172,8 @@ var init_models2 = __esm(() => {
33160
33172
  isFeatured: true,
33161
33173
  free: true,
33162
33174
  updateArgs: {
33163
- context_window: 180000
33175
+ context_window: 160000,
33176
+ max_output_tokens: 64000
33164
33177
  }
33165
33178
  },
33166
33179
  {
@@ -33169,7 +33182,8 @@ var init_models2 = __esm(() => {
33169
33182
  label: "Minimax M2",
33170
33183
  description: "Minimax's latest model",
33171
33184
  updateArgs: {
33172
- context_window: 196000
33185
+ context_window: 160000,
33186
+ max_output_tokens: 64000
33173
33187
  }
33174
33188
  },
33175
33189
  {
@@ -38450,6 +38464,10 @@ var init_EnterPlanMode2 = __esm(() => {
38450
38464
 
38451
38465
  // src/tools/impl/ExitPlanMode.ts
38452
38466
  async function exit_plan_mode() {
38467
+ if (permissionMode2.getMode() === "plan") {
38468
+ const restoredMode = permissionMode2.getModeBeforePlan() ?? "default";
38469
+ permissionMode2.setMode(restoredMode);
38470
+ }
38453
38471
  return {
38454
38472
  message: `User has approved your plan. You can now start coding.
38455
38473
  ` + `Start with updating your todo list if applicable.
@@ -38457,6 +38475,9 @@ async function exit_plan_mode() {
38457
38475
  ` + "Tip: If this plan will be referenced in the future by your future-self, " + "other agents, or humans, consider renaming the plan file to something easily " + "identifiable with a timestamp (e.g., `2026-01-auth-refactor.md`) rather than the random name."
38458
38476
  };
38459
38477
  }
38478
+ var init_ExitPlanMode2 = __esm(() => {
38479
+ init_mode();
38480
+ });
38460
38481
 
38461
38482
  // src/tools/impl/Glob.ts
38462
38483
  import { execFile } from "node:child_process";
@@ -54856,12 +54877,14 @@ async function executeSubagent(type, config, model, userPrompt, baseURL, subagen
54856
54877
  return executeSubagent(type, config, primaryModel, userPrompt, baseURL, subagentId, true, signal, undefined, undefined, maxTurns);
54857
54878
  }
54858
54879
  }
54880
+ const propagatedError = state.finalError?.trim();
54881
+ const fallbackError = stderr || `Subagent exited with code ${exitCode}`;
54859
54882
  return {
54860
54883
  agentId: state.agentId || "",
54861
54884
  conversationId: state.conversationId || undefined,
54862
54885
  report: "",
54863
54886
  success: false,
54864
- error: stderr || `Subagent exited with code ${exitCode}`
54887
+ error: propagatedError || fallbackError
54865
54888
  };
54866
54889
  }
54867
54890
  if (state.finalResult !== null) {
@@ -55053,6 +55076,34 @@ function extractTaskNotificationsForDisplay(message) {
55053
55076
  }
55054
55077
 
55055
55078
  // src/tools/impl/Task.ts
55079
+ function buildTaskResultHeader(subagentType, result) {
55080
+ return [
55081
+ `subagent_type=${subagentType}`,
55082
+ result.agentId ? `agent_id=${result.agentId}` : undefined,
55083
+ result.conversationId ? `conversation_id=${result.conversationId}` : undefined
55084
+ ].filter(Boolean).join(" ");
55085
+ }
55086
+ function writeTaskTranscriptStart(outputFile, description, subagentType) {
55087
+ appendToOutputFile(outputFile, `[Task started: ${description}]
55088
+ [subagent_type: ${subagentType}]
55089
+
55090
+ `);
55091
+ }
55092
+ function writeTaskTranscriptResult(outputFile, result, header) {
55093
+ if (result.success) {
55094
+ appendToOutputFile(outputFile, `${header}
55095
+
55096
+ ${result.report}
55097
+
55098
+ [Task completed]
55099
+ `);
55100
+ return;
55101
+ }
55102
+ appendToOutputFile(outputFile, `[error] ${result.error || "Subagent execution failed"}
55103
+
55104
+ [Task failed]
55105
+ `);
55106
+ }
55056
55107
  async function task(args) {
55057
55108
  const { command = "run", model, toolCallId, signal } = args;
55058
55109
  if (command === "refresh") {
@@ -55091,7 +55142,7 @@ async function task(args) {
55091
55142
  registerSubagent(subagentId, subagent_type, description, toolCallId, isBackground);
55092
55143
  if (isBackground) {
55093
55144
  const taskId = getNextTaskId();
55094
- const outputFile = createBackgroundOutputFile(taskId);
55145
+ const outputFile2 = createBackgroundOutputFile(taskId);
55095
55146
  const abortController = new AbortController;
55096
55147
  const bgTask = {
55097
55148
  description,
@@ -55100,37 +55151,21 @@ async function task(args) {
55100
55151
  status: "running",
55101
55152
  output: [],
55102
55153
  startTime: new Date,
55103
- outputFile,
55154
+ outputFile: outputFile2,
55104
55155
  abortController
55105
55156
  };
55106
55157
  backgroundTasks.set(taskId, bgTask);
55107
- appendToOutputFile(outputFile, `[Task started: ${description}]
55108
- [subagent_type: ${subagent_type}]
55109
-
55110
- `);
55158
+ writeTaskTranscriptStart(outputFile2, description, subagent_type);
55111
55159
  spawnSubagent(subagent_type, prompt, model, subagentId, abortController.signal, args.agent_id, args.conversation_id, args.max_turns).then((result) => {
55112
55160
  bgTask.status = result.success ? "completed" : "failed";
55113
55161
  if (result.error) {
55114
55162
  bgTask.error = result.error;
55115
55163
  }
55116
- const header = [
55117
- `subagent_type=${subagent_type}`,
55118
- result.agentId ? `agent_id=${result.agentId}` : undefined,
55119
- result.conversationId ? `conversation_id=${result.conversationId}` : undefined
55120
- ].filter(Boolean).join(" ");
55164
+ const header = buildTaskResultHeader(subagent_type, result);
55165
+ writeTaskTranscriptResult(outputFile2, result, header);
55121
55166
  if (result.success) {
55122
- appendToOutputFile(outputFile, `${header}
55123
-
55124
- ${result.report}
55125
- `);
55126
55167
  bgTask.output.push(result.report || "");
55127
- } else {
55128
- appendToOutputFile(outputFile, `[error] ${result.error || "Subagent execution failed"}
55129
- `);
55130
55168
  }
55131
- appendToOutputFile(outputFile, `
55132
- [Task ${result.success ? "completed" : "failed"}]
55133
- `);
55134
55169
  completeSubagent(subagentId, {
55135
55170
  success: result.success,
55136
55171
  error: result.error,
@@ -55149,7 +55184,7 @@ ${result.report || ""}` : result.error || "Subagent execution failed";
55149
55184
  status: result.success ? "completed" : "failed",
55150
55185
  summary: `Agent "${description}" ${result.success ? "completed" : "failed"}`,
55151
55186
  result: truncatedResult,
55152
- outputFile,
55187
+ outputFile: outputFile2,
55153
55188
  usage: {
55154
55189
  totalTokens: result.totalTokens,
55155
55190
  toolUses,
@@ -55162,7 +55197,7 @@ ${result.report || ""}` : result.error || "Subagent execution failed";
55162
55197
  const errorMessage = error instanceof Error ? error.message : String(error);
55163
55198
  bgTask.status = "failed";
55164
55199
  bgTask.error = errorMessage;
55165
- appendToOutputFile(outputFile, `[error] ${errorMessage}
55200
+ appendToOutputFile(outputFile2, `[error] ${errorMessage}
55166
55201
  `);
55167
55202
  completeSubagent(subagentId, { success: false, error: errorMessage });
55168
55203
  const subagentSnapshot = getSnapshot2();
@@ -55173,7 +55208,7 @@ ${result.report || ""}` : result.error || "Subagent execution failed";
55173
55208
  status: "failed",
55174
55209
  summary: `Agent "${description}" failed`,
55175
55210
  result: errorMessage,
55176
- outputFile,
55211
+ outputFile: outputFile2,
55177
55212
  usage: {
55178
55213
  toolUses,
55179
55214
  durationMs
@@ -55183,8 +55218,11 @@ ${result.report || ""}` : result.error || "Subagent execution failed";
55183
55218
  runSubagentStopHooks(subagent_type, subagentId, false, errorMessage, args.agent_id, args.conversation_id).catch(() => {});
55184
55219
  });
55185
55220
  return `Task running in background with ID: ${taskId}
55186
- Output file: ${outputFile}`;
55221
+ Output file: ${outputFile2}`;
55187
55222
  }
55223
+ const foregroundTaskId = getNextTaskId();
55224
+ const outputFile = createBackgroundOutputFile(foregroundTaskId);
55225
+ writeTaskTranscriptStart(outputFile, description, subagent_type);
55188
55226
  try {
55189
55227
  const result = await spawnSubagent(subagent_type, prompt, model, subagentId, signal, args.agent_id, args.conversation_id, args.max_turns);
55190
55228
  completeSubagent(subagentId, {
@@ -55194,24 +55232,34 @@ Output file: ${outputFile}`;
55194
55232
  });
55195
55233
  runSubagentStopHooks(subagent_type, subagentId, result.success, result.error, result.agentId, result.conversationId).catch(() => {});
55196
55234
  if (!result.success) {
55197
- return `Error: ${result.error || "Subagent execution failed"}`;
55235
+ const errorMessage = result.error || "Subagent execution failed";
55236
+ const failedResult = {
55237
+ ...result,
55238
+ error: errorMessage
55239
+ };
55240
+ writeTaskTranscriptResult(outputFile, failedResult, "");
55241
+ return `Error: ${errorMessage}
55242
+ Output file: ${outputFile}`;
55198
55243
  }
55199
- const header = [
55200
- `subagent_type=${subagent_type}`,
55201
- result.agentId ? `agent_id=${result.agentId}` : undefined,
55202
- result.conversationId ? `conversation_id=${result.conversationId}` : undefined
55203
- ].filter(Boolean).join(" ");
55244
+ const header = buildTaskResultHeader(subagent_type, result);
55204
55245
  const fullOutput = `${header}
55205
55246
 
55206
55247
  ${result.report}`;
55248
+ writeTaskTranscriptResult(outputFile, result, header);
55207
55249
  const userCwd = process.env.USER_CWD || process.cwd();
55208
55250
  const { content: truncatedOutput } = truncateByChars(fullOutput, LIMITS.TASK_OUTPUT_CHARS, "Task", { workingDirectory: userCwd, toolName: "Task" });
55209
- return truncatedOutput;
55251
+ return `${truncatedOutput}
55252
+ Output file: ${outputFile}`;
55210
55253
  } catch (error) {
55211
55254
  const errorMessage = error instanceof Error ? error.message : String(error);
55212
55255
  completeSubagent(subagentId, { success: false, error: errorMessage });
55213
55256
  runSubagentStopHooks(subagent_type, subagentId, false, errorMessage, args.agent_id, args.conversation_id).catch(() => {});
55214
- return `Error: ${errorMessage}`;
55257
+ appendToOutputFile(outputFile, `[error] ${errorMessage}
55258
+
55259
+ [Task failed]
55260
+ `);
55261
+ return `Error: ${errorMessage}
55262
+ Output file: ${outputFile}`;
55215
55263
  }
55216
55264
  }
55217
55265
  var VALID_DEPLOY_TYPES;
@@ -55584,7 +55632,7 @@ var init_EnterPlanMode3 = __esm(() => {
55584
55632
 
55585
55633
  // src/tools/schemas/ExitPlanMode.json
55586
55634
  var ExitPlanMode_default2;
55587
- var init_ExitPlanMode2 = __esm(() => {
55635
+ var init_ExitPlanMode3 = __esm(() => {
55588
55636
  ExitPlanMode_default2 = {
55589
55637
  type: "object",
55590
55638
  properties: {},
@@ -56535,6 +56583,7 @@ var init_toolDefinitions = __esm(async () => {
56535
56583
  init_BashOutput2();
56536
56584
  init_Edit2();
56537
56585
  init_EnterPlanMode2();
56586
+ init_ExitPlanMode2();
56538
56587
  init_Glob2();
56539
56588
  init_GlobGemini2();
56540
56589
  init_Grep2();
@@ -56559,7 +56608,7 @@ var init_toolDefinitions = __esm(async () => {
56559
56608
  init_BashOutput3();
56560
56609
  init_Edit3();
56561
56610
  init_EnterPlanMode3();
56562
- init_ExitPlanMode2();
56611
+ init_ExitPlanMode3();
56563
56612
  init_Glob3();
56564
56613
  init_GlobGemini3();
56565
56614
  init_Grep3();
@@ -58591,6 +58640,7 @@ var exports_analyzer = {};
58591
58640
  __export(exports_analyzer, {
58592
58641
  analyzeApprovalContext: () => analyzeApprovalContext
58593
58642
  });
58643
+ import { homedir as homedir11 } from "node:os";
58594
58644
  import { dirname as dirname8, resolve as resolve15 } from "node:path";
58595
58645
  function analyzeApprovalContext(toolName, toolArgs, workingDirectory) {
58596
58646
  const resolveFilePath = () => {
@@ -58691,7 +58741,85 @@ function containsDangerousCommand(command) {
58691
58741
  }
58692
58742
  return false;
58693
58743
  }
58694
- function analyzeBashApproval(command, _workingDir) {
58744
+ function escapeRegex(text) {
58745
+ return text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
58746
+ }
58747
+ function normalizePathSeparators(path18) {
58748
+ return path18.replace(/\\/g, "/");
58749
+ }
58750
+ function parseAbsoluteCommandPaths(command) {
58751
+ const normalized = command.replace(/\\"/g, '"').replace(/\\'/g, "'");
58752
+ const candidates = [];
58753
+ const quotedRegex = /["']((?:[A-Za-z]:)?\/[^"']+)["']/g;
58754
+ let quotedMatch = quotedRegex.exec(normalized);
58755
+ while (quotedMatch) {
58756
+ if (quotedMatch[1]) {
58757
+ candidates.push(normalizePathSeparators(quotedMatch[1]));
58758
+ }
58759
+ quotedMatch = quotedRegex.exec(normalized);
58760
+ }
58761
+ const tokens = normalized.split(/\s+/);
58762
+ for (const token of tokens) {
58763
+ const cleaned = token.replace(/^["'`([{]+/, "").replace(/["'`),;|\]}]+$/g, "");
58764
+ if (/^(?:[A-Za-z]:)?\//.test(cleaned)) {
58765
+ candidates.push(normalizePathSeparators(cleaned));
58766
+ }
58767
+ }
58768
+ return Array.from(new Set(candidates));
58769
+ }
58770
+ function detectSkillScript(command, workingDir) {
58771
+ const pathCandidates = parseAbsoluteCommandPaths(command);
58772
+ if (pathCandidates.length === 0) {
58773
+ return null;
58774
+ }
58775
+ const normalizedWorkingDir = normalizePathSeparators(workingDir).replace(/\/$/, "");
58776
+ const normalizedHomeDir = normalizePathSeparators(homedir11()).replace(/\/$/, "");
58777
+ const detect = (source, regex2) => {
58778
+ for (const candidate of pathCandidates) {
58779
+ const match3 = candidate.match(regex2);
58780
+ if (!match3?.[1]) {
58781
+ continue;
58782
+ }
58783
+ const skillName = match3[1];
58784
+ const skillRootPath = match3[0].replace(/\/scripts\/$/, "");
58785
+ return { source, skillName, skillRootPath };
58786
+ }
58787
+ return null;
58788
+ };
58789
+ const projectRegex = new RegExp(`^${escapeRegex(normalizedWorkingDir)}/\\.skills/(.+?)/scripts/`);
58790
+ const projectSkill = detect("project", projectRegex);
58791
+ if (projectSkill) {
58792
+ return projectSkill;
58793
+ }
58794
+ const agentRegex = new RegExp(`^${escapeRegex(normalizedHomeDir)}/\\.letta/agents/[^/]+/skills/(.+?)/scripts/`);
58795
+ const agentSkill = detect("agent-scoped", agentRegex);
58796
+ if (agentSkill) {
58797
+ return agentSkill;
58798
+ }
58799
+ const globalRegex = new RegExp(`^${escapeRegex(normalizedHomeDir)}/\\.letta/skills/(.+?)/scripts/`);
58800
+ const globalSkill = detect("global", globalRegex);
58801
+ if (globalSkill) {
58802
+ return globalSkill;
58803
+ }
58804
+ const bundledSkill = detect("bundled", /\/skills\/builtin\/([^/]+)\/scripts\//);
58805
+ if (bundledSkill) {
58806
+ return bundledSkill;
58807
+ }
58808
+ return null;
58809
+ }
58810
+ function buildSkillScriptRule(command, skillRootPath) {
58811
+ const normalizedCommand = normalizePathSeparators(command).trim();
58812
+ const rootIndex = normalizedCommand.indexOf(skillRootPath);
58813
+ if (rootIndex === -1) {
58814
+ return `Bash(${normalizedCommand})`;
58815
+ }
58816
+ const rulePrefix = normalizedCommand.slice(0, rootIndex + skillRootPath.length);
58817
+ return `Bash(${rulePrefix}:*)`;
58818
+ }
58819
+ function getSkillApprovalText(source, skillName) {
58820
+ return `Yes, and don't ask again for scripts in ${source} skill '${skillName}'`;
58821
+ }
58822
+ function analyzeBashApproval(command, workingDir) {
58695
58823
  const parts = command.trim().split(/\s+/);
58696
58824
  const baseCommand = parts[0] || "";
58697
58825
  const firstArg = parts[1] || "";
@@ -58715,6 +58843,18 @@ function analyzeBashApproval(command, _workingDir) {
58715
58843
  safetyLevel: "dangerous"
58716
58844
  };
58717
58845
  }
58846
+ const skillScript = detectSkillScript(command, workingDir);
58847
+ if (skillScript) {
58848
+ const { source, skillName, skillRootPath } = skillScript;
58849
+ return {
58850
+ recommendedRule: buildSkillScriptRule(command, skillRootPath),
58851
+ ruleDescription: `scripts in ${source} skill '${skillName}'`,
58852
+ approveAlwaysText: getSkillApprovalText(source, skillName),
58853
+ defaultScope: "project",
58854
+ allowPersistence: true,
58855
+ safetyLevel: "moderate"
58856
+ };
58857
+ }
58718
58858
  if (baseCommand === "git") {
58719
58859
  const gitSubcommand = firstArg;
58720
58860
  const safeGitCommands = ["status", "diff", "log", "show", "branch"];
@@ -58995,25 +59135,32 @@ var init_filter = __esm(() => {
58995
59135
  var exports_manager2 = {};
58996
59136
  __export(exports_manager2, {
58997
59137
  waitForToolsetReady: () => waitForToolsetReady,
59138
+ setExternalToolExecutor: () => setExternalToolExecutor,
58998
59139
  savePermissionRule: () => savePermissionRule2,
58999
59140
  requiresApproval: () => requiresApproval,
59141
+ registerExternalTools: () => registerExternalTools,
59000
59142
  loadTools: () => loadTools,
59001
59143
  loadSpecificTools: () => loadSpecificTools,
59002
59144
  isToolsetSwitchInProgress: () => isToolsetSwitchInProgress,
59003
59145
  isOpenAIModel: () => isOpenAIModel,
59004
59146
  isGeminiModel: () => isGeminiModel,
59147
+ isExternalTool: () => isExternalTool,
59005
59148
  getToolSchemas: () => getToolSchemas,
59006
59149
  getToolSchema: () => getToolSchema,
59007
59150
  getToolPermissions: () => getToolPermissions,
59008
59151
  getToolNames: () => getToolNames,
59009
59152
  getServerToolName: () => getServerToolName,
59010
59153
  getInternalToolName: () => getInternalToolName,
59154
+ getExternalToolsAsClientTools: () => getExternalToolsAsClientTools,
59155
+ getExternalToolDefinition: () => getExternalToolDefinition,
59011
59156
  getClientToolsFromRegistry: () => getClientToolsFromRegistry,
59012
59157
  getAllLettaToolNames: () => getAllLettaToolNames,
59013
59158
  executeTool: () => executeTool,
59159
+ executeExternalTool: () => executeExternalTool,
59014
59160
  clipToolReturn: () => clipToolReturn,
59015
59161
  clearToolsWithLock: () => clearToolsWithLock,
59016
59162
  clearTools: () => clearTools,
59163
+ clearExternalTools: () => clearExternalTools,
59017
59164
  checkToolPermission: () => checkToolPermission,
59018
59165
  analyzeToolApproval: () => analyzeToolApproval,
59019
59166
  TOOL_NAMES: () => TOOL_NAMES,
@@ -59087,8 +59234,65 @@ function resolveInternalToolName(name) {
59087
59234
  }
59088
59235
  return;
59089
59236
  }
59237
+ function getExternalToolsRegistry() {
59238
+ const global2 = globalThis;
59239
+ if (!global2[EXTERNAL_TOOLS_KEY]) {
59240
+ global2[EXTERNAL_TOOLS_KEY] = new Map;
59241
+ }
59242
+ return global2[EXTERNAL_TOOLS_KEY];
59243
+ }
59244
+ function registerExternalTools(tools) {
59245
+ const registry = getExternalToolsRegistry();
59246
+ for (const tool of tools) {
59247
+ registry.set(tool.name, tool);
59248
+ }
59249
+ }
59250
+ function setExternalToolExecutor(executor) {
59251
+ globalThis[EXTERNAL_EXECUTOR_KEY] = executor;
59252
+ }
59253
+ function clearExternalTools() {
59254
+ getExternalToolsRegistry().clear();
59255
+ delete globalThis[EXTERNAL_EXECUTOR_KEY];
59256
+ }
59257
+ function isExternalTool(name) {
59258
+ return getExternalToolsRegistry().has(name);
59259
+ }
59260
+ function getExternalToolDefinition(name) {
59261
+ return getExternalToolsRegistry().get(name);
59262
+ }
59263
+ function getExternalToolsAsClientTools() {
59264
+ return Array.from(getExternalToolsRegistry().values()).map((tool) => ({
59265
+ name: tool.name,
59266
+ description: tool.description,
59267
+ parameters: tool.parameters
59268
+ }));
59269
+ }
59270
+ async function executeExternalTool(toolCallId, toolName, input) {
59271
+ const executor = globalThis[EXTERNAL_EXECUTOR_KEY];
59272
+ if (!executor) {
59273
+ return {
59274
+ toolReturn: `External tool executor not set for tool: ${toolName}`,
59275
+ status: "error"
59276
+ };
59277
+ }
59278
+ try {
59279
+ const result = await executor(toolCallId, toolName, input);
59280
+ const textContent = result.content.filter((c) => c.type === "text" && c.text).map((c) => c.text).join(`
59281
+ `);
59282
+ return {
59283
+ toolReturn: textContent || JSON.stringify(result.content),
59284
+ status: result.isError ? "error" : "success"
59285
+ };
59286
+ } catch (error) {
59287
+ const errorMessage = error instanceof Error ? error.message : String(error);
59288
+ return {
59289
+ toolReturn: `External tool execution error: ${errorMessage}`,
59290
+ status: "error"
59291
+ };
59292
+ }
59293
+ }
59090
59294
  function getClientToolsFromRegistry() {
59091
- return Array.from(toolRegistry.entries()).map(([name, tool]) => {
59295
+ const builtInTools = Array.from(toolRegistry.entries()).map(([name, tool]) => {
59092
59296
  const serverName = getServerToolName(name);
59093
59297
  return {
59094
59298
  name: serverName,
@@ -59096,6 +59300,8 @@ function getClientToolsFromRegistry() {
59096
59300
  parameters: tool.schema.input_schema
59097
59301
  };
59098
59302
  });
59303
+ const externalTools = getExternalToolsAsClientTools();
59304
+ return [...builtInTools, ...externalTools];
59099
59305
  }
59100
59306
  function getToolPermissions(toolName) {
59101
59307
  return TOOL_PERMISSIONS[toolName] || { requiresApproval: false };
@@ -59357,6 +59563,9 @@ ${files.join(`
59357
59563
  return JSON.stringify(result);
59358
59564
  }
59359
59565
  async function executeTool(name, args, options) {
59566
+ if (isExternalTool(name)) {
59567
+ return executeExternalTool(options?.toolCallId ?? `ext-${Date.now()}`, name, args);
59568
+ }
59360
59569
  const internalName = resolveInternalToolName(name);
59361
59570
  if (!internalName) {
59362
59571
  return {
@@ -59523,7 +59732,7 @@ function clearToolsWithLock() {
59523
59732
  releaseSwitchLock();
59524
59733
  }
59525
59734
  }
59526
- var TOOL_NAMES, STREAMING_SHELL_TOOLS, TOOL_NAME_MAPPINGS, ANTHROPIC_DEFAULT_TOOLS, OPENAI_DEFAULT_TOOLS, GEMINI_DEFAULT_TOOLS, OPENAI_PASCAL_TOOLS, GEMINI_PASCAL_TOOLS, TOOL_PERMISSIONS, REGISTRY_KEY, SWITCH_LOCK_KEY, toolRegistry;
59735
+ var TOOL_NAMES, STREAMING_SHELL_TOOLS, TOOL_NAME_MAPPINGS, ANTHROPIC_DEFAULT_TOOLS, OPENAI_DEFAULT_TOOLS, GEMINI_DEFAULT_TOOLS, OPENAI_PASCAL_TOOLS, GEMINI_PASCAL_TOOLS, TOOL_PERMISSIONS, REGISTRY_KEY, SWITCH_LOCK_KEY, toolRegistry, EXTERNAL_TOOLS_KEY, EXTERNAL_EXECUTOR_KEY;
59527
59736
  var init_manager3 = __esm(async () => {
59528
59737
  init_model();
59529
59738
  init_subagents();
@@ -59685,6 +59894,8 @@ var init_manager3 = __esm(async () => {
59685
59894
  REGISTRY_KEY = Symbol.for("@letta/toolRegistry");
59686
59895
  SWITCH_LOCK_KEY = Symbol.for("@letta/toolSwitchLock");
59687
59896
  toolRegistry = getRegistry();
59897
+ EXTERNAL_TOOLS_KEY = Symbol.for("@letta/externalTools");
59898
+ EXTERNAL_EXECUTOR_KEY = Symbol.for("@letta/externalToolExecutor");
59688
59899
  });
59689
59900
 
59690
59901
  // src/agent/approval-execution.ts
@@ -61980,6 +62191,23 @@ function isConversationBusyError(detail) {
61980
62191
  return false;
61981
62192
  return detail.toLowerCase().includes(CONVERSATION_BUSY_DETAIL_FRAGMENT);
61982
62193
  }
62194
+ function classifyPreStreamConflict(detail) {
62195
+ if (isApprovalPendingError(detail))
62196
+ return "approval_pending";
62197
+ if (isConversationBusyError(detail))
62198
+ return "conversation_busy";
62199
+ return null;
62200
+ }
62201
+ function getPreStreamErrorAction(detail, conversationBusyRetries, maxConversationBusyRetries) {
62202
+ const kind = classifyPreStreamConflict(detail);
62203
+ if (kind === "approval_pending") {
62204
+ return "resolve_approval_pending";
62205
+ }
62206
+ if (kind === "conversation_busy" && conversationBusyRetries < maxConversationBusyRetries) {
62207
+ return "retry_conversation_busy";
62208
+ }
62209
+ return "rethrow";
62210
+ }
61983
62211
  async function fetchRunErrorDetail(runId) {
61984
62212
  if (!runId)
61985
62213
  return null;
@@ -61992,7 +62220,7 @@ async function fetchRunErrorDetail(runId) {
61992
62220
  return null;
61993
62221
  }
61994
62222
  }
61995
- var INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids", APPROVAL_PENDING_DETAIL_FRAGMENT = "cannot send a new message", CONVERSATION_BUSY_DETAIL_FRAGMENT = "another request is currently being processed";
62223
+ var INVALID_TOOL_CALL_IDS_FRAGMENT = "invalid tool call ids", APPROVAL_PENDING_DETAIL_FRAGMENT = "waiting for approval", CONVERSATION_BUSY_DETAIL_FRAGMENT = "another request is currently being processed";
61996
62224
  var init_approval_recovery = __esm(async () => {
61997
62225
  await init_client2();
61998
62226
  });
@@ -62224,7 +62452,7 @@ function buildModelSettings(modelHandle, updateArgs) {
62224
62452
  } else {
62225
62453
  settings = {};
62226
62454
  }
62227
- if (typeof updateArgs?.max_output_tokens === "number") {
62455
+ if (typeof updateArgs?.max_output_tokens === "number" && "provider_type" in settings) {
62228
62456
  settings.max_output_tokens = updateArgs.max_output_tokens;
62229
62457
  }
62230
62458
  return settings;
@@ -62237,7 +62465,10 @@ async function updateAgentLLMConfig(agentId, modelHandle, updateArgs) {
62237
62465
  await client.agents.update(agentId, {
62238
62466
  model: modelHandle,
62239
62467
  ...hasModelSettings && { model_settings: modelSettings },
62240
- ...contextWindow && { context_window_limit: contextWindow }
62468
+ ...contextWindow && { context_window_limit: contextWindow },
62469
+ ...typeof updateArgs?.max_output_tokens === "number" && {
62470
+ max_tokens: updateArgs.max_output_tokens
62471
+ }
62241
62472
  });
62242
62473
  const finalAgent = await client.agents.retrieve(agentId);
62243
62474
  return finalAgent.llm_config;
@@ -63117,6 +63348,181 @@ function findLastSafeSplitPoint(content) {
63117
63348
  }
63118
63349
  var MIN_SPLIT_LENGTH = 1500;
63119
63350
 
63351
+ // src/tools/interactivePolicy.ts
63352
+ function isInteractiveApprovalTool(toolName) {
63353
+ return INTERACTIVE_APPROVAL_TOOLS.has(toolName);
63354
+ }
63355
+ function isHeadlessAutoAllowTool(toolName) {
63356
+ return HEADLESS_AUTO_ALLOW_TOOLS.has(toolName);
63357
+ }
63358
+ var INTERACTIVE_APPROVAL_TOOLS, RUNTIME_USER_INPUT_TOOLS, HEADLESS_AUTO_ALLOW_TOOLS;
63359
+ var init_interactivePolicy = __esm(() => {
63360
+ INTERACTIVE_APPROVAL_TOOLS = new Set([
63361
+ "AskUserQuestion",
63362
+ "EnterPlanMode",
63363
+ "ExitPlanMode"
63364
+ ]);
63365
+ RUNTIME_USER_INPUT_TOOLS = new Set(["AskUserQuestion", "ExitPlanMode"]);
63366
+ HEADLESS_AUTO_ALLOW_TOOLS = new Set(["EnterPlanMode"]);
63367
+ });
63368
+
63369
+ // src/tools/toolset.ts
63370
+ var exports_toolset = {};
63371
+ __export(exports_toolset, {
63372
+ switchToolsetForModel: () => switchToolsetForModel,
63373
+ reattachMemoryTool: () => reattachMemoryTool,
63374
+ forceToolsetSwitch: () => forceToolsetSwitch,
63375
+ ensureCorrectMemoryTool: () => ensureCorrectMemoryTool,
63376
+ detachMemoryTools: () => detachMemoryTools,
63377
+ MEMORY_TOOL_NAMES: () => MEMORY_TOOL_NAMES
63378
+ });
63379
+ async function ensureCorrectMemoryTool(agentId, modelIdentifier, useMemoryPatch) {
63380
+ const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
63381
+ const client = await getClient2();
63382
+ const shouldUsePatch = useMemoryPatch !== undefined ? useMemoryPatch : isOpenAIModel(resolvedModel);
63383
+ try {
63384
+ const agentWithTools = await client.agents.retrieve(agentId, {
63385
+ include: ["agent.tools"]
63386
+ });
63387
+ const currentTools = agentWithTools.tools || [];
63388
+ const mapByName = new Map(currentTools.map((t) => [t.name, t.id]));
63389
+ const hasAnyMemoryTool = mapByName.has("memory") || mapByName.has("memory_apply_patch");
63390
+ if (!hasAnyMemoryTool) {
63391
+ return;
63392
+ }
63393
+ const desiredMemoryTool = shouldUsePatch ? "memory_apply_patch" : "memory";
63394
+ const otherMemoryTool = desiredMemoryTool === "memory" ? "memory_apply_patch" : "memory";
63395
+ let desiredId = mapByName.get(desiredMemoryTool);
63396
+ if (!desiredId) {
63397
+ const resp = await client.tools.list({ name: desiredMemoryTool });
63398
+ desiredId = resp.items[0]?.id;
63399
+ }
63400
+ if (!desiredId) {
63401
+ return;
63402
+ }
63403
+ const otherId = mapByName.get(otherMemoryTool);
63404
+ if (mapByName.has(desiredMemoryTool) && !otherId) {
63405
+ return;
63406
+ }
63407
+ const currentIds = currentTools.map((t) => t.id).filter((id) => typeof id === "string");
63408
+ const newIds = new Set(currentIds);
63409
+ if (otherId)
63410
+ newIds.delete(otherId);
63411
+ newIds.add(desiredId);
63412
+ const updatedRules = (agentWithTools.tool_rules || []).map((r) => r.tool_name === otherMemoryTool ? { ...r, tool_name: desiredMemoryTool } : r);
63413
+ await client.agents.update(agentId, {
63414
+ tool_ids: Array.from(newIds),
63415
+ tool_rules: updatedRules
63416
+ });
63417
+ } catch (err) {
63418
+ console.warn(`Warning: Failed to sync memory tool: ${err instanceof Error ? err.message : String(err)}`);
63419
+ }
63420
+ }
63421
+ async function detachMemoryTools(agentId) {
63422
+ const client = await getClient2();
63423
+ try {
63424
+ const agentWithTools = await client.agents.retrieve(agentId, {
63425
+ include: ["agent.tools"]
63426
+ });
63427
+ const currentTools = agentWithTools.tools || [];
63428
+ let detachedAny = false;
63429
+ for (const tool of currentTools) {
63430
+ if (tool.name && MEMORY_TOOL_NAMES.has(tool.name)) {
63431
+ if (tool.id) {
63432
+ await client.agents.tools.detach(tool.id, { agent_id: agentId });
63433
+ detachedAny = true;
63434
+ }
63435
+ }
63436
+ }
63437
+ return detachedAny;
63438
+ } catch (err) {
63439
+ console.warn(`Warning: Failed to detach memory tools: ${err instanceof Error ? err.message : String(err)}`);
63440
+ return false;
63441
+ }
63442
+ }
63443
+ async function reattachMemoryTool(agentId, modelIdentifier) {
63444
+ const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
63445
+ const client = await getClient2();
63446
+ const shouldUsePatch = isOpenAIModel(resolvedModel);
63447
+ try {
63448
+ const agentWithTools = await client.agents.retrieve(agentId, {
63449
+ include: ["agent.tools"]
63450
+ });
63451
+ const currentTools = agentWithTools.tools || [];
63452
+ const mapByName = new Map(currentTools.map((t) => [t.name, t.id]));
63453
+ const desiredMemoryTool = shouldUsePatch ? "memory_apply_patch" : "memory";
63454
+ if (mapByName.has(desiredMemoryTool)) {
63455
+ return;
63456
+ }
63457
+ const resp = await client.tools.list({ name: desiredMemoryTool });
63458
+ const toolId = resp.items[0]?.id;
63459
+ if (!toolId) {
63460
+ console.warn(`Memory tool "${desiredMemoryTool}" not found on server`);
63461
+ return;
63462
+ }
63463
+ await client.agents.tools.attach(toolId, { agent_id: agentId });
63464
+ } catch (err) {
63465
+ console.warn(`Warning: Failed to reattach memory tool: ${err instanceof Error ? err.message : String(err)}`);
63466
+ }
63467
+ }
63468
+ async function forceToolsetSwitch(toolsetName, agentId) {
63469
+ let modelForLoading;
63470
+ if (toolsetName === "none") {
63471
+ clearToolsWithLock();
63472
+ return;
63473
+ } else if (toolsetName === "codex") {
63474
+ await loadSpecificTools([...CODEX_TOOLS]);
63475
+ modelForLoading = "openai/gpt-4";
63476
+ } else if (toolsetName === "codex_snake") {
63477
+ await loadTools("openai/gpt-4");
63478
+ modelForLoading = "openai/gpt-4";
63479
+ } else if (toolsetName === "gemini") {
63480
+ await loadSpecificTools([...GEMINI_TOOLS]);
63481
+ modelForLoading = "google_ai/gemini-3-pro-preview";
63482
+ } else if (toolsetName === "gemini_snake") {
63483
+ await loadTools("google_ai/gemini-3-pro-preview");
63484
+ modelForLoading = "google_ai/gemini-3-pro-preview";
63485
+ } else {
63486
+ await loadTools("anthropic/claude-sonnet-4");
63487
+ modelForLoading = "anthropic/claude-sonnet-4";
63488
+ }
63489
+ const useMemoryPatch = toolsetName === "codex" || toolsetName === "codex_snake";
63490
+ await ensureCorrectMemoryTool(agentId, modelForLoading, useMemoryPatch);
63491
+ }
63492
+ async function switchToolsetForModel(modelIdentifier, agentId) {
63493
+ const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
63494
+ await loadTools(resolvedModel);
63495
+ const loadedAfterPrimary = getToolNames().length;
63496
+ if (loadedAfterPrimary === 0 && !toolFilter.isActive()) {
63497
+ await loadTools();
63498
+ if (getToolNames().length === 0) {
63499
+ throw new Error(`Failed to load any Letta tools for model "${resolvedModel}".`);
63500
+ }
63501
+ }
63502
+ await ensureCorrectMemoryTool(agentId, resolvedModel);
63503
+ const { isGeminiModel: isGeminiModel3 } = await init_manager3().then(() => exports_manager2);
63504
+ const toolsetName = isOpenAIModel(resolvedModel) ? "codex" : isGeminiModel3(resolvedModel) ? "gemini" : "default";
63505
+ return toolsetName;
63506
+ }
63507
+ var CODEX_TOOLS, GEMINI_TOOLS, MEMORY_TOOL_NAMES;
63508
+ var init_toolset = __esm(async () => {
63509
+ init_model();
63510
+ init_filter();
63511
+ await __promiseAll([
63512
+ init_client2(),
63513
+ init_manager3()
63514
+ ]);
63515
+ CODEX_TOOLS = OPENAI_PASCAL_TOOLS;
63516
+ GEMINI_TOOLS = GEMINI_PASCAL_TOOLS;
63517
+ MEMORY_TOOL_NAMES = new Set([
63518
+ "memory",
63519
+ "memory_apply_patch",
63520
+ "memory_insert",
63521
+ "memory_replace",
63522
+ "memory_rethink"
63523
+ ]);
63524
+ });
63525
+
63120
63526
  // src/cli/helpers/toolNameMapping.ts
63121
63527
  function getDisplayToolName(rawName) {
63122
63528
  if (rawName === "write")
@@ -63219,10 +63625,10 @@ function isPlanTool(rawName, displayName) {
63219
63625
  return rawName === "update_plan" || rawName === "UpdatePlan" || displayName === "Planning";
63220
63626
  }
63221
63627
  function alwaysRequiresUserInput(name) {
63222
- return name === "AskUserQuestion" || name === "EnterPlanMode" || name === "ExitPlanMode";
63628
+ return isInteractiveApprovalTool(name);
63223
63629
  }
63224
63630
  function isMemoryTool(name) {
63225
- return name === "memory" || name === "memory_apply_patch";
63631
+ return MEMORY_TOOL_NAMES.has(name);
63226
63632
  }
63227
63633
  function isFileEditTool(name) {
63228
63634
  return name === "edit" || name === "Edit" || name === "multi_edit" || name === "MultiEdit" || name === "Replace" || name === "replace";
@@ -63246,6 +63652,10 @@ function isSearchTool(name) {
63246
63652
  function isGlobTool(name) {
63247
63653
  return name === "glob" || name === "Glob" || name === "glob_gemini" || name === "GlobGemini";
63248
63654
  }
63655
+ var init_toolNameMapping = __esm(async () => {
63656
+ init_interactivePolicy();
63657
+ await init_toolset();
63658
+ });
63249
63659
 
63250
63660
  // src/cli/helpers/accumulator.ts
63251
63661
  function appendStreamingOutput(state, chunk, startTime, isStderr = false) {
@@ -63705,6 +64115,11 @@ function onChunk(b, chunk, ctx) {
63705
64115
  eventData: eventChunk.event_data || {},
63706
64116
  phase: "running"
63707
64117
  }));
64118
+ if (eventType === "compaction") {
64119
+ runPreCompactHooks(ctx?.lastContextTokens, undefined, b.agentId, undefined).catch((error) => {
64120
+ debugLog("hooks", "PreCompact hook error (accumulator)", error);
64121
+ });
64122
+ }
63708
64123
  break;
63709
64124
  }
63710
64125
  break;
@@ -63749,7 +64164,10 @@ var init_accumulator = __esm(async () => {
63749
64164
  init_constants();
63750
64165
  init_debug();
63751
64166
  init_backfill();
63752
- await init_hooks();
64167
+ await __promiseAll([
64168
+ init_hooks(),
64169
+ init_toolNameMapping()
64170
+ ]);
63753
64171
  CANCEL_REASON_TEXT = {
63754
64172
  user_interrupt: INTERRUPTED_BY_USER,
63755
64173
  stream_error: "Stream error",
@@ -63864,6 +64282,49 @@ var init_errorContext = __esm(() => {
63864
64282
  });
63865
64283
 
63866
64284
  // src/cli/helpers/errorFormatter.ts
64285
+ function extractReasonList(value) {
64286
+ if (!Array.isArray(value))
64287
+ return [];
64288
+ return value.filter((reason) => typeof reason === "string").map((reason) => reason.toLowerCase());
64289
+ }
64290
+ function getErrorReasons(e) {
64291
+ const reasons = new Set;
64292
+ const errorBody = e.error;
64293
+ if (errorBody && typeof errorBody === "object") {
64294
+ const body = errorBody;
64295
+ for (const reason of extractReasonList(body.reasons)) {
64296
+ reasons.add(reason);
64297
+ }
64298
+ if (body.error && typeof body.error === "object") {
64299
+ const nested = body.error;
64300
+ for (const reason of extractReasonList(nested.reasons)) {
64301
+ reasons.add(reason);
64302
+ }
64303
+ }
64304
+ }
64305
+ const message = e.message?.toLowerCase() ?? "";
64306
+ for (const knownReason of [
64307
+ "not-enough-credits",
64308
+ "model-unknown",
64309
+ "byok-not-available-on-free-tier",
64310
+ "free-usage-exceeded",
64311
+ "premium-usage-exceeded",
64312
+ "standard-usage-exceeded",
64313
+ "basic-usage-exceeded",
64314
+ "context-window-size-not-supported",
64315
+ "agents-limit-exceeded",
64316
+ "exceeded-quota"
64317
+ ]) {
64318
+ if (message.includes(knownReason)) {
64319
+ reasons.add(knownReason);
64320
+ }
64321
+ }
64322
+ return Array.from(reasons);
64323
+ }
64324
+ function hasErrorReason(e, reason, reasons) {
64325
+ const allReasons = reasons ?? getErrorReasons(e);
64326
+ return allReasons.includes(reason.toLowerCase());
64327
+ }
63867
64328
  function getRateLimitResetMs(e) {
63868
64329
  if (e.status !== 429)
63869
64330
  return;
@@ -63917,52 +64378,87 @@ function getResourceLimitMessage(e) {
63917
64378
  }
63918
64379
  return;
63919
64380
  }
63920
- function isAgentLimitError(e) {
64381
+ function isAgentLimitError(e, reasons) {
63921
64382
  if (e.status !== 429)
63922
64383
  return false;
63923
- const errorBody = e.error;
63924
- if (errorBody && typeof errorBody === "object") {
63925
- if ("reasons" in errorBody && Array.isArray(errorBody.reasons)) {
63926
- if (errorBody.reasons.includes("agents-limit-exceeded")) {
63927
- return true;
63928
- }
63929
- }
63930
- }
63931
- return false;
64384
+ return hasErrorReason(e, "agents-limit-exceeded", reasons);
63932
64385
  }
63933
- function isCreditExhaustedError(e) {
64386
+ function isCreditExhaustedError(e, reasons) {
63934
64387
  if (e.status !== 402)
63935
64388
  return false;
63936
- const errorBody = e.error;
63937
- if (errorBody && typeof errorBody === "object") {
63938
- if ("reasons" in errorBody && Array.isArray(errorBody.reasons)) {
63939
- if (errorBody.reasons.includes("not-enough-credits")) {
63940
- return true;
64389
+ return hasErrorReason(e, "not-enough-credits", reasons);
64390
+ }
64391
+ function findEncryptedContentDetail(e) {
64392
+ if (typeof e !== "object" || e === null)
64393
+ return;
64394
+ const obj = e;
64395
+ if (typeof obj.detail === "string" && obj.detail.includes("invalid_encrypted_content")) {
64396
+ return obj.detail;
64397
+ }
64398
+ if (obj.error && typeof obj.error === "object") {
64399
+ const errObj = obj.error;
64400
+ if (errObj.error && typeof errObj.error === "object") {
64401
+ const inner = errObj.error;
64402
+ if (typeof inner.detail === "string" && inner.detail.includes("invalid_encrypted_content")) {
64403
+ return inner.detail;
63941
64404
  }
63942
64405
  }
63943
- if ("error" in errorBody && typeof errorBody.error === "object") {
63944
- const nested = errorBody.error;
63945
- if ("reasons" in nested && Array.isArray(nested.reasons)) {
63946
- if (nested.reasons.includes("not-enough-credits")) {
63947
- return true;
63948
- }
63949
- }
64406
+ if (typeof errObj.detail === "string" && errObj.detail.includes("invalid_encrypted_content")) {
64407
+ return errObj.detail;
63950
64408
  }
63951
64409
  }
63952
- if (e.message?.includes("not-enough-credits")) {
63953
- return true;
64410
+ return;
64411
+ }
64412
+ function checkEncryptedContentError(e) {
64413
+ const detail = findEncryptedContentDetail(e);
64414
+ if (!detail) {
64415
+ try {
64416
+ const errorStr = typeof e === "string" ? e : JSON.stringify(e);
64417
+ if (!errorStr.includes("invalid_encrypted_content"))
64418
+ return;
64419
+ } catch {
64420
+ return;
64421
+ }
64422
+ return "OpenAI error: Encrypted content could not be verified — organization mismatch." + ENCRYPTED_CONTENT_HINT;
63954
64423
  }
63955
- return false;
64424
+ try {
64425
+ const jsonStart = detail.indexOf("{");
64426
+ if (jsonStart >= 0) {
64427
+ const parsed = JSON.parse(detail.slice(jsonStart));
64428
+ const innerError = parsed.error || parsed;
64429
+ if (innerError.code === "invalid_encrypted_content") {
64430
+ const msg = String(innerError.message || "Encrypted content verification failed.").replaceAll('"', "\\\"");
64431
+ return [
64432
+ "OpenAI error:",
64433
+ " {",
64434
+ ` type: "${innerError.type || "invalid_request_error"}",`,
64435
+ ` code: "${innerError.code}",`,
64436
+ ` message: "${msg}"`,
64437
+ " }",
64438
+ ENCRYPTED_CONTENT_HINT
64439
+ ].join(`
64440
+ `);
64441
+ }
64442
+ }
64443
+ } catch {}
64444
+ return "OpenAI error: Encrypted content could not be verified — organization mismatch." + ENCRYPTED_CONTENT_HINT;
64445
+ }
64446
+ function isEncryptedContentError(e) {
64447
+ return findEncryptedContentDetail(e) !== undefined;
63956
64448
  }
63957
64449
  function formatErrorDetails(e, agentId, conversationId) {
63958
64450
  let runId;
64451
+ const encryptedContentMsg = checkEncryptedContentError(e);
64452
+ if (encryptedContentMsg)
64453
+ return encryptedContentMsg;
63959
64454
  if (e instanceof APIError2) {
64455
+ const reasons = getErrorReasons(e);
63960
64456
  const rateLimitResetMs = getRateLimitResetMs(e);
63961
64457
  if (rateLimitResetMs !== undefined) {
63962
64458
  const resetInfo = rateLimitResetMs > 0 ? formatResetTime(rateLimitResetMs) : "Try again later";
63963
64459
  return `You've hit your usage limit. ${resetInfo}. View usage: ${LETTA_USAGE_URL}`;
63964
64460
  }
63965
- if (isAgentLimitError(e)) {
64461
+ if (isAgentLimitError(e, reasons)) {
63966
64462
  const { billingTier } = getErrorContext();
63967
64463
  if (billingTier?.toLowerCase() === "free") {
63968
64464
  return `You've reached the agent limit (3) for the Free Plan. Delete agents at: ${LETTA_AGENTS_URL}
@@ -63971,6 +64467,12 @@ Or upgrade to Pro for unlimited agents at: ${LETTA_USAGE_URL}`;
63971
64467
  return `You've reached your agent limit. Delete agents at: ${LETTA_AGENTS_URL}
63972
64468
  Or check your plan at: ${LETTA_USAGE_URL}`;
63973
64469
  }
64470
+ if (hasErrorReason(e, "model-unknown", reasons)) {
64471
+ return `The selected model is not currently available for this account or provider. Run /model and press R to refresh available models, then choose an available model or connect a provider with /connect.`;
64472
+ }
64473
+ if (hasErrorReason(e, "context-window-size-not-supported", reasons)) {
64474
+ return `The selected context window is not supported for this model. Switch models with /model or pick a model with a larger context window.`;
64475
+ }
63974
64476
  const resourceLimitMsg = getResourceLimitMessage(e);
63975
64477
  if (resourceLimitMsg) {
63976
64478
  const match3 = resourceLimitMsg.match(/limit for (\w+)/);
@@ -63979,13 +64481,19 @@ Or check your plan at: ${LETTA_USAGE_URL}`;
63979
64481
  Upgrade at: ${LETTA_USAGE_URL}
63980
64482
  Delete ${resourceType} at: ${LETTA_AGENTS_URL}`;
63981
64483
  }
63982
- if (isCreditExhaustedError(e)) {
63983
- const { billingTier, modelDisplayName } = getErrorContext();
63984
- if (billingTier?.toLowerCase() === "free") {
63985
- const modelInfo = modelDisplayName ? ` (${modelDisplayName})` : "";
63986
- return `Selected hosted model${modelInfo} not available on Free plan. Switch to a free model with /model (glm-4.7 or minimax-m2.1), upgrade your account at ${LETTA_USAGE_URL}, or connect your own API keys with /connect.`;
63987
- }
63988
- return `Your account is out of credits. Redeem additional credits or configure auto-recharge on your account page: ${LETTA_USAGE_URL}`;
64484
+ if (isCreditExhaustedError(e, reasons)) {
64485
+ 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.`;
64486
+ }
64487
+ if (hasErrorReason(e, "premium-usage-exceeded", reasons) || hasErrorReason(e, "standard-usage-exceeded", reasons) || hasErrorReason(e, "basic-usage-exceeded", reasons)) {
64488
+ return `You've reached your hosted model usage limit. View your plan and usage at ${LETTA_USAGE_URL}, or connect your own provider keys with /connect.`;
64489
+ }
64490
+ if (hasErrorReason(e, "byok-not-available-on-free-tier", reasons)) {
64491
+ const { modelDisplayName } = getErrorContext();
64492
+ const modelInfo = modelDisplayName ? ` (${modelDisplayName})` : "";
64493
+ return `Selected BYOK model${modelInfo} is not available on the Free plan. Switch to a free hosted model with /model (glm-4.7 or minimax-m2.1), or upgrade at ${LETTA_USAGE_URL}.`;
64494
+ }
64495
+ if (hasErrorReason(e, "free-usage-exceeded", reasons)) {
64496
+ return `You've reached the Free plan hosted model usage limit. Switch to free hosted models with /model (glm-4.7 or minimax-m2.1), upgrade at ${LETTA_USAGE_URL}, or connect your own provider keys with /connect.`;
63989
64497
  }
63990
64498
  if (e.error && typeof e.error === "object" && "error" in e.error) {
63991
64499
  const errorData = e.error.error;
@@ -64041,10 +64549,18 @@ function createAgentLink(runId, agentId, conversationId) {
64041
64549
  const url = `https://app.letta.com/agents/${agentId}${conversationId ? `?conversation=${conversationId}` : ""}`;
64042
64550
  return `View agent: \x1B]8;;${url}\x1B\\${agentId}\x1B]8;;\x1B\\ (run: ${runId})`;
64043
64551
  }
64044
- var LETTA_USAGE_URL = "https://app.letta.com/settings/organization/usage", LETTA_AGENTS_URL = "https://app.letta.com/projects/default-project/agents";
64552
+ var LETTA_USAGE_URL = "https://app.letta.com/settings/organization/usage", LETTA_AGENTS_URL = "https://app.letta.com/projects/default-project/agents", ENCRYPTED_CONTENT_HINT;
64045
64553
  var init_errorFormatter = __esm(() => {
64046
64554
  init_error();
64047
64555
  init_errorContext();
64556
+ ENCRYPTED_CONTENT_HINT = [
64557
+ "",
64558
+ "This occurs when the conversation contains messages with encrypted",
64559
+ "reasoning from a different OpenAI authentication scope (e.g. switching",
64560
+ "between ChatGPT OAuth and an OpenAI API key).",
64561
+ "Use /clear to start a new conversation."
64562
+ ].join(`
64563
+ `);
64048
64564
  });
64049
64565
 
64050
64566
  // src/cli/helpers/streamProcessor.ts
@@ -64317,155 +64833,6 @@ var init_stream = __esm(async () => {
64317
64833
  ]);
64318
64834
  });
64319
64835
 
64320
- // src/tools/toolset.ts
64321
- var exports_toolset = {};
64322
- __export(exports_toolset, {
64323
- switchToolsetForModel: () => switchToolsetForModel,
64324
- reattachMemoryTool: () => reattachMemoryTool,
64325
- forceToolsetSwitch: () => forceToolsetSwitch,
64326
- ensureCorrectMemoryTool: () => ensureCorrectMemoryTool,
64327
- detachMemoryTools: () => detachMemoryTools
64328
- });
64329
- async function ensureCorrectMemoryTool(agentId, modelIdentifier, useMemoryPatch) {
64330
- const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
64331
- const client = await getClient2();
64332
- const shouldUsePatch = useMemoryPatch !== undefined ? useMemoryPatch : isOpenAIModel(resolvedModel);
64333
- try {
64334
- const agentWithTools = await client.agents.retrieve(agentId, {
64335
- include: ["agent.tools"]
64336
- });
64337
- const currentTools = agentWithTools.tools || [];
64338
- const mapByName = new Map(currentTools.map((t) => [t.name, t.id]));
64339
- const hasAnyMemoryTool = mapByName.has("memory") || mapByName.has("memory_apply_patch");
64340
- if (!hasAnyMemoryTool) {
64341
- return;
64342
- }
64343
- const desiredMemoryTool = shouldUsePatch ? "memory_apply_patch" : "memory";
64344
- const otherMemoryTool = desiredMemoryTool === "memory" ? "memory_apply_patch" : "memory";
64345
- let desiredId = mapByName.get(desiredMemoryTool);
64346
- if (!desiredId) {
64347
- const resp = await client.tools.list({ name: desiredMemoryTool });
64348
- desiredId = resp.items[0]?.id;
64349
- }
64350
- if (!desiredId) {
64351
- return;
64352
- }
64353
- const otherId = mapByName.get(otherMemoryTool);
64354
- if (mapByName.has(desiredMemoryTool) && !otherId) {
64355
- return;
64356
- }
64357
- const currentIds = currentTools.map((t) => t.id).filter((id) => typeof id === "string");
64358
- const newIds = new Set(currentIds);
64359
- if (otherId)
64360
- newIds.delete(otherId);
64361
- newIds.add(desiredId);
64362
- const updatedRules = (agentWithTools.tool_rules || []).map((r) => r.tool_name === otherMemoryTool ? { ...r, tool_name: desiredMemoryTool } : r);
64363
- await client.agents.update(agentId, {
64364
- tool_ids: Array.from(newIds),
64365
- tool_rules: updatedRules
64366
- });
64367
- } catch (err) {
64368
- console.warn(`Warning: Failed to sync memory tool: ${err instanceof Error ? err.message : String(err)}`);
64369
- }
64370
- }
64371
- async function detachMemoryTools(agentId) {
64372
- const client = await getClient2();
64373
- try {
64374
- const agentWithTools = await client.agents.retrieve(agentId, {
64375
- include: ["agent.tools"]
64376
- });
64377
- const currentTools = agentWithTools.tools || [];
64378
- let detachedAny = false;
64379
- for (const tool of currentTools) {
64380
- if (tool.name === "memory" || tool.name === "memory_apply_patch") {
64381
- if (tool.id) {
64382
- await client.agents.tools.detach(tool.id, { agent_id: agentId });
64383
- detachedAny = true;
64384
- }
64385
- }
64386
- }
64387
- return detachedAny;
64388
- } catch (err) {
64389
- console.warn(`Warning: Failed to detach memory tools: ${err instanceof Error ? err.message : String(err)}`);
64390
- return false;
64391
- }
64392
- }
64393
- async function reattachMemoryTool(agentId, modelIdentifier) {
64394
- const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
64395
- const client = await getClient2();
64396
- const shouldUsePatch = isOpenAIModel(resolvedModel);
64397
- try {
64398
- const agentWithTools = await client.agents.retrieve(agentId, {
64399
- include: ["agent.tools"]
64400
- });
64401
- const currentTools = agentWithTools.tools || [];
64402
- const mapByName = new Map(currentTools.map((t) => [t.name, t.id]));
64403
- const desiredMemoryTool = shouldUsePatch ? "memory_apply_patch" : "memory";
64404
- if (mapByName.has(desiredMemoryTool)) {
64405
- return;
64406
- }
64407
- const resp = await client.tools.list({ name: desiredMemoryTool });
64408
- const toolId = resp.items[0]?.id;
64409
- if (!toolId) {
64410
- console.warn(`Memory tool "${desiredMemoryTool}" not found on server`);
64411
- return;
64412
- }
64413
- await client.agents.tools.attach(toolId, { agent_id: agentId });
64414
- } catch (err) {
64415
- console.warn(`Warning: Failed to reattach memory tool: ${err instanceof Error ? err.message : String(err)}`);
64416
- }
64417
- }
64418
- async function forceToolsetSwitch(toolsetName, agentId) {
64419
- let modelForLoading;
64420
- if (toolsetName === "none") {
64421
- clearToolsWithLock();
64422
- return;
64423
- } else if (toolsetName === "codex") {
64424
- await loadSpecificTools([...CODEX_TOOLS]);
64425
- modelForLoading = "openai/gpt-4";
64426
- } else if (toolsetName === "codex_snake") {
64427
- await loadTools("openai/gpt-4");
64428
- modelForLoading = "openai/gpt-4";
64429
- } else if (toolsetName === "gemini") {
64430
- await loadSpecificTools([...GEMINI_TOOLS]);
64431
- modelForLoading = "google_ai/gemini-3-pro-preview";
64432
- } else if (toolsetName === "gemini_snake") {
64433
- await loadTools("google_ai/gemini-3-pro-preview");
64434
- modelForLoading = "google_ai/gemini-3-pro-preview";
64435
- } else {
64436
- await loadTools("anthropic/claude-sonnet-4");
64437
- modelForLoading = "anthropic/claude-sonnet-4";
64438
- }
64439
- const useMemoryPatch = toolsetName === "codex" || toolsetName === "codex_snake";
64440
- await ensureCorrectMemoryTool(agentId, modelForLoading, useMemoryPatch);
64441
- }
64442
- async function switchToolsetForModel(modelIdentifier, agentId) {
64443
- const resolvedModel = resolveModel(modelIdentifier) ?? modelIdentifier;
64444
- await loadTools(resolvedModel);
64445
- const loadedAfterPrimary = getToolNames().length;
64446
- if (loadedAfterPrimary === 0 && !toolFilter.isActive()) {
64447
- await loadTools();
64448
- if (getToolNames().length === 0) {
64449
- throw new Error(`Failed to load any Letta tools for model "${resolvedModel}".`);
64450
- }
64451
- }
64452
- await ensureCorrectMemoryTool(agentId, resolvedModel);
64453
- const { isGeminiModel: isGeminiModel3 } = await init_manager3().then(() => exports_manager2);
64454
- const toolsetName = isOpenAIModel(resolvedModel) ? "codex" : isGeminiModel3(resolvedModel) ? "gemini" : "default";
64455
- return toolsetName;
64456
- }
64457
- var CODEX_TOOLS, GEMINI_TOOLS;
64458
- var init_toolset = __esm(async () => {
64459
- init_model();
64460
- init_filter();
64461
- await __promiseAll([
64462
- init_client2(),
64463
- init_manager3()
64464
- ]);
64465
- CODEX_TOOLS = OPENAI_PASCAL_TOOLS;
64466
- GEMINI_TOOLS = GEMINI_PASCAL_TOOLS;
64467
- });
64468
-
64469
64836
  // src/agent/github-utils.ts
64470
64837
  var exports_github_utils = {};
64471
64838
  __export(exports_github_utils, {
@@ -64501,6 +64868,7 @@ function parseDirNames(entries) {
64501
64868
  // src/agent/import.ts
64502
64869
  var exports_import = {};
64503
64870
  __export(exports_import, {
64871
+ importAgentFromRegistry: () => importAgentFromRegistry,
64504
64872
  importAgentFromFile: () => importAgentFromFile,
64505
64873
  extractSkillsFromAf: () => extractSkillsFromAf
64506
64874
  });
@@ -64608,6 +64976,45 @@ async function downloadGitHubDirectory(entries, destDir, owner, repo, branch, ba
64608
64976
  }
64609
64977
  }
64610
64978
  }
64979
+ function parseRegistryHandle(handle) {
64980
+ const normalized = handle.startsWith("@") ? handle.slice(1) : handle;
64981
+ const parts = normalized.split("/");
64982
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
64983
+ throw new Error(`Invalid import handle "${handle}". Use format: @author/agentname`);
64984
+ }
64985
+ return { author: parts[0], name: parts[1] };
64986
+ }
64987
+ async function importAgentFromRegistry(options) {
64988
+ const { tmpdir: tmpdir2 } = await import("node:os");
64989
+ const { join: join20 } = await import("node:path");
64990
+ const { writeFile: writeFile5, unlink: unlink2 } = await import("node:fs/promises");
64991
+ const { author, name } = parseRegistryHandle(options.handle);
64992
+ const rawUrl = `https://raw.githubusercontent.com/${AGENT_REGISTRY_OWNER}/${AGENT_REGISTRY_REPO}/refs/heads/${AGENT_REGISTRY_BRANCH}/agents/@${author}/${name}/${name}.af`;
64993
+ const response = await fetch(rawUrl);
64994
+ if (!response.ok) {
64995
+ if (response.status === 404) {
64996
+ throw new Error(`Agent @${author}/${name} not found in registry. Check that the agent exists at https://github.com/${AGENT_REGISTRY_OWNER}/${AGENT_REGISTRY_REPO}/tree/${AGENT_REGISTRY_BRANCH}/agents/@${author}/${name}`);
64997
+ }
64998
+ throw new Error(`Failed to download agent @${author}/${name}: ${response.statusText}`);
64999
+ }
65000
+ const afContent = await response.text();
65001
+ const tempPath = join20(tmpdir2(), `letta-import-${author}-${name}-${Date.now()}.af`);
65002
+ await writeFile5(tempPath, afContent, "utf-8");
65003
+ try {
65004
+ const result = await importAgentFromFile({
65005
+ filePath: tempPath,
65006
+ modelOverride: options.modelOverride,
65007
+ stripMessages: options.stripMessages ?? true,
65008
+ stripSkills: options.stripSkills ?? false
65009
+ });
65010
+ return result;
65011
+ } finally {
65012
+ try {
65013
+ await unlink2(tempPath);
65014
+ } catch {}
65015
+ }
65016
+ }
65017
+ var AGENT_REGISTRY_OWNER = "letta-ai", AGENT_REGISTRY_REPO = "agent-file", AGENT_REGISTRY_BRANCH = "main";
64611
65018
  var init_import = __esm(async () => {
64612
65019
  init_model();
64613
65020
  await __promiseAll([
@@ -65059,6 +65466,7 @@ In headless mode, use:
65059
65466
  process.exit(1);
65060
65467
  }
65061
65468
  }
65469
+ let isRegistryImport = false;
65062
65470
  if (fromAfFile) {
65063
65471
  if (specifiedAgentId) {
65064
65472
  console.error("Error: --from-af cannot be used with --agent");
@@ -65072,6 +65480,15 @@ In headless mode, use:
65072
65480
  console.error("Error: --from-af cannot be used with --new");
65073
65481
  process.exit(1);
65074
65482
  }
65483
+ if (fromAfFile.startsWith("@")) {
65484
+ isRegistryImport = true;
65485
+ const normalized = fromAfFile.slice(1);
65486
+ const parts = normalized.split("/");
65487
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
65488
+ console.error(`Error: Invalid registry handle "${fromAfFile}". Use format: @author/agentname`);
65489
+ process.exit(1);
65490
+ }
65491
+ }
65075
65492
  }
65076
65493
  if (initBlocksRaw && !forceNew) {
65077
65494
  console.error("Error: --init-blocks can only be used together with --new to control initial memory blocks.");
@@ -65154,13 +65571,24 @@ In headless mode, use:
65154
65571
  }
65155
65572
  }
65156
65573
  if (!agent && fromAfFile) {
65157
- const { importAgentFromFile: importAgentFromFile2 } = await init_import().then(() => exports_import);
65158
- const result = await importAgentFromFile2({
65159
- filePath: fromAfFile,
65160
- modelOverride: model,
65161
- stripMessages: true,
65162
- stripSkills: false
65163
- });
65574
+ let result;
65575
+ if (isRegistryImport) {
65576
+ const { importAgentFromRegistry: importAgentFromRegistry2 } = await init_import().then(() => exports_import);
65577
+ result = await importAgentFromRegistry2({
65578
+ handle: fromAfFile,
65579
+ modelOverride: model,
65580
+ stripMessages: true,
65581
+ stripSkills: false
65582
+ });
65583
+ } else {
65584
+ const { importAgentFromFile: importAgentFromFile2 } = await init_import().then(() => exports_import);
65585
+ result = await importAgentFromFile2({
65586
+ filePath: fromAfFile,
65587
+ modelOverride: model,
65588
+ stripMessages: true,
65589
+ stripSkills: false
65590
+ });
65591
+ }
65164
65592
  agent = result.agent;
65165
65593
  isNewlyCreatedAgent = true;
65166
65594
  if (result.skills && result.skills.length > 0) {
@@ -65363,8 +65791,11 @@ In headless mode, use:
65363
65791
  console.error(`Error: Invalid input format "${inputFormat}". Valid formats: stream-json`);
65364
65792
  process.exit(1);
65365
65793
  }
65794
+ const { getClientToolsFromRegistry: getClientToolsFromRegistry2 } = await init_manager3().then(() => exports_manager2);
65795
+ const loadedToolNames = getClientToolsFromRegistry2().map((t) => t.name);
65796
+ const availableTools = loadedToolNames.length > 0 ? loadedToolNames : agent.tools?.map((t) => t.name).filter((n) => !!n) || [];
65366
65797
  if (isBidirectionalMode) {
65367
- await runBidirectionalMode(agent, conversationId, client, outputFormat, includePartialMessages);
65798
+ await runBidirectionalMode(agent, conversationId, client, outputFormat, includePartialMessages, availableTools);
65368
65799
  return;
65369
65800
  }
65370
65801
  const buffers = createBuffers(agent.id);
@@ -65378,7 +65809,7 @@ In headless mode, use:
65378
65809
  agent_id: agent.id,
65379
65810
  conversation_id: conversationId,
65380
65811
  model: agent.llm_config?.model ?? "",
65381
- tools: agent.tools?.map((t) => t.name).filter((n) => !!n) || [],
65812
+ tools: availableTools,
65382
65813
  cwd: process.cwd(),
65383
65814
  mcp_servers: [],
65384
65815
  permission_mode: "",
@@ -65404,6 +65835,7 @@ In headless mode, use:
65404
65835
  if (pendingApprovals.length === 0)
65405
65836
  break;
65406
65837
  const { autoAllowed, autoDenied } = await classifyApprovals(pendingApprovals, {
65838
+ alwaysRequiresUserInput: isInteractiveApprovalTool,
65407
65839
  treatAskAsDeny: true,
65408
65840
  denyReasonForAsk: "Tool requires approval (headless mode)",
65409
65841
  requireArgsForAutoApprove: true,
@@ -65583,7 +66015,24 @@ ${SYSTEM_REMINDER_CLOSE}
65583
66015
  if (!errorDetail && preStreamError instanceof Error) {
65584
66016
  errorDetail = preStreamError.message;
65585
66017
  }
65586
- if (isConversationBusyError(errorDetail) && conversationBusyRetries < CONVERSATION_BUSY_MAX_RETRIES) {
66018
+ const preStreamAction = getPreStreamErrorAction(errorDetail, conversationBusyRetries, CONVERSATION_BUSY_MAX_RETRIES);
66019
+ if (preStreamAction === "resolve_approval_pending") {
66020
+ if (outputFormat === "stream-json") {
66021
+ const recoveryMsg = {
66022
+ type: "recovery",
66023
+ recovery_type: "approval_pending",
66024
+ message: "Detected pending approval conflict on send; resolving before retry",
66025
+ session_id: sessionId,
66026
+ uuid: `recovery-pre-stream-${crypto.randomUUID()}`
66027
+ };
66028
+ console.log(JSON.stringify(recoveryMsg));
66029
+ } else {
66030
+ console.error("Pending approval detected, resolving before retry...");
66031
+ }
66032
+ await resolveAllPendingApprovals();
66033
+ continue;
66034
+ }
66035
+ if (preStreamAction === "retry_conversation_busy") {
65587
66036
  conversationBusyRetries += 1;
65588
66037
  if (outputFormat === "stream-json") {
65589
66038
  const retryMsg = {
@@ -65656,6 +66105,7 @@ ${SYSTEM_REMINDER_CLOSE}
65656
66105
  }
65657
66106
  if (updatedApproval && !autoApprovalEmitted.has(updatedApproval.toolCallId)) {
65658
66107
  const { autoAllowed } = await classifyApprovals([updatedApproval], {
66108
+ alwaysRequiresUserInput: isInteractiveApprovalTool,
65659
66109
  requireArgsForAutoApprove: true,
65660
66110
  missingNameReason: "Tool call incomplete - missing name"
65661
66111
  });
@@ -65734,9 +66184,8 @@ ${SYSTEM_REMINDER_CLOSE}
65734
66184
  console.error("Unexpected empty approvals array");
65735
66185
  process.exit(1);
65736
66186
  }
65737
- const { autoAllowed, autoDenied } = await classifyApprovals(approvals, {
65738
- treatAskAsDeny: true,
65739
- denyReasonForAsk: "Tool requires approval (headless mode)",
66187
+ const { autoAllowed, autoDenied, needsUserInput } = await classifyApprovals(approvals, {
66188
+ alwaysRequiresUserInput: isInteractiveApprovalTool,
65740
66189
  requireArgsForAutoApprove: true,
65741
66190
  missingNameReason: "Tool call incomplete - missing name"
65742
66191
  });
@@ -65745,6 +66194,19 @@ ${SYSTEM_REMINDER_CLOSE}
65745
66194
  type: "approve",
65746
66195
  approval: ac.approval
65747
66196
  })),
66197
+ ...needsUserInput.map((ac) => {
66198
+ if (isHeadlessAutoAllowTool(ac.approval.toolName)) {
66199
+ return {
66200
+ type: "approve",
66201
+ approval: ac.approval
66202
+ };
66203
+ }
66204
+ return {
66205
+ type: "deny",
66206
+ approval: ac.approval,
66207
+ reason: "Tool requires approval (headless mode)"
66208
+ };
66209
+ }),
65748
66210
  ...autoDenied.map((ac) => {
65749
66211
  const fallback = "matchedRule" in ac.permission && ac.permission.matchedRule ? `Permission denied: ${ac.permission.matchedRule}` : ac.permission.reason ? `Permission denied: ${ac.permission.reason}` : "Permission denied: Unknown reason";
65750
66212
  return {
@@ -66007,7 +66469,7 @@ ${SYSTEM_REMINDER_CLOSE}
66007
66469
  markMilestone("HEADLESS_COMPLETE");
66008
66470
  reportAllMilestones();
66009
66471
  }
66010
- async function runBidirectionalMode(agent, conversationId, _client, _outputFormat, includePartialMessages) {
66472
+ async function runBidirectionalMode(agent, conversationId, client, _outputFormat, includePartialMessages, availableTools) {
66011
66473
  const sessionId = agent.id;
66012
66474
  const readline = await import("node:readline");
66013
66475
  const initEvent = {
@@ -66017,12 +66479,74 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
66017
66479
  agent_id: agent.id,
66018
66480
  conversation_id: conversationId,
66019
66481
  model: agent.llm_config?.model,
66020
- tools: agent.tools?.map((t) => t.name) || [],
66482
+ tools: availableTools,
66021
66483
  cwd: process.cwd(),
66022
66484
  uuid: `init-${agent.id}`
66023
66485
  };
66024
66486
  console.log(JSON.stringify(initEvent));
66025
66487
  let currentAbortController = null;
66488
+ const resolveAllPendingApprovals = async () => {
66489
+ const { getResumeData: getResumeData3 } = await Promise.resolve().then(() => (init_check_approval(), exports_check_approval));
66490
+ while (true) {
66491
+ const freshAgent = await client.agents.retrieve(agent.id);
66492
+ let resume;
66493
+ try {
66494
+ resume = await getResumeData3(client, freshAgent, conversationId);
66495
+ } catch (error) {
66496
+ if (error instanceof APIError2 && (error.status === 404 || error.status === 422)) {
66497
+ break;
66498
+ }
66499
+ throw error;
66500
+ }
66501
+ const pendingApprovals = resume.pendingApprovals || [];
66502
+ if (pendingApprovals.length === 0)
66503
+ break;
66504
+ const { autoAllowed, autoDenied } = await classifyApprovals(pendingApprovals, {
66505
+ treatAskAsDeny: true,
66506
+ denyReasonForAsk: "Tool requires approval (headless mode)",
66507
+ requireArgsForAutoApprove: true,
66508
+ missingNameReason: "Tool call incomplete - missing name"
66509
+ });
66510
+ const decisions = [
66511
+ ...autoAllowed.map((ac) => ({
66512
+ type: "approve",
66513
+ approval: ac.approval,
66514
+ reason: ac.permission.reason || "Allowed by permission rule",
66515
+ matchedRule: "matchedRule" in ac.permission && ac.permission.matchedRule ? ac.permission.matchedRule : "auto-approved"
66516
+ })),
66517
+ ...autoDenied.map((ac) => {
66518
+ const fallback = "matchedRule" in ac.permission && ac.permission.matchedRule ? `Permission denied: ${ac.permission.matchedRule}` : ac.permission.reason ? `Permission denied: ${ac.permission.reason}` : "Permission denied: Unknown reason";
66519
+ return {
66520
+ type: "deny",
66521
+ approval: ac.approval,
66522
+ reason: ac.denyReason ?? fallback
66523
+ };
66524
+ })
66525
+ ];
66526
+ const { executeApprovalBatch: executeApprovalBatch2 } = await init_approval_execution().then(() => exports_approval_execution);
66527
+ const executedResults = await executeApprovalBatch2(decisions);
66528
+ const approvalInput = {
66529
+ type: "approval",
66530
+ approvals: executedResults
66531
+ };
66532
+ const approvalMessages = [approvalInput];
66533
+ {
66534
+ const { consumeQueuedSkillContent: consumeQueuedSkillContent2 } = await Promise.resolve().then(() => (init_skillContentRegistry(), exports_skillContentRegistry));
66535
+ const skillContents = consumeQueuedSkillContent2();
66536
+ if (skillContents.length > 0) {
66537
+ approvalMessages.push({
66538
+ role: "user",
66539
+ content: skillContents.map((sc) => ({
66540
+ type: "text",
66541
+ text: sc.content
66542
+ }))
66543
+ });
66544
+ }
66545
+ }
66546
+ const approvalStream = await sendMessageStream(conversationId, approvalMessages, { agentId: agent.id });
66547
+ await drainStreamWithResume(approvalStream, createBuffers(agent.id), () => {});
66548
+ }
66549
+ };
66026
66550
  const rl = readline.createInterface({
66027
66551
  input: process.stdin,
66028
66552
  terminal: false
@@ -66142,7 +66666,7 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
66142
66666
  response: {
66143
66667
  agent_id: agent.id,
66144
66668
  model: agent.llm_config?.model,
66145
- tools: agent.tools?.map((t) => t.name) || []
66669
+ tools: availableTools
66146
66670
  }
66147
66671
  },
66148
66672
  session_id: sessionId,
@@ -66164,6 +66688,54 @@ async function runBidirectionalMode(agent, conversationId, _client, _outputForma
66164
66688
  uuid: crypto.randomUUID()
66165
66689
  };
66166
66690
  console.log(JSON.stringify(interruptResponse));
66691
+ } else if (subtype === "register_external_tools") {
66692
+ const toolsRequest = message.request;
66693
+ const tools = toolsRequest.tools ?? [];
66694
+ registerExternalTools(tools);
66695
+ setExternalToolExecutor(async (toolCallId, toolName, input) => {
66696
+ const execRequest = {
66697
+ type: "control_request",
66698
+ request_id: `ext-${toolCallId}`,
66699
+ request: {
66700
+ subtype: "execute_external_tool",
66701
+ tool_call_id: toolCallId,
66702
+ tool_name: toolName,
66703
+ input
66704
+ }
66705
+ };
66706
+ console.log(JSON.stringify(execRequest));
66707
+ while (true) {
66708
+ const line2 = await getNextLine();
66709
+ if (line2 === null) {
66710
+ return {
66711
+ content: [{ type: "text", text: "stdin closed" }],
66712
+ isError: true
66713
+ };
66714
+ }
66715
+ if (!line2.trim())
66716
+ continue;
66717
+ try {
66718
+ const msg = JSON.parse(line2);
66719
+ if (msg.type === "control_response" && msg.response?.subtype === "external_tool_result" && msg.response?.tool_call_id === toolCallId) {
66720
+ return {
66721
+ content: msg.response.content ?? [{ type: "text", text: "" }],
66722
+ isError: msg.response.is_error ?? false
66723
+ };
66724
+ }
66725
+ } catch {}
66726
+ }
66727
+ });
66728
+ const registerResponse = {
66729
+ type: "control_response",
66730
+ response: {
66731
+ subtype: "success",
66732
+ request_id: requestId ?? "",
66733
+ response: { registered: tools.length }
66734
+ },
66735
+ session_id: sessionId,
66736
+ uuid: crypto.randomUUID()
66737
+ };
66738
+ console.log(JSON.stringify(registerResponse));
66167
66739
  } else {
66168
66740
  const errorResponse = {
66169
66741
  type: "control_response",
@@ -66232,9 +66804,40 @@ ${enrichedContent}`;
66232
66804
  ];
66233
66805
  }
66234
66806
  }
66235
- const stream2 = await sendMessageStream(conversationId, currentInput, {
66236
- agentId: agent.id
66237
- });
66807
+ let stream2;
66808
+ try {
66809
+ stream2 = await sendMessageStream(conversationId, currentInput, {
66810
+ agentId: agent.id
66811
+ });
66812
+ } catch (preStreamError) {
66813
+ let errorDetail = "";
66814
+ if (preStreamError instanceof APIError2 && preStreamError.error && typeof preStreamError.error === "object") {
66815
+ const errObj = preStreamError.error;
66816
+ if (errObj.error && typeof errObj.error === "object" && "detail" in errObj.error) {
66817
+ const nested = errObj.error;
66818
+ errorDetail = typeof nested.detail === "string" ? nested.detail : "";
66819
+ }
66820
+ if (!errorDetail && typeof errObj.detail === "string") {
66821
+ errorDetail = errObj.detail;
66822
+ }
66823
+ }
66824
+ if (!errorDetail && preStreamError instanceof Error) {
66825
+ errorDetail = preStreamError.message;
66826
+ }
66827
+ if (isApprovalPendingError(errorDetail)) {
66828
+ const recoveryMsg = {
66829
+ type: "recovery",
66830
+ recovery_type: "approval_pending",
66831
+ message: "Detected pending approval conflict on send; resolving before retry",
66832
+ session_id: sessionId,
66833
+ uuid: `recovery-bidir-${crypto.randomUUID()}`
66834
+ };
66835
+ console.log(JSON.stringify(recoveryMsg));
66836
+ await resolveAllPendingApprovals();
66837
+ continue;
66838
+ }
66839
+ throw preStreamError;
66840
+ }
66238
66841
  const streamJsonHook = ({
66239
66842
  chunk,
66240
66843
  shouldOutput,
@@ -66302,6 +66905,7 @@ ${enrichedContent}`;
66302
66905
  break;
66303
66906
  }
66304
66907
  const { autoAllowed, autoDenied, needsUserInput } = await classifyApprovals(approvals, {
66908
+ alwaysRequiresUserInput: isInteractiveApprovalTool,
66305
66909
  requireArgsForAutoApprove: true,
66306
66910
  missingNameReason: "Tool call incomplete - missing name"
66307
66911
  });
@@ -66459,6 +67063,7 @@ var init_headless = __esm(async () => {
66459
67063
  init_model();
66460
67064
  init_errorFormatter();
66461
67065
  init_constants();
67066
+ init_interactivePolicy();
66462
67067
  init_timing();
66463
67068
  await __promiseAll([
66464
67069
  init_approval_recovery(),
@@ -66469,7 +67074,8 @@ var init_headless = __esm(async () => {
66469
67074
  init_accumulator(),
66470
67075
  init_approvalClassification(),
66471
67076
  init_stream(),
66472
- init_settings_manager()
67077
+ init_settings_manager(),
67078
+ init_manager3()
66473
67079
  ]);
66474
67080
  });
66475
67081
 
@@ -66720,10 +67326,10 @@ __export(exports_settings, {
66720
67326
  loadProjectSettings: () => loadProjectSettings,
66721
67327
  getSetting: () => getSetting
66722
67328
  });
66723
- import { homedir as homedir12 } from "node:os";
67329
+ import { homedir as homedir13 } from "node:os";
66724
67330
  import { join as join20 } from "node:path";
66725
67331
  function getSettingsPath() {
66726
- return join20(homedir12(), ".letta", "settings.json");
67332
+ return join20(homedir13(), ".letta", "settings.json");
66727
67333
  }
66728
67334
  async function loadSettings() {
66729
67335
  const settingsPath = getSettingsPath();
@@ -68371,7 +68977,9 @@ function formatArgsDisplay(argsJson, toolName) {
68371
68977
  return { display, parsed };
68372
68978
  }
68373
68979
  var isRecord3 = (v) => typeof v === "object" && v !== null;
68374
- var init_formatArgsDisplay = () => {};
68980
+ var init_formatArgsDisplay = __esm(async () => {
68981
+ await init_toolNameMapping();
68982
+ });
68375
68983
 
68376
68984
  // node_modules/diff/libesm/diff/base.js
68377
68985
  class Diff {
@@ -70249,11 +70857,11 @@ function getFileEditHeader(toolName, toolArgs) {
70249
70857
  }
70250
70858
  var import_react33, jsx_dev_runtime14, SOLID_LINE5 = "─", DOTTED_LINE2 = "╌", ApprovalPreview;
70251
70859
  var init_ApprovalPreview = __esm(async () => {
70252
- init_formatArgsDisplay();
70253
70860
  init_useTerminalWidth();
70254
70861
  init_colors();
70255
70862
  await __promiseAll([
70256
70863
  init_build2(),
70864
+ init_formatArgsDisplay(),
70257
70865
  init_AdvancedDiffRenderer(),
70258
70866
  init_BashPreview(),
70259
70867
  init_PlanPreview(),
@@ -70962,13 +71570,13 @@ function getDiffKind(toolName) {
70962
71570
  var import_react38, jsx_dev_runtime17, SOLID_LINE8 = "─", DOTTED_LINE3 = "╌", InlineFileEditApproval;
70963
71571
  var init_InlineFileEditApproval = __esm(async () => {
70964
71572
  init_diff2();
70965
- init_formatArgsDisplay();
70966
71573
  init_useProgressIndicator();
70967
71574
  init_useTerminalWidth();
70968
71575
  init_useTextInputCursor();
70969
71576
  init_colors();
70970
71577
  await __promiseAll([
70971
71578
  init_build2(),
71579
+ init_formatArgsDisplay(),
70972
71580
  init_AdvancedDiffRenderer(),
70973
71581
  init_Text2()
70974
71582
  ]);
@@ -72543,6 +73151,7 @@ function getQuestions(approval) {
72543
73151
  var import_react43, jsx_dev_runtime22, ApprovalSwitch;
72544
73152
  var init_ApprovalSwitch = __esm(async () => {
72545
73153
  await __promiseAll([
73154
+ init_toolNameMapping(),
72546
73155
  init_InlineBashApproval(),
72547
73156
  init_InlineEnterPlanModeApproval(),
72548
73157
  init_InlineFileEditApproval(),
@@ -72717,8 +73326,9 @@ function AnimationProvider({
72717
73326
  children,
72718
73327
  shouldAnimate
72719
73328
  }) {
73329
+ const contextValue = import_react45.useMemo(() => ({ shouldAnimate }), [shouldAnimate]);
72720
73330
  return /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(AnimationContext.Provider, {
72721
- value: { shouldAnimate },
73331
+ value: contextValue,
72722
73332
  children
72723
73333
  }, undefined, false, undefined, this);
72724
73334
  }
@@ -72774,11 +73384,18 @@ var init_CollapsedOutputDisplay = __esm(async () => {
72774
73384
  jsx_dev_runtime26 = __toESM(require_jsx_dev_runtime(), 1);
72775
73385
  CollapsedOutputDisplay = import_react47.memo(({
72776
73386
  output,
72777
- maxLines = DEFAULT_COLLAPSED_LINES
73387
+ maxLines = DEFAULT_COLLAPSED_LINES,
73388
+ maxChars
72778
73389
  }) => {
72779
73390
  const columns = useTerminalWidth();
72780
73391
  const contentWidth = Math.max(0, columns - PREFIX_WIDTH);
72781
- const lines = output.split(`
73392
+ let displayOutput = output;
73393
+ let clippedByChars = false;
73394
+ if (typeof maxChars === "number" && maxChars > 0 && output.length > maxChars) {
73395
+ displayOutput = `${output.slice(0, maxChars)}…`;
73396
+ clippedByChars = true;
73397
+ }
73398
+ const lines = displayOutput.split(`
72782
73399
  `);
72783
73400
  if (lines.length > 0 && lines[lines.length - 1] === "") {
72784
73401
  lines.pop();
@@ -72853,6 +73470,26 @@ var init_CollapsedOutputDisplay = __esm(async () => {
72853
73470
  }, undefined, true, undefined, this)
72854
73471
  }, undefined, false, undefined, this)
72855
73472
  ]
73473
+ }, undefined, true, undefined, this),
73474
+ clippedByChars && /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
73475
+ flexDirection: "row",
73476
+ children: [
73477
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
73478
+ width: PREFIX_WIDTH,
73479
+ flexShrink: 0,
73480
+ children: /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text2, {
73481
+ children: " "
73482
+ }, undefined, false, undefined, this)
73483
+ }, undefined, false, undefined, this),
73484
+ /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Box_default, {
73485
+ flexGrow: 1,
73486
+ width: contentWidth,
73487
+ children: /* @__PURE__ */ jsx_dev_runtime26.jsxDEV(Text2, {
73488
+ dimColor: true,
73489
+ children: "… output clipped"
73490
+ }, undefined, false, undefined, this)
73491
+ }, undefined, false, undefined, this)
73492
+ ]
72856
73493
  }, undefined, true, undefined, this)
72857
73494
  ]
72858
73495
  }, undefined, true, undefined, this);
@@ -74896,7 +75533,7 @@ import {
74896
75533
  readFileSync as readFileSync6,
74897
75534
  writeFileSync as writeFileSync5
74898
75535
  } from "node:fs";
74899
- import { homedir as homedir13, platform as platform3 } from "node:os";
75536
+ import { homedir as homedir14, platform as platform3 } from "node:os";
74900
75537
  import { dirname as dirname12, join as join22 } from "node:path";
74901
75538
  function detectTerminalType() {
74902
75539
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
@@ -74929,7 +75566,7 @@ function getKeybindingsPath(terminal) {
74929
75566
  }[terminal];
74930
75567
  const os5 = platform3();
74931
75568
  if (os5 === "darwin") {
74932
- return join22(homedir13(), "Library", "Application Support", appName, "User", "keybindings.json");
75569
+ return join22(homedir14(), "Library", "Application Support", appName, "User", "keybindings.json");
74933
75570
  }
74934
75571
  if (os5 === "win32") {
74935
75572
  const appData = process.env.APPDATA;
@@ -74938,7 +75575,7 @@ function getKeybindingsPath(terminal) {
74938
75575
  return join22(appData, appName, "User", "keybindings.json");
74939
75576
  }
74940
75577
  if (os5 === "linux") {
74941
- return join22(homedir13(), ".config", appName, "User", "keybindings.json");
75578
+ return join22(homedir14(), ".config", appName, "User", "keybindings.json");
74942
75579
  }
74943
75580
  return null;
74944
75581
  }
@@ -75095,10 +75732,10 @@ function getWezTermConfigPath() {
75095
75732
  if (existsSync13(xdgPath))
75096
75733
  return xdgPath;
75097
75734
  }
75098
- const configPath = join22(homedir13(), ".config", "wezterm", "wezterm.lua");
75735
+ const configPath = join22(homedir14(), ".config", "wezterm", "wezterm.lua");
75099
75736
  if (existsSync13(configPath))
75100
75737
  return configPath;
75101
- return join22(homedir13(), ".wezterm.lua");
75738
+ return join22(homedir14(), ".wezterm.lua");
75102
75739
  }
75103
75740
  function wezTermDeleteFixExists(configPath) {
75104
75741
  if (!existsSync13(configPath))
@@ -80501,9 +81138,15 @@ function SlashCommandAutocomplete({
80501
81138
  manageActiveState: false
80502
81139
  });
80503
81140
  import_react64.useLayoutEffect(() => {
80504
- const isActive = matches.length > 0 || showNoMatches;
81141
+ const queryLength = queryInfo?.query.length ?? 0;
81142
+ const isActive = !hideAutocomplete && (matches.length > 0 || queryLength > 0);
80505
81143
  onActiveChange?.(isActive);
80506
- }, [matches.length, showNoMatches, onActiveChange]);
81144
+ }, [
81145
+ hideAutocomplete,
81146
+ matches.length,
81147
+ onActiveChange,
81148
+ queryInfo?.query.length
81149
+ ]);
80507
81150
  if (!currentInput.startsWith("/")) {
80508
81151
  return null;
80509
81152
  }
@@ -80725,7 +81368,8 @@ var init_ShimmerText = __esm(async () => {
80725
81368
  color = colors.status.processing,
80726
81369
  boldPrefix,
80727
81370
  message,
80728
- shimmerOffset
81371
+ shimmerOffset,
81372
+ wrap
80729
81373
  }) {
80730
81374
  const fullText = `${boldPrefix ? `${boldPrefix} ` : ""}${message}…`;
80731
81375
  const prefixLength = boldPrefix ? boldPrefix.length + 1 : 0;
@@ -80740,6 +81384,7 @@ var init_ShimmerText = __esm(async () => {
80740
81384
  return isInPrefix ? source_default.bold(styledChar) : styledChar;
80741
81385
  }).join("");
80742
81386
  return /* @__PURE__ */ jsx_dev_runtime44.jsxDEV(Text2, {
81387
+ wrap,
80743
81388
  children: shimmerText
80744
81389
  }, undefined, false, undefined, this);
80745
81390
  });
@@ -80748,6 +81393,15 @@ var init_ShimmerText = __esm(async () => {
80748
81393
  // src/cli/components/InputRich.tsx
80749
81394
  import { EventEmitter as EventEmitter5 } from "node:events";
80750
81395
  import { stdin } from "node:process";
81396
+ function truncateEnd(value, maxChars) {
81397
+ if (maxChars <= 0)
81398
+ return "";
81399
+ if (value.length <= maxChars)
81400
+ return value;
81401
+ if (maxChars <= 3)
81402
+ return value.slice(0, maxChars);
81403
+ return `${value.slice(0, maxChars - 3)}...`;
81404
+ }
80751
81405
  function getVisualLines(text, lineWidth) {
80752
81406
  const lines = [];
80753
81407
  let lineStart = 0;
@@ -80813,7 +81467,9 @@ function Input({
80813
81467
  onPasteError,
80814
81468
  restoredInput,
80815
81469
  onRestoredInputConsumed,
80816
- networkPhase = null
81470
+ networkPhase = null,
81471
+ terminalWidth,
81472
+ shouldAnimate = true
80817
81473
  }) {
80818
81474
  const [value, setValue] = import_react68.useState("");
80819
81475
  const [escapePressed, setEscapePressed] = import_react68.useState(false);
@@ -80825,7 +81481,7 @@ function Input({
80825
81481
  const [isAutocompleteActive, setIsAutocompleteActive] = import_react68.useState(false);
80826
81482
  const [cursorPos, setCursorPos] = import_react68.useState(undefined);
80827
81483
  const [currentCursorPosition, setCurrentCursorPosition] = import_react68.useState(0);
80828
- const columns = useTerminalWidth();
81484
+ const columns = terminalWidth;
80829
81485
  const contentWidth = Math.max(0, columns - 2);
80830
81486
  const interactionEnabled = visible && inputEnabled;
80831
81487
  const reserveInputSpace = !collapseInputWhenDisabled;
@@ -80834,6 +81490,38 @@ function Input({
80834
81490
  return Math.max(1, getVisualLines(value, contentWidth).length);
80835
81491
  }, [value, contentWidth]);
80836
81492
  const inputChromeHeight = inputRowLines + 3;
81493
+ const computedFooterRightColumnWidth = import_react68.useMemo(() => Math.max(28, Math.min(72, Math.floor(columns * 0.45))), [columns]);
81494
+ const [footerRightColumnWidth, setFooterRightColumnWidth] = import_react68.useState(computedFooterRightColumnWidth);
81495
+ const debugFlicker = process.env.LETTA_DEBUG_FLICKER === "1";
81496
+ import_react68.useEffect(() => {
81497
+ if (!streaming) {
81498
+ setFooterRightColumnWidth(computedFooterRightColumnWidth);
81499
+ return;
81500
+ }
81501
+ if (computedFooterRightColumnWidth >= footerRightColumnWidth) {
81502
+ const growthDelta = computedFooterRightColumnWidth - footerRightColumnWidth;
81503
+ if (debugFlicker && growthDelta >= FOOTER_WIDTH_STREAMING_DELTA) {
81504
+ console.error(`[debug:flicker:footer-width] defer growth ${footerRightColumnWidth} -> ${computedFooterRightColumnWidth} (delta=${growthDelta})`);
81505
+ }
81506
+ return;
81507
+ }
81508
+ const shrinkDelta = footerRightColumnWidth - computedFooterRightColumnWidth;
81509
+ if (shrinkDelta < FOOTER_WIDTH_STREAMING_DELTA) {
81510
+ if (debugFlicker && shrinkDelta > 0) {
81511
+ console.error(`[debug:flicker:footer-width] ignore minor shrink ${footerRightColumnWidth} -> ${computedFooterRightColumnWidth} (delta=${shrinkDelta})`);
81512
+ }
81513
+ return;
81514
+ }
81515
+ if (debugFlicker) {
81516
+ console.error(`[debug:flicker:footer-width] shrink ${footerRightColumnWidth} -> ${computedFooterRightColumnWidth} (delta=${shrinkDelta})`);
81517
+ }
81518
+ setFooterRightColumnWidth(computedFooterRightColumnWidth);
81519
+ }, [
81520
+ streaming,
81521
+ computedFooterRightColumnWidth,
81522
+ footerRightColumnWidth,
81523
+ debugFlicker
81524
+ ]);
80837
81525
  const [history, setHistory] = import_react68.useState([]);
80838
81526
  const [historyIndex, setHistoryIndex] = import_react68.useState(-1);
80839
81527
  const [temporaryInput, setTemporaryInput] = import_react68.useState("");
@@ -80849,18 +81537,18 @@ function Input({
80849
81537
  onRestoredInputConsumed?.();
80850
81538
  }
80851
81539
  }, [restoredInput, value, onRestoredInputConsumed]);
80852
- const handleBangAtEmpty = () => {
81540
+ const handleBangAtEmpty = import_react68.useCallback(() => {
80853
81541
  if (isBashMode)
80854
81542
  return false;
80855
81543
  setIsBashMode(true);
80856
81544
  return true;
80857
- };
80858
- const handleBackspaceAtEmpty = () => {
81545
+ }, [isBashMode]);
81546
+ const handleBackspaceAtEmpty = import_react68.useCallback(() => {
80859
81547
  if (!isBashMode)
80860
81548
  return false;
80861
81549
  setIsBashMode(false);
80862
81550
  return true;
80863
- };
81551
+ }, [isBashMode]);
80864
81552
  import_react68.useEffect(() => {
80865
81553
  if (cursorPos !== undefined) {
80866
81554
  const timer = setTimeout(() => setCursorPos(undefined), 0);
@@ -80881,9 +81569,6 @@ function Input({
80881
81569
  setCurrentMode(externalMode);
80882
81570
  }
80883
81571
  }, [externalMode]);
80884
- const [shimmerOffset, setShimmerOffset] = import_react68.useState(-3);
80885
- const [elapsedMs, setElapsedMs] = import_react68.useState(0);
80886
- const streamStartRef = import_react68.useRef(null);
80887
81572
  import_react68.useEffect(() => {
80888
81573
  if (!interactionEnabled) {
80889
81574
  setIsAutocompleteActive(false);
@@ -81102,35 +81787,7 @@ function Input({
81102
81787
  clearTimeout(ctrlCTimerRef.current);
81103
81788
  };
81104
81789
  }, []);
81105
- import_react68.useEffect(() => {
81106
- if (!streaming || !visible)
81107
- return;
81108
- const id = setInterval(() => {
81109
- setShimmerOffset((prev) => {
81110
- const prefixLen = agentName ? agentName.length + 1 : 0;
81111
- const len = prefixLen + thinkingMessage.length;
81112
- const next = prev + 1;
81113
- return next > len + 3 ? -3 : next;
81114
- });
81115
- }, 120);
81116
- return () => clearInterval(id);
81117
- }, [streaming, thinkingMessage, visible, agentName]);
81118
- import_react68.useEffect(() => {
81119
- if (streaming && visible) {
81120
- if (streamStartRef.current === null) {
81121
- streamStartRef.current = performance.now();
81122
- }
81123
- const id = setInterval(() => {
81124
- if (streamStartRef.current !== null) {
81125
- setElapsedMs(performance.now() - streamStartRef.current);
81126
- }
81127
- }, 1000);
81128
- return () => clearInterval(id);
81129
- }
81130
- streamStartRef.current = null;
81131
- setElapsedMs(0);
81132
- }, [streaming, visible]);
81133
- const handleSubmit = async () => {
81790
+ const handleSubmit = import_react68.useCallback(async () => {
81134
81791
  if (isAutocompleteActive) {
81135
81792
  return;
81136
81793
  }
@@ -81140,9 +81797,11 @@ function Input({
81140
81797
  return;
81141
81798
  if (bashRunning)
81142
81799
  return;
81143
- if (previousValue.trim() !== history[history.length - 1]) {
81144
- setHistory([...history, previousValue]);
81145
- }
81800
+ setHistory((prev) => {
81801
+ if (previousValue.trim() === prev[prev.length - 1])
81802
+ return prev;
81803
+ return [...prev, previousValue];
81804
+ });
81146
81805
  setHistoryIndex(-1);
81147
81806
  setTemporaryInput("");
81148
81807
  setValue("");
@@ -81151,8 +81810,12 @@ function Input({
81151
81810
  }
81152
81811
  return;
81153
81812
  }
81154
- if (previousValue.trim() && previousValue !== history[history.length - 1]) {
81155
- setHistory([...history, previousValue]);
81813
+ if (previousValue.trim()) {
81814
+ setHistory((prev) => {
81815
+ if (previousValue === prev[prev.length - 1])
81816
+ return prev;
81817
+ return [...prev, previousValue];
81818
+ });
81156
81819
  }
81157
81820
  setHistoryIndex(-1);
81158
81821
  setTemporaryInput("");
@@ -81161,8 +81824,15 @@ function Input({
81161
81824
  if (!result.submitted) {
81162
81825
  setValue(previousValue);
81163
81826
  }
81164
- };
81165
- const handleFileSelect = (selectedPath) => {
81827
+ }, [
81828
+ isAutocompleteActive,
81829
+ value,
81830
+ isBashMode,
81831
+ bashRunning,
81832
+ onBashSubmit,
81833
+ onSubmit
81834
+ ]);
81835
+ const handleFileSelect = import_react68.useCallback((selectedPath) => {
81166
81836
  const atIndex = value.lastIndexOf("@");
81167
81837
  if (atIndex === -1)
81168
81838
  return;
@@ -81181,21 +81851,25 @@ function Input({
81181
81851
  }
81182
81852
  setValue(newValue);
81183
81853
  setCursorPos(newCursorPos);
81184
- };
81185
- const handleCommandSelect = async (selectedCommand) => {
81854
+ }, [value]);
81855
+ const handleCommandSelect = import_react68.useCallback(async (selectedCommand) => {
81186
81856
  const commandToSubmit = selectedCommand.trim();
81187
- if (commandToSubmit && commandToSubmit !== history[history.length - 1]) {
81188
- setHistory([...history, commandToSubmit]);
81857
+ if (commandToSubmit) {
81858
+ setHistory((prev) => {
81859
+ if (commandToSubmit === prev[prev.length - 1])
81860
+ return prev;
81861
+ return [...prev, commandToSubmit];
81862
+ });
81189
81863
  }
81190
81864
  setHistoryIndex(-1);
81191
81865
  setTemporaryInput("");
81192
81866
  setValue("");
81193
81867
  await onSubmit(commandToSubmit);
81194
- };
81195
- const handleCommandAutocomplete = (selectedCommand) => {
81868
+ }, [onSubmit]);
81869
+ const handleCommandAutocomplete = import_react68.useCallback((selectedCommand) => {
81196
81870
  setValue(selectedCommand);
81197
81871
  setCursorPos(selectedCommand.length);
81198
- };
81872
+ }, []);
81199
81873
  const modeInfo = import_react68.useMemo(() => {
81200
81874
  if (ralphPending) {
81201
81875
  if (ralphPendingYolo) {
@@ -81237,164 +81911,145 @@ function Input({
81237
81911
  return null;
81238
81912
  }
81239
81913
  }, [ralphPending, ralphPendingYolo, ralphActive, currentMode]);
81240
- const estimatedTokens = charsToTokens(tokenCount);
81241
- const totalElapsedMs = elapsedBaseMs + elapsedMs;
81242
- const shouldShowTokenCount = streaming && estimatedTokens > TOKEN_DISPLAY_THRESHOLD;
81243
- const shouldShowElapsed = streaming && totalElapsedMs > ELAPSED_DISPLAY_THRESHOLD_MS;
81244
- const elapsedLabel = formatElapsedLabel(totalElapsedMs);
81245
- const networkArrow = import_react68.useMemo(() => {
81246
- if (!networkPhase)
81247
- return "";
81248
- if (networkPhase === "upload")
81249
- return "↑";
81250
- if (networkPhase === "download")
81251
- return "↑";
81252
- return "↑̸";
81253
- }, [networkPhase]);
81254
- const showErrorArrow = networkArrow === "↑̸";
81255
- const statusHintText = import_react68.useMemo(() => {
81256
- const hintColor = source_default.hex(colors.subagent.hint);
81257
- const hintBold = hintColor.bold;
81258
- const parts = [];
81259
- if (shouldShowElapsed) {
81260
- parts.push(elapsedLabel);
81261
- }
81262
- if (shouldShowTokenCount) {
81263
- parts.push(`${formatCompact(estimatedTokens)}${networkArrow ? ` ${networkArrow}` : ""}`);
81264
- } else if (showErrorArrow) {
81265
- parts.push(networkArrow);
81266
- }
81267
- const suffix = `${parts.length > 0 ? ` · ${parts.join(" · ")}` : ""})`;
81268
- if (interruptRequested) {
81269
- return hintColor(` (interrupting${suffix}`);
81270
- }
81271
- return hintColor(" (") + hintBold("esc") + hintColor(` to interrupt${suffix}`);
81914
+ const horizontalLine = import_react68.useMemo(() => "─".repeat(columns), [columns]);
81915
+ const lowerPane = import_react68.useMemo(() => {
81916
+ return /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(jsx_dev_runtime45.Fragment, {
81917
+ children: [
81918
+ messageQueue && messageQueue.length > 0 && /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(QueuedMessages, {
81919
+ messages: messageQueue
81920
+ }, undefined, false, undefined, this),
81921
+ interactionEnabled ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81922
+ flexDirection: "column",
81923
+ children: [
81924
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81925
+ dimColor: !isBashMode,
81926
+ color: isBashMode ? colors.bash.border : undefined,
81927
+ children: horizontalLine
81928
+ }, undefined, false, undefined, this),
81929
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81930
+ flexDirection: "row",
81931
+ children: [
81932
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81933
+ width: 2,
81934
+ flexShrink: 0,
81935
+ children: [
81936
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81937
+ color: isBashMode ? colors.bash.prompt : colors.input.prompt,
81938
+ children: isBashMode ? "!" : ">"
81939
+ }, undefined, false, undefined, this),
81940
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81941
+ children: " "
81942
+ }, undefined, false, undefined, this)
81943
+ ]
81944
+ }, undefined, true, undefined, this),
81945
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81946
+ flexGrow: 1,
81947
+ width: contentWidth,
81948
+ children: /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(PasteAwareTextInput, {
81949
+ value,
81950
+ onChange: setValue,
81951
+ onSubmit: handleSubmit,
81952
+ cursorPosition: cursorPos,
81953
+ onCursorMove: setCurrentCursorPosition,
81954
+ focus: interactionEnabled && !onEscapeCancel,
81955
+ onBangAtEmpty: handleBangAtEmpty,
81956
+ onBackspaceAtEmpty: handleBackspaceAtEmpty,
81957
+ onPasteError
81958
+ }, undefined, false, undefined, this)
81959
+ }, undefined, false, undefined, this)
81960
+ ]
81961
+ }, undefined, true, undefined, this),
81962
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81963
+ dimColor: !isBashMode,
81964
+ color: isBashMode ? colors.bash.border : undefined,
81965
+ children: horizontalLine
81966
+ }, undefined, false, undefined, this),
81967
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(InputAssist, {
81968
+ currentInput: value,
81969
+ cursorPosition: currentCursorPosition,
81970
+ onFileSelect: handleFileSelect,
81971
+ onCommandSelect: handleCommandSelect,
81972
+ onCommandAutocomplete: handleCommandAutocomplete,
81973
+ onAutocompleteActiveChange: setIsAutocompleteActive,
81974
+ agentId,
81975
+ agentName,
81976
+ serverUrl,
81977
+ workingDirectory: process.cwd(),
81978
+ conversationId
81979
+ }, undefined, false, undefined, this),
81980
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(InputFooter, {
81981
+ ctrlCPressed,
81982
+ escapePressed,
81983
+ isBashMode,
81984
+ modeName: modeInfo?.name ?? null,
81985
+ modeColor: modeInfo?.color ?? null,
81986
+ showExitHint: ralphActive || ralphPending,
81987
+ agentName,
81988
+ currentModel,
81989
+ isOpenAICodexProvider: currentModelProvider === OPENAI_CODEX_PROVIDER_NAME,
81990
+ isByokProvider: currentModelProvider?.startsWith("lc-") || currentModelProvider === OPENAI_CODEX_PROVIDER_NAME,
81991
+ hideFooter,
81992
+ rightColumnWidth: footerRightColumnWidth
81993
+ }, undefined, false, undefined, this)
81994
+ ]
81995
+ }, undefined, true, undefined, this) : reserveInputSpace ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81996
+ height: inputChromeHeight
81997
+ }, undefined, false, undefined, this) : null
81998
+ ]
81999
+ }, undefined, true, undefined, this);
81272
82000
  }, [
81273
- shouldShowElapsed,
81274
- elapsedLabel,
81275
- shouldShowTokenCount,
81276
- estimatedTokens,
81277
- interruptRequested,
81278
- networkArrow,
81279
- showErrorArrow
82001
+ messageQueue,
82002
+ interactionEnabled,
82003
+ isBashMode,
82004
+ horizontalLine,
82005
+ contentWidth,
82006
+ value,
82007
+ handleSubmit,
82008
+ cursorPos,
82009
+ onEscapeCancel,
82010
+ handleBangAtEmpty,
82011
+ handleBackspaceAtEmpty,
82012
+ onPasteError,
82013
+ currentCursorPosition,
82014
+ handleFileSelect,
82015
+ handleCommandSelect,
82016
+ handleCommandAutocomplete,
82017
+ agentId,
82018
+ agentName,
82019
+ serverUrl,
82020
+ conversationId,
82021
+ ctrlCPressed,
82022
+ escapePressed,
82023
+ modeInfo?.name,
82024
+ modeInfo?.color,
82025
+ ralphActive,
82026
+ ralphPending,
82027
+ currentModel,
82028
+ currentModelProvider,
82029
+ hideFooter,
82030
+ footerRightColumnWidth,
82031
+ reserveInputSpace,
82032
+ inputChromeHeight
81280
82033
  ]);
81281
- const horizontalLine = import_react68.useMemo(() => "─".repeat(columns), [columns]);
81282
82034
  if (!visible) {
81283
82035
  return null;
81284
82036
  }
81285
82037
  return /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81286
82038
  flexDirection: "column",
81287
82039
  children: [
81288
- streaming && /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81289
- flexDirection: "row",
81290
- marginBottom: 1,
81291
- children: [
81292
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81293
- width: 2,
81294
- flexShrink: 0,
81295
- children: /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81296
- color: colors.status.processing,
81297
- children: /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Spinner2, {
81298
- type: "layer"
81299
- }, undefined, false, undefined, this)
81300
- }, undefined, false, undefined, this)
81301
- }, undefined, false, undefined, this),
81302
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81303
- flexGrow: 1,
81304
- flexDirection: "row",
81305
- children: [
81306
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(ShimmerText, {
81307
- boldPrefix: agentName || undefined,
81308
- message: thinkingMessage,
81309
- shimmerOffset
81310
- }, undefined, false, undefined, this),
81311
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81312
- children: statusHintText
81313
- }, undefined, false, undefined, this)
81314
- ]
81315
- }, undefined, true, undefined, this)
81316
- ]
81317
- }, undefined, true, undefined, this),
81318
- messageQueue && messageQueue.length > 0 && /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(QueuedMessages, {
81319
- messages: messageQueue
82040
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(StreamingStatus, {
82041
+ streaming,
82042
+ visible,
82043
+ tokenCount,
82044
+ elapsedBaseMs,
82045
+ thinkingMessage,
82046
+ agentName,
82047
+ interruptRequested,
82048
+ networkPhase,
82049
+ terminalWidth: columns,
82050
+ shouldAnimate
81320
82051
  }, undefined, false, undefined, this),
81321
- interactionEnabled ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81322
- flexDirection: "column",
81323
- children: [
81324
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81325
- dimColor: !isBashMode,
81326
- color: isBashMode ? colors.bash.border : undefined,
81327
- children: horizontalLine
81328
- }, undefined, false, undefined, this),
81329
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81330
- flexDirection: "row",
81331
- children: [
81332
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81333
- width: 2,
81334
- flexShrink: 0,
81335
- children: [
81336
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81337
- color: isBashMode ? colors.bash.prompt : colors.input.prompt,
81338
- children: isBashMode ? "!" : ">"
81339
- }, undefined, false, undefined, this),
81340
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81341
- children: " "
81342
- }, undefined, false, undefined, this)
81343
- ]
81344
- }, undefined, true, undefined, this),
81345
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81346
- flexGrow: 1,
81347
- width: contentWidth,
81348
- children: /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(PasteAwareTextInput, {
81349
- value,
81350
- onChange: setValue,
81351
- onSubmit: handleSubmit,
81352
- cursorPosition: cursorPos,
81353
- onCursorMove: setCurrentCursorPosition,
81354
- focus: interactionEnabled && !onEscapeCancel,
81355
- onBangAtEmpty: handleBangAtEmpty,
81356
- onBackspaceAtEmpty: handleBackspaceAtEmpty,
81357
- onPasteError
81358
- }, undefined, false, undefined, this)
81359
- }, undefined, false, undefined, this)
81360
- ]
81361
- }, undefined, true, undefined, this),
81362
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81363
- dimColor: !isBashMode,
81364
- color: isBashMode ? colors.bash.border : undefined,
81365
- children: horizontalLine
81366
- }, undefined, false, undefined, this),
81367
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(InputAssist, {
81368
- currentInput: value,
81369
- cursorPosition: currentCursorPosition,
81370
- onFileSelect: handleFileSelect,
81371
- onCommandSelect: handleCommandSelect,
81372
- onCommandAutocomplete: handleCommandAutocomplete,
81373
- onAutocompleteActiveChange: setIsAutocompleteActive,
81374
- agentId,
81375
- agentName,
81376
- serverUrl,
81377
- workingDirectory: process.cwd(),
81378
- conversationId
81379
- }, undefined, false, undefined, this),
81380
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(InputFooter, {
81381
- ctrlCPressed,
81382
- escapePressed,
81383
- isBashMode,
81384
- modeName: modeInfo?.name ?? null,
81385
- modeColor: modeInfo?.color ?? null,
81386
- showExitHint: ralphActive || ralphPending,
81387
- agentName,
81388
- currentModel,
81389
- isOpenAICodexProvider: currentModelProvider === OPENAI_CODEX_PROVIDER_NAME,
81390
- isByokProvider: currentModelProvider?.startsWith("lc-") || currentModelProvider === OPENAI_CODEX_PROVIDER_NAME,
81391
- isAutocompleteActive,
81392
- hideFooter
81393
- }, undefined, false, undefined, this)
81394
- ]
81395
- }, undefined, true, undefined, this) : reserveInputSpace ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81396
- height: inputChromeHeight
81397
- }, undefined, false, undefined, this) : null
82052
+ lowerPane
81398
82053
  ]
81399
82054
  }, undefined, true, undefined, this);
81400
82055
  }
@@ -81417,14 +82072,13 @@ function formatElapsedLabel(ms) {
81417
82072
  }
81418
82073
  return seconds > 0 ? `${minutes}m ${seconds}s` : `${minutes}m`;
81419
82074
  }
81420
- var import_react68, jsx_dev_runtime45, Spinner2, ESC_CLEAR_WINDOW_MS = 2500, InputFooter;
82075
+ var import_react68, jsx_dev_runtime45, Spinner2, ESC_CLEAR_WINDOW_MS = 2500, FOOTER_WIDTH_STREAMING_DELTA = 2, InputFooter, StreamingStatus;
81421
82076
  var init_InputRich = __esm(async () => {
81422
82077
  init_source();
81423
82078
  init_oauth();
81424
82079
  init_constants();
81425
82080
  init_mode();
81426
82081
  init_mode2();
81427
- init_useTerminalWidth();
81428
82082
  init_colors();
81429
82083
  await __promiseAll([
81430
82084
  init_build2(),
@@ -81451,78 +82105,249 @@ var init_InputRich = __esm(async () => {
81451
82105
  currentModel,
81452
82106
  isOpenAICodexProvider,
81453
82107
  isByokProvider,
81454
- isAutocompleteActive,
81455
- hideFooter
82108
+ hideFooter,
82109
+ rightColumnWidth
82110
+ }) {
82111
+ const hideFooterContent = hideFooter;
82112
+ const maxAgentChars = Math.max(10, Math.floor(rightColumnWidth * 0.45));
82113
+ const displayAgentName = truncateEnd(agentName || "Unnamed", maxAgentChars);
82114
+ const byokExtraChars = isByokProvider ? 2 : 0;
82115
+ const reservedChars = displayAgentName.length + byokExtraChars + 4;
82116
+ const maxModelChars = Math.max(8, rightColumnWidth - reservedChars);
82117
+ const displayModel = truncateEnd(currentModel ?? "unknown", maxModelChars);
82118
+ const rightTextLength = displayAgentName.length + displayModel.length + byokExtraChars + 3;
82119
+ const rightPrefixSpaces = Math.max(0, rightColumnWidth - rightTextLength);
82120
+ const rightLabel = import_react68.useMemo(() => {
82121
+ const parts = [];
82122
+ parts.push(" ".repeat(rightPrefixSpaces));
82123
+ parts.push(source_default.hex(colors.footer.agentName)(displayAgentName));
82124
+ parts.push(source_default.dim(" ["));
82125
+ parts.push(source_default.dim(displayModel));
82126
+ if (isByokProvider) {
82127
+ parts.push(source_default.dim(" "));
82128
+ parts.push(isOpenAICodexProvider ? source_default.hex("#74AA9C")("▲") : source_default.yellow("▲"));
82129
+ }
82130
+ parts.push(source_default.dim("]"));
82131
+ return parts.join("");
82132
+ }, [
82133
+ rightPrefixSpaces,
82134
+ displayAgentName,
82135
+ displayModel,
82136
+ isByokProvider,
82137
+ isOpenAICodexProvider
82138
+ ]);
82139
+ return /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
82140
+ flexDirection: "row",
82141
+ marginBottom: 1,
82142
+ children: [
82143
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
82144
+ flexGrow: 1,
82145
+ paddingRight: 1,
82146
+ children: hideFooterContent ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82147
+ children: " "
82148
+ }, undefined, false, undefined, this) : ctrlCPressed ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82149
+ dimColor: true,
82150
+ children: "Press CTRL-C again to exit"
82151
+ }, undefined, false, undefined, this) : escapePressed ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82152
+ dimColor: true,
82153
+ children: "Press Esc again to clear"
82154
+ }, undefined, false, undefined, this) : isBashMode ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82155
+ children: [
82156
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82157
+ color: colors.bash.prompt,
82158
+ children: "⏵⏵ bash mode"
82159
+ }, undefined, false, undefined, this),
82160
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82161
+ color: colors.bash.prompt,
82162
+ dimColor: true,
82163
+ children: [
82164
+ " ",
82165
+ "(backspace to exit)"
82166
+ ]
82167
+ }, undefined, true, undefined, this)
82168
+ ]
82169
+ }, undefined, true, undefined, this) : modeName && modeColor ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82170
+ children: [
82171
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82172
+ color: modeColor,
82173
+ children: [
82174
+ "⏵⏵ ",
82175
+ modeName
82176
+ ]
82177
+ }, undefined, true, undefined, this),
82178
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82179
+ color: modeColor,
82180
+ dimColor: true,
82181
+ children: [
82182
+ " ",
82183
+ "(shift+tab to ",
82184
+ showExitHint ? "exit" : "cycle",
82185
+ ")"
82186
+ ]
82187
+ }, undefined, true, undefined, this)
82188
+ ]
82189
+ }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82190
+ dimColor: true,
82191
+ children: "Press / for commands"
82192
+ }, undefined, false, undefined, this)
82193
+ }, undefined, false, undefined, this),
82194
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
82195
+ width: rightColumnWidth,
82196
+ flexShrink: 0,
82197
+ children: hideFooterContent ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82198
+ children: " ".repeat(rightColumnWidth)
82199
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82200
+ children: rightLabel
82201
+ }, undefined, false, undefined, this)
82202
+ }, undefined, false, undefined, this)
82203
+ ]
82204
+ }, undefined, true, undefined, this);
82205
+ });
82206
+ StreamingStatus = import_react68.memo(function StreamingStatus2({
82207
+ streaming,
82208
+ visible,
82209
+ tokenCount,
82210
+ elapsedBaseMs,
82211
+ thinkingMessage,
82212
+ agentName,
82213
+ interruptRequested,
82214
+ networkPhase,
82215
+ terminalWidth,
82216
+ shouldAnimate
81456
82217
  }) {
81457
- if (hideFooter || isAutocompleteActive) {
82218
+ const [shimmerOffset, setShimmerOffset] = import_react68.useState(-3);
82219
+ const [elapsedMs, setElapsedMs] = import_react68.useState(0);
82220
+ const streamStartRef = import_react68.useRef(null);
82221
+ import_react68.useEffect(() => {
82222
+ if (!streaming || !visible || !shouldAnimate)
82223
+ return;
82224
+ const id = setInterval(() => {
82225
+ setShimmerOffset((prev) => {
82226
+ const prefixLen = agentName ? agentName.length + 1 : 0;
82227
+ const len = prefixLen + thinkingMessage.length;
82228
+ const next = prev + 1;
82229
+ return next > len + 3 ? -3 : next;
82230
+ });
82231
+ }, 120);
82232
+ return () => clearInterval(id);
82233
+ }, [streaming, thinkingMessage, visible, agentName, shouldAnimate]);
82234
+ import_react68.useEffect(() => {
82235
+ if (!shouldAnimate) {
82236
+ setShimmerOffset(-3);
82237
+ }
82238
+ }, [shouldAnimate]);
82239
+ import_react68.useEffect(() => {
82240
+ if (streaming && visible) {
82241
+ if (streamStartRef.current === null) {
82242
+ streamStartRef.current = performance.now();
82243
+ }
82244
+ const id = setInterval(() => {
82245
+ if (streamStartRef.current !== null) {
82246
+ setElapsedMs(performance.now() - streamStartRef.current);
82247
+ }
82248
+ }, 1000);
82249
+ return () => clearInterval(id);
82250
+ }
82251
+ streamStartRef.current = null;
82252
+ setElapsedMs(0);
82253
+ }, [streaming, visible]);
82254
+ const estimatedTokens = charsToTokens(tokenCount);
82255
+ const totalElapsedMs = elapsedBaseMs + elapsedMs;
82256
+ const shouldShowTokenCount = streaming && estimatedTokens > TOKEN_DISPLAY_THRESHOLD;
82257
+ const shouldShowElapsed = streaming && totalElapsedMs > ELAPSED_DISPLAY_THRESHOLD_MS;
82258
+ const elapsedLabel = formatElapsedLabel(totalElapsedMs);
82259
+ const networkArrow = import_react68.useMemo(() => {
82260
+ if (!networkPhase)
82261
+ return "";
82262
+ if (networkPhase === "upload")
82263
+ return "↑";
82264
+ if (networkPhase === "download")
82265
+ return "↑";
82266
+ return "↑̸";
82267
+ }, [networkPhase]);
82268
+ const showErrorArrow = networkArrow === "↑̸";
82269
+ const statusContentWidth = Math.max(0, terminalWidth - 2);
82270
+ const minMessageWidth = 12;
82271
+ const statusHintParts = import_react68.useMemo(() => {
82272
+ const parts = [];
82273
+ if (shouldShowElapsed) {
82274
+ parts.push(elapsedLabel);
82275
+ }
82276
+ if (shouldShowTokenCount) {
82277
+ parts.push(`${formatCompact(estimatedTokens)}${networkArrow ? ` ${networkArrow}` : ""}`);
82278
+ } else if (showErrorArrow) {
82279
+ parts.push(networkArrow);
82280
+ }
82281
+ return parts;
82282
+ }, [
82283
+ shouldShowElapsed,
82284
+ elapsedLabel,
82285
+ shouldShowTokenCount,
82286
+ estimatedTokens,
82287
+ networkArrow,
82288
+ showErrorArrow
82289
+ ]);
82290
+ const statusHintSuffix = statusHintParts.length ? ` · ${statusHintParts.join(" · ")}` : "";
82291
+ const statusHintPlain = interruptRequested ? ` (interrupting${statusHintSuffix})` : ` (esc to interrupt${statusHintSuffix})`;
82292
+ const statusHintWidth = Array.from(statusHintPlain).length;
82293
+ const maxHintWidth = Math.max(0, statusContentWidth - minMessageWidth);
82294
+ const hintColumnWidth = Math.max(0, Math.min(statusHintWidth, maxHintWidth));
82295
+ const maxMessageWidth = Math.max(0, statusContentWidth - hintColumnWidth);
82296
+ const statusLabel = `${agentName ? `${agentName} ` : ""}${thinkingMessage}…`;
82297
+ const statusLabelWidth = Array.from(statusLabel).length;
82298
+ const messageColumnWidth = Math.max(0, Math.min(maxMessageWidth, Math.max(minMessageWidth, statusLabelWidth)));
82299
+ const statusHintText = import_react68.useMemo(() => {
82300
+ const hintColor = source_default.hex(colors.subagent.hint);
82301
+ const hintBold = hintColor.bold;
82302
+ const suffix = `${statusHintSuffix})`;
82303
+ if (interruptRequested) {
82304
+ return hintColor(` (interrupting${suffix}`);
82305
+ }
82306
+ return hintColor(" (") + hintBold("esc") + hintColor(` to interrupt${suffix}`);
82307
+ }, [interruptRequested, statusHintSuffix]);
82308
+ if (!streaming || !visible) {
81458
82309
  return null;
81459
82310
  }
81460
82311
  return /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
81461
- justifyContent: "space-between",
82312
+ flexDirection: "row",
81462
82313
  marginBottom: 1,
81463
82314
  children: [
81464
- ctrlCPressed ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81465
- dimColor: true,
81466
- children: "Press CTRL-C again to exit"
81467
- }, undefined, false, undefined, this) : escapePressed ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81468
- dimColor: true,
81469
- children: "Press Esc again to clear"
81470
- }, undefined, false, undefined, this) : isBashMode ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81471
- children: [
81472
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81473
- color: colors.bash.prompt,
81474
- children: "⏵⏵ bash mode"
81475
- }, undefined, false, undefined, this),
81476
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81477
- color: colors.bash.prompt,
81478
- dimColor: true,
81479
- children: [
81480
- " ",
81481
- "(backspace to exit)"
81482
- ]
81483
- }, undefined, true, undefined, this)
81484
- ]
81485
- }, undefined, true, undefined, this) : modeName && modeColor ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81486
- children: [
81487
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81488
- color: modeColor,
81489
- children: [
81490
- "⏵⏵ ",
81491
- modeName
81492
- ]
81493
- }, undefined, true, undefined, this),
81494
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81495
- color: modeColor,
81496
- dimColor: true,
81497
- children: [
81498
- " ",
81499
- "(shift+tab to ",
81500
- showExitHint ? "exit" : "cycle",
81501
- ")"
81502
- ]
81503
- }, undefined, true, undefined, this)
81504
- ]
81505
- }, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81506
- dimColor: true,
81507
- children: "Press / for commands"
82315
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
82316
+ width: 2,
82317
+ flexShrink: 0,
82318
+ children: /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82319
+ color: colors.status.processing,
82320
+ children: shouldAnimate ? /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Spinner2, {
82321
+ type: "layer"
82322
+ }, undefined, false, undefined, this) : "●"
82323
+ }, undefined, false, undefined, this)
81508
82324
  }, undefined, false, undefined, this),
81509
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82325
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
82326
+ width: statusContentWidth,
82327
+ flexShrink: 0,
82328
+ flexDirection: "row",
81510
82329
  children: [
81511
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81512
- color: colors.footer.agentName,
81513
- children: agentName || "Unnamed"
82330
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
82331
+ width: messageColumnWidth,
82332
+ flexShrink: 0,
82333
+ children: /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(ShimmerText, {
82334
+ boldPrefix: agentName || undefined,
82335
+ message: thinkingMessage,
82336
+ shimmerOffset: shouldAnimate ? shimmerOffset : -3,
82337
+ wrap: "truncate-end"
82338
+ }, undefined, false, undefined, this)
81514
82339
  }, undefined, false, undefined, this),
81515
- /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81516
- dimColor: true,
81517
- children: [
81518
- ` [${currentModel ?? "unknown"}`,
81519
- isByokProvider && /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
81520
- color: isOpenAICodexProvider ? "#74AA9C" : "yellow",
81521
- children: " ▲"
81522
- }, undefined, false, undefined, this),
81523
- "]"
81524
- ]
81525
- }, undefined, true, undefined, this)
82340
+ hintColumnWidth > 0 && /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
82341
+ width: hintColumnWidth,
82342
+ flexShrink: 0,
82343
+ children: /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Text2, {
82344
+ wrap: "truncate-end",
82345
+ children: statusHintText
82346
+ }, undefined, false, undefined, this)
82347
+ }, undefined, false, undefined, this),
82348
+ /* @__PURE__ */ jsx_dev_runtime45.jsxDEV(Box_default, {
82349
+ flexGrow: 1
82350
+ }, undefined, false, undefined, this)
81526
82351
  ]
81527
82352
  }, undefined, true, undefined, this)
81528
82353
  ]
@@ -84640,22 +85465,22 @@ function ModelSelector({
84640
85465
  };
84641
85466
  const getCategoryDescription = (cat) => {
84642
85467
  if (cat === "server-recommended") {
84643
- return "Recommended models on the server";
85468
+ return "Recommended models currently available for this account";
84644
85469
  }
84645
85470
  if (cat === "server-all") {
84646
- return "All models on the server";
85471
+ return "All models currently available for this account";
84647
85472
  }
84648
85473
  if (cat === "supported") {
84649
- return isFreeTier ? "Upgrade your account to access more models" : "Recommended models on the Letta API";
85474
+ return isFreeTier ? "Upgrade your account to access more models" : "Recommended Letta API models currently available for this account";
84650
85475
  }
84651
85476
  if (cat === "byok")
84652
- return "Recommended models via your API keys (use /connect to add more)";
85477
+ return "Recommended models via your connected API keys (use /connect to add more)";
84653
85478
  if (cat === "byok-all")
84654
- return "All models via your API keys (use /connect to add more)";
85479
+ return "All models via your connected API keys (use /connect to add more)";
84655
85480
  if (cat === "all") {
84656
- return isFreeTier ? "Upgrade your account to access more models" : "All models on the Letta API";
85481
+ return isFreeTier ? "Upgrade your account to access more models" : "All Letta API models currently available for this account";
84657
85482
  }
84658
- return "All models on the Letta API";
85483
+ return "All Letta API models currently available for this account";
84659
85484
  };
84660
85485
  const renderTabBar = () => /* @__PURE__ */ jsx_dev_runtime51.jsxDEV(Box_default, {
84661
85486
  flexDirection: "row",
@@ -84815,7 +85640,7 @@ function ModelSelector({
84815
85640
  currentList.length,
84816
85641
  " models",
84817
85642
  isCached ? " · cached" : "",
84818
- " · R to refresh"
85643
+ " · R to refresh availability"
84819
85644
  ]
84820
85645
  }, undefined, true, undefined, this),
84821
85646
  /* @__PURE__ */ jsx_dev_runtime51.jsxDEV(Text2, {
@@ -85431,11 +86256,11 @@ var init_byok_providers = __esm(async () => {
85431
86256
 
85432
86257
  // src/utils/aws-credentials.ts
85433
86258
  import { readFile as readFile12 } from "node:fs/promises";
85434
- import { homedir as homedir14 } from "node:os";
86259
+ import { homedir as homedir15 } from "node:os";
85435
86260
  import { join as join26 } from "node:path";
85436
86261
  async function parseAwsCredentials() {
85437
- const credentialsPath = join26(homedir14(), ".aws", "credentials");
85438
- const configPath = join26(homedir14(), ".aws", "config");
86262
+ const credentialsPath = join26(homedir15(), ".aws", "credentials");
86263
+ const configPath = join26(homedir15(), ".aws", "config");
85439
86264
  const profiles = new Map;
85440
86265
  try {
85441
86266
  const content = await readFile12(credentialsPath, "utf-8");
@@ -88406,13 +89231,14 @@ function isShellTool2(name) {
88406
89231
  var import_react85, jsx_dev_runtime65, ToolCallMessage;
88407
89232
  var init_ToolCallMessageRich = __esm(async () => {
88408
89233
  init_constants();
88409
- init_formatArgsDisplay();
88410
89234
  init_subagentState();
88411
89235
  init_useTerminalWidth();
88412
89236
  init_colors();
88413
89237
  await __promiseAll([
88414
89238
  init_build2(),
88415
89239
  init_manager3(),
89240
+ init_formatArgsDisplay(),
89241
+ init_toolNameMapping(),
88416
89242
  init_Text2(),
88417
89243
  init_AdvancedDiffRenderer(),
88418
89244
  init_BlinkDot(),
@@ -89153,7 +89979,8 @@ var init_ToolCallMessageRich = __esm(async () => {
89153
89979
  streaming: line.streaming
89154
89980
  }, undefined, false, undefined, this),
89155
89981
  isShellTool2(rawName) && line.phase === "finished" && line.resultText && line.resultOk !== false && /* @__PURE__ */ jsx_dev_runtime65.jsxDEV(CollapsedOutputDisplay, {
89156
- output: line.resultText
89982
+ output: line.resultText,
89983
+ maxChars: 300
89157
89984
  }, undefined, false, undefined, this),
89158
89985
  (() => {
89159
89986
  const showDefaultResult = !isShellTool2(rawName) || line.phase === "finished" && line.resultOk === false || line.phase !== "running" && line.phase !== "finished";
@@ -90240,8 +91067,9 @@ function createSubagentGroupItem(taskToolCalls) {
90240
91067
  agents
90241
91068
  };
90242
91069
  }
90243
- var init_subagentAggregation = __esm(() => {
91070
+ var init_subagentAggregation = __esm(async () => {
90244
91071
  init_subagentState();
91072
+ await init_toolNameMapping();
90245
91073
  });
90246
91074
 
90247
91075
  // src/cli/helpers/subagentTurnStart.ts
@@ -90414,7 +91242,7 @@ __export(exports_shellAliases, {
90414
91242
  clearAliasCache: () => clearAliasCache
90415
91243
  });
90416
91244
  import { existsSync as existsSync16, readFileSync as readFileSync8 } from "node:fs";
90417
- import { homedir as homedir15 } from "node:os";
91245
+ import { homedir as homedir16 } from "node:os";
90418
91246
  import { join as join27 } from "node:path";
90419
91247
  function parseAliasesFromFile(filePath) {
90420
91248
  const aliases = new Map;
@@ -90484,7 +91312,7 @@ function loadAliases(forceReload = false) {
90484
91312
  if (aliasCache && !forceReload) {
90485
91313
  return aliasCache;
90486
91314
  }
90487
- const home = homedir15();
91315
+ const home = homedir16();
90488
91316
  const allAliases = new Map;
90489
91317
  for (const file of ALIAS_FILES) {
90490
91318
  const filePath = join27(home, file);
@@ -91105,21 +91933,6 @@ Use /disconnect codex to remove the current connection first.`, false);
91105
91933
  ctx.setCommandRunning(true);
91106
91934
  const cmdId = addCommandResult3(ctx.buffersRef, ctx.refreshDerived, msg, "Checking account eligibility...", true, "running");
91107
91935
  try {
91108
- const eligibility = await checkOpenAICodexEligibility();
91109
- if (!eligibility.eligible) {
91110
- updateCommandResult3(ctx.buffersRef, ctx.refreshDerived, cmdId, msg, `✗ ChatGPT OAuth requires a Pro or Enterprise plan
91111
-
91112
- ` + `This feature is only available for Letta Pro or Enterprise customers.
91113
- ` + `Current plan: ${eligibility.billing_tier}
91114
-
91115
- ` + `To upgrade your plan, visit:
91116
-
91117
- ` + ` https://app.letta.com/settings/organization/usage
91118
-
91119
- ` + `If you have an OpenAI API key, you can use it directly by setting:
91120
- ` + ` export OPENAI_API_KEY=your-key`, false, "finished");
91121
- return;
91122
- }
91123
91936
  updateCommandResult3(ctx.buffersRef, ctx.refreshDerived, cmdId, msg, `Starting OAuth flow...
91124
91937
  A browser window will open for authorization.`, true, "running");
91125
91938
  const { authorizationUrl, state, codeVerifier, redirectUri } = await startOpenAIOAuth(OPENAI_OAUTH_CONFIG.defaultPort);
@@ -91173,20 +91986,7 @@ Your ChatGPT Plus/Pro subscription is now linked.`, true, "finished");
91173
91986
  } catch (error) {
91174
91987
  settingsManager.clearOAuthState();
91175
91988
  const errorMessage = getErrorMessage2(error);
91176
- let displayMessage;
91177
- if (errorMessage === "PLAN_UPGRADE_REQUIRED") {
91178
- displayMessage = `✗ ChatGPT OAuth requires a Pro or Enterprise plan
91179
-
91180
- ` + `This feature is only available for Letta Pro or Enterprise customers.
91181
- To upgrade your plan, visit:
91182
-
91183
- https://app.letta.com/settings/organization/usage
91184
-
91185
- If you have an OpenAI API key, you can use it directly by setting:
91186
- export OPENAI_API_KEY=your-key`;
91187
- } else {
91188
- displayMessage = `✗ Failed to connect: ${errorMessage}`;
91189
- }
91989
+ const displayMessage = `✗ Failed to connect: ${errorMessage}`;
91190
91990
  updateCommandResult3(ctx.buffersRef, ctx.refreshDerived, cmdId, msg, displayMessage, false, "finished");
91191
91991
  } finally {
91192
91992
  ctx.setCommandRunning(false);
@@ -91495,7 +92295,7 @@ async function packageSkills(agentId, skillsDir) {
91495
92295
  console.warn(`Skipping invalid skill ${entry.name}: missing SKILL.md`);
91496
92296
  continue;
91497
92297
  }
91498
- const sourceUrl = await findSkillSourceUrl(entry.name);
92298
+ const sourceUrl = skillsDir ? null : await findSkillSourceUrl(entry.name);
91499
92299
  const skill2 = { name: entry.name };
91500
92300
  if (sourceUrl) {
91501
92301
  skill2.source_url = sourceUrl;
@@ -91571,7 +92371,7 @@ __export(exports_App, {
91571
92371
  default: () => App2
91572
92372
  });
91573
92373
  import { existsSync as existsSync17, readFileSync as readFileSync9, renameSync, writeFileSync as writeFileSync6 } from "node:fs";
91574
- import { homedir as homedir16, tmpdir as tmpdir3 } from "node:os";
92374
+ import { homedir as homedir17, tmpdir as tmpdir3 } from "node:os";
91575
92375
  import { join as join28 } from "node:path";
91576
92376
  function getErrorHintForStopReason(stopReason, currentModelId) {
91577
92377
  if (currentModelId === "opus" && stopReason === "llm_api_error" && getModelInfo("bedrock-opus")) {
@@ -92228,18 +93028,93 @@ function App2({
92228
93028
  setCommandRunning(false);
92229
93029
  }
92230
93030
  }, [setCommandRunning]);
92231
- const columns = useTerminalWidth();
93031
+ const rawColumns = useTerminalWidth();
92232
93032
  const terminalRows = useTerminalRows();
92233
- const prevColumnsRef = import_react91.useRef(columns);
92234
- const lastClearedColumnsRef = import_react91.useRef(columns);
93033
+ const [stableColumns, setStableColumns] = import_react91.useState(rawColumns);
93034
+ const stableColumnsTimeoutRef = import_react91.useRef(null);
93035
+ const prevColumnsRef = import_react91.useRef(rawColumns);
93036
+ const lastClearedColumnsRef = import_react91.useRef(rawColumns);
92235
93037
  const pendingResizeRef = import_react91.useRef(false);
92236
93038
  const pendingResizeColumnsRef = import_react91.useRef(null);
92237
93039
  const [staticRenderEpoch, setStaticRenderEpoch] = import_react91.useState(0);
92238
93040
  const resizeClearTimeout = import_react91.useRef(null);
93041
+ const lastClearAtRef = import_react91.useRef(0);
92239
93042
  const isInitialResizeRef = import_react91.useRef(true);
93043
+ const columns = stableColumns;
93044
+ const debugFlicker = process.env.LETTA_DEBUG_FLICKER === "1";
93045
+ import_react91.useEffect(() => {
93046
+ if (rawColumns === stableColumns) {
93047
+ if (stableColumnsTimeoutRef.current) {
93048
+ clearTimeout(stableColumnsTimeoutRef.current);
93049
+ stableColumnsTimeoutRef.current = null;
93050
+ }
93051
+ return;
93052
+ }
93053
+ const delta = Math.abs(rawColumns - stableColumns);
93054
+ if (delta >= MIN_RESIZE_DELTA) {
93055
+ if (stableColumnsTimeoutRef.current) {
93056
+ clearTimeout(stableColumnsTimeoutRef.current);
93057
+ stableColumnsTimeoutRef.current = null;
93058
+ }
93059
+ setStableColumns(rawColumns);
93060
+ return;
93061
+ }
93062
+ if (stableColumnsTimeoutRef.current) {
93063
+ clearTimeout(stableColumnsTimeoutRef.current);
93064
+ }
93065
+ stableColumnsTimeoutRef.current = setTimeout(() => {
93066
+ stableColumnsTimeoutRef.current = null;
93067
+ setStableColumns(rawColumns);
93068
+ }, STABLE_WIDTH_SETTLE_MS);
93069
+ }, [rawColumns, stableColumns]);
93070
+ const clearAndRemount = import_react91.useCallback((targetColumns) => {
93071
+ if (debugFlicker) {
93072
+ console.error(`[debug:flicker:clear-remount] target=${targetColumns} previousCleared=${lastClearedColumnsRef.current} raw=${prevColumnsRef.current}`);
93073
+ }
93074
+ if (typeof process !== "undefined" && process.stdout && "write" in process.stdout && process.stdout.isTTY) {
93075
+ process.stdout.write(CLEAR_SCREEN_AND_HOME);
93076
+ }
93077
+ setStaticRenderEpoch((epoch) => epoch + 1);
93078
+ lastClearedColumnsRef.current = targetColumns;
93079
+ lastClearAtRef.current = Date.now();
93080
+ }, [debugFlicker]);
93081
+ const scheduleResizeClear = import_react91.useCallback((targetColumns) => {
93082
+ if (targetColumns === lastClearedColumnsRef.current) {
93083
+ return;
93084
+ }
93085
+ if (resizeClearTimeout.current) {
93086
+ clearTimeout(resizeClearTimeout.current);
93087
+ resizeClearTimeout.current = null;
93088
+ }
93089
+ const elapsedSinceClear = Date.now() - lastClearAtRef.current;
93090
+ const rateLimitDelay = elapsedSinceClear >= MIN_CLEAR_INTERVAL_MS ? 0 : MIN_CLEAR_INTERVAL_MS - elapsedSinceClear;
93091
+ const delay = Math.max(RESIZE_SETTLE_MS, rateLimitDelay);
93092
+ if (debugFlicker) {
93093
+ console.error(`[debug:flicker:resize-schedule] target=${targetColumns} delay=${delay}ms elapsedSinceClear=${elapsedSinceClear}ms`);
93094
+ }
93095
+ resizeClearTimeout.current = setTimeout(() => {
93096
+ resizeClearTimeout.current = null;
93097
+ if (prevColumnsRef.current !== targetColumns) {
93098
+ if (debugFlicker) {
93099
+ console.error(`[debug:flicker:resize-skip] stale target=${targetColumns} currentRaw=${prevColumnsRef.current}`);
93100
+ }
93101
+ return;
93102
+ }
93103
+ if (targetColumns === lastClearedColumnsRef.current) {
93104
+ if (debugFlicker) {
93105
+ console.error(`[debug:flicker:resize-skip] already-cleared target=${targetColumns}`);
93106
+ }
93107
+ return;
93108
+ }
93109
+ if (debugFlicker) {
93110
+ console.error(`[debug:flicker:resize-fire] clear target=${targetColumns}`);
93111
+ }
93112
+ clearAndRemount(targetColumns);
93113
+ }, delay);
93114
+ }, [clearAndRemount, debugFlicker]);
92240
93115
  import_react91.useEffect(() => {
92241
93116
  const prev = prevColumnsRef.current;
92242
- if (columns === prev)
93117
+ if (rawColumns === prev)
92243
93118
  return;
92244
93119
  if (resizeClearTimeout.current) {
92245
93120
  clearTimeout(resizeClearTimeout.current);
@@ -92247,52 +93122,38 @@ function App2({
92247
93122
  }
92248
93123
  if (isInitialResizeRef.current) {
92249
93124
  isInitialResizeRef.current = false;
92250
- prevColumnsRef.current = columns;
92251
- lastClearedColumnsRef.current = columns;
93125
+ prevColumnsRef.current = rawColumns;
93126
+ lastClearedColumnsRef.current = rawColumns;
92252
93127
  return;
92253
93128
  }
92254
- const delta = Math.abs(columns - prev);
93129
+ const delta = Math.abs(rawColumns - prev);
92255
93130
  const isMinorJitter = delta > 0 && delta < MIN_RESIZE_DELTA;
93131
+ if (isMinorJitter) {
93132
+ prevColumnsRef.current = rawColumns;
93133
+ return;
93134
+ }
92256
93135
  if (streaming) {
92257
- if (isMinorJitter) {
92258
- prevColumnsRef.current = columns;
92259
- return;
92260
- }
92261
93136
  pendingResizeRef.current = true;
92262
- pendingResizeColumnsRef.current = columns;
92263
- prevColumnsRef.current = columns;
93137
+ pendingResizeColumnsRef.current = rawColumns;
93138
+ prevColumnsRef.current = rawColumns;
92264
93139
  return;
92265
93140
  }
92266
- if (columns === lastClearedColumnsRef.current) {
93141
+ if (rawColumns === lastClearedColumnsRef.current) {
92267
93142
  pendingResizeRef.current = false;
92268
93143
  pendingResizeColumnsRef.current = null;
92269
- prevColumnsRef.current = columns;
93144
+ prevColumnsRef.current = rawColumns;
92270
93145
  return;
92271
93146
  }
92272
- const scheduledColumns = columns;
92273
- resizeClearTimeout.current = setTimeout(() => {
92274
- resizeClearTimeout.current = null;
92275
- if (typeof process !== "undefined" && process.stdout && "write" in process.stdout && process.stdout.isTTY) {
92276
- process.stdout.write(CLEAR_SCREEN_AND_HOME);
92277
- }
92278
- setStaticRenderEpoch((epoch) => epoch + 1);
92279
- lastClearedColumnsRef.current = scheduledColumns;
92280
- }, 150);
92281
- prevColumnsRef.current = columns;
92282
- return () => {
92283
- if (resizeClearTimeout.current) {
92284
- clearTimeout(resizeClearTimeout.current);
92285
- resizeClearTimeout.current = null;
92286
- }
92287
- };
92288
- }, [columns, streaming]);
93147
+ scheduleResizeClear(rawColumns);
93148
+ prevColumnsRef.current = rawColumns;
93149
+ }, [rawColumns, streaming, scheduleResizeClear]);
92289
93150
  import_react91.useEffect(() => {
92290
93151
  if (streaming) {
92291
93152
  if (resizeClearTimeout.current) {
92292
93153
  clearTimeout(resizeClearTimeout.current);
92293
93154
  resizeClearTimeout.current = null;
92294
93155
  pendingResizeRef.current = true;
92295
- pendingResizeColumnsRef.current = columns;
93156
+ pendingResizeColumnsRef.current = rawColumns;
92296
93157
  }
92297
93158
  return;
92298
93159
  }
@@ -92305,12 +93166,20 @@ function App2({
92305
93166
  return;
92306
93167
  if (pendingColumns === lastClearedColumnsRef.current)
92307
93168
  return;
92308
- if (typeof process !== "undefined" && process.stdout && "write" in process.stdout && process.stdout.isTTY) {
92309
- process.stdout.write(CLEAR_SCREEN_AND_HOME);
92310
- }
92311
- setStaticRenderEpoch((epoch) => epoch + 1);
92312
- lastClearedColumnsRef.current = pendingColumns;
92313
- }, [columns, streaming]);
93169
+ scheduleResizeClear(pendingColumns);
93170
+ }, [rawColumns, streaming, scheduleResizeClear]);
93171
+ import_react91.useEffect(() => {
93172
+ return () => {
93173
+ if (resizeClearTimeout.current) {
93174
+ clearTimeout(resizeClearTimeout.current);
93175
+ resizeClearTimeout.current = null;
93176
+ }
93177
+ if (stableColumnsTimeoutRef.current) {
93178
+ clearTimeout(stableColumnsTimeoutRef.current);
93179
+ stableColumnsTimeoutRef.current = null;
93180
+ }
93181
+ };
93182
+ }, []);
92314
93183
  const deferredToolCallCommitsRef = import_react91.useRef(new Map);
92315
93184
  const [deferredCommitAt, setDeferredCommitAt] = import_react91.useState(null);
92316
93185
  const resetDeferredToolCallCommits = import_react91.useCallback(() => {
@@ -92909,7 +93778,7 @@ function App2({
92909
93778
  continue;
92910
93779
  if (!line.toolCallId || !line.name)
92911
93780
  continue;
92912
- if (line.name !== "memory" && line.name !== "memory_apply_patch")
93781
+ if (!isMemoryTool(line.name))
92913
93782
  continue;
92914
93783
  if (memorySyncProcessedToolCallsRef.current.has(line.toolCallId))
92915
93784
  continue;
@@ -93980,7 +94849,9 @@ ${feedback}
93980
94849
  };
93981
94850
  const errorDetails = formatErrorDetails(errorObject, agentIdRef.current);
93982
94851
  appendError(errorDetails, true);
93983
- appendError(getErrorHintForStopReason(stopReasonToHandle, currentModelId), true);
94852
+ if (!isEncryptedContentError(errorObject)) {
94853
+ appendError(getErrorHintForStopReason(stopReasonToHandle, currentModelId), true);
94854
+ }
93984
94855
  } else {
93985
94856
  appendError(`An error occurred during agent execution
93986
94857
  (run_id: ${lastRunId}, stop_reason: ${stopReason})`, true);
@@ -96852,8 +97723,9 @@ ${SYSTEM_REMINDER_CLOSE}
96852
97723
  };
96853
97724
  }
96854
97725
  if (!selectedModel) {
96855
- const cmd = overlayCommand ?? commandRunner.start("/model", `Model not found: ${modelId}`);
96856
- cmd.fail(`Model not found: ${modelId}`);
97726
+ const output = `Model not found: ${modelId}. Run /model and press R to refresh available models.`;
97727
+ const cmd = overlayCommand ?? commandRunner.start("/model", output);
97728
+ cmd.fail(output);
96857
97729
  return;
96858
97730
  }
96859
97731
  const model = selectedModel;
@@ -96902,8 +97774,11 @@ Consider switching to a different system prompt using /system to match.` : null;
96902
97774
  });
96903
97775
  } catch (error) {
96904
97776
  const errorDetails = formatErrorDetails(error, agentId);
96905
- const cmd = overlayCommand ?? commandRunner.start("/model", "Failed to switch model.");
96906
- cmd.fail(`Failed to switch model: ${errorDetails}`);
97777
+ const modelLabel = selectedModel?.label ?? modelId;
97778
+ const guidance = "Run /model and press R to refresh available models. If the model is still unavailable, choose another model or connect a provider with /connect.";
97779
+ const cmd = overlayCommand ?? commandRunner.start("/model", `Failed to switch model to ${modelLabel}.`);
97780
+ cmd.fail(`Failed to switch model to ${modelLabel}: ${errorDetails}
97781
+ ${guidance}`);
96907
97782
  }
96908
97783
  }, [
96909
97784
  agentId,
@@ -97070,9 +97945,9 @@ Consider switching to a different system prompt using /system to match.` : null;
97070
97945
  commandRunner.start
97071
97946
  ]);
97072
97947
  const handleFeedbackSubmit = import_react91.useCallback(async (message) => {
97948
+ const overlayCommand = consumeOverlayCommand("feedback");
97073
97949
  closeOverlay();
97074
97950
  await withCommandLock(async () => {
97075
- const overlayCommand = consumeOverlayCommand("feedback");
97076
97951
  const cmd = overlayCommand ?? commandRunner.start("/feedback", "Sending feedback...");
97077
97952
  try {
97078
97953
  const resolvedMessage = resolvePlaceholders(message);
@@ -97279,7 +98154,7 @@ Consider switching to a different system prompt using /system to match.` : null;
97279
98154
  }
97280
98155
  if (!planFileExists()) {
97281
98156
  const planFilePath = permissionMode2.getPlanFilePath();
97282
- const plansDir = join28(homedir16(), ".letta", "plans");
98157
+ const plansDir = join28(homedir17(), ".letta", "plans");
97283
98158
  handlePlanKeepPlanning(`You must write your plan to a plan file before exiting plan mode.
97284
98159
  ` + (planFilePath ? `Plan file path: ${planFilePath}
97285
98160
  ` : "") + `Use a write tool to create your plan in ${plansDir}, then use ExitPlanMode to present the plan to the user.`);
@@ -97435,7 +98310,7 @@ Plan file path: ${planFilePath}`;
97435
98310
  deferredCommitAt
97436
98311
  ]);
97437
98312
  const { agents: subagents } = import_react91.useSyncExternalStore(subscribe2, getSnapshot2);
97438
- const shouldAnimate = import_react91.useMemo(() => {
98313
+ const estimatedLiveHeight = import_react91.useMemo(() => {
97439
98314
  const countLines4 = (text) => {
97440
98315
  if (!text)
97441
98316
  return 0;
@@ -97459,8 +98334,23 @@ Plan file path: ${planFilePath}`;
97459
98334
  const subagentsHeight = subagents.length * LINES_PER_SUBAGENT;
97460
98335
  const FIXED_BUFFER = 20;
97461
98336
  const estimatedHeight = liveItemsHeight + subagentsHeight + FIXED_BUFFER;
97462
- return estimatedHeight < terminalRows;
97463
- }, [liveItems, terminalRows, subagents.length]);
98337
+ return estimatedHeight;
98338
+ }, [liveItems, subagents.length]);
98339
+ const [shouldAnimate, setShouldAnimate] = import_react91.useState(() => estimatedLiveHeight < terminalRows);
98340
+ import_react91.useEffect(() => {
98341
+ if (terminalRows <= 0) {
98342
+ setShouldAnimate(false);
98343
+ return;
98344
+ }
98345
+ const disableThreshold = terminalRows;
98346
+ const resumeThreshold = Math.max(0, terminalRows - ANIMATION_RESUME_HYSTERESIS_ROWS);
98347
+ setShouldAnimate((prev) => {
98348
+ if (prev) {
98349
+ return estimatedLiveHeight < disableThreshold;
98350
+ }
98351
+ return estimatedLiveHeight < resumeThreshold;
98352
+ });
98353
+ }, [estimatedLiveHeight, terminalRows]);
97464
98354
  import_react91.useEffect(() => {
97465
98355
  if (loadingState === "ready" && !welcomeCommittedRef.current && messageHistory.length === 0) {
97466
98356
  if (!continueSession && !agentProvenance) {
@@ -97813,7 +98703,9 @@ Plan file path: ${planFilePath}`;
97813
98703
  onPasteError: handlePasteError,
97814
98704
  restoredInput,
97815
98705
  onRestoredInputConsumed: () => setRestoredInput(null),
97816
- networkPhase
98706
+ networkPhase,
98707
+ terminalWidth: columns,
98708
+ shouldAnimate
97817
98709
  }, undefined, false, undefined, this)
97818
98710
  }, undefined, false, undefined, this),
97819
98711
  activeOverlay === "model" && /* @__PURE__ */ jsx_dev_runtime69.jsxDEV(ModelSelector, {
@@ -98319,7 +99211,7 @@ Open /mcp to attach or detach tools for this server.`, true);
98319
99211
  ]
98320
99212
  }, resumeKey, true, undefined, this);
98321
99213
  }
98322
- var import_react91, jsx_dev_runtime69, CLEAR_SCREEN_AND_HOME = "\x1B[2J\x1B[H", MIN_RESIZE_DELTA = 2, TOOL_CALL_COMMIT_DEFER_MS = 50, EAGER_CANCEL = true, LLM_API_ERROR_MAX_RETRIES2 = 3, CONVERSATION_BUSY_MAX_RETRIES2 = 3, CONVERSATION_BUSY_RETRY_BASE_DELAY_MS = 2500, INTERRUPT_MESSAGE = "Interrupted – tell the agent what to do differently. Something went wrong? Use /feedback to report issues.", ERROR_FEEDBACK_HINT = "Something went wrong? Use /feedback to report issues.", OPUS_BEDROCK_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to Bedrock Opus 4.5", PROVIDER_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to another provider", INTERACTIVE_SLASH_COMMANDS, NON_STATE_COMMANDS, APPROVAL_OPTIONS_HEIGHT = 8, APPROVAL_PREVIEW_BUFFER = 4, MIN_WRAP_WIDTH = 10, TEXT_WRAP_GUTTER = 6, DIFF_WRAP_GUTTER = 12;
99214
+ var import_react91, jsx_dev_runtime69, CLEAR_SCREEN_AND_HOME = "\x1B[2J\x1B[H", MIN_RESIZE_DELTA = 2, RESIZE_SETTLE_MS = 250, MIN_CLEAR_INTERVAL_MS = 750, STABLE_WIDTH_SETTLE_MS = 180, TOOL_CALL_COMMIT_DEFER_MS = 50, ANIMATION_RESUME_HYSTERESIS_ROWS = 2, EAGER_CANCEL = true, LLM_API_ERROR_MAX_RETRIES2 = 3, CONVERSATION_BUSY_MAX_RETRIES2 = 3, CONVERSATION_BUSY_RETRY_BASE_DELAY_MS = 2500, INTERRUPT_MESSAGE = "Interrupted – tell the agent what to do differently. Something went wrong? Use /feedback to report issues.", ERROR_FEEDBACK_HINT = "Something went wrong? Use /feedback to report issues.", OPUS_BEDROCK_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to Bedrock Opus 4.5", PROVIDER_FALLBACK_HINT = "Downstream provider issues? Use /model to switch to another provider", INTERACTIVE_SLASH_COMMANDS, NON_STATE_COMMANDS, APPROVAL_OPTIONS_HEIGHT = 8, APPROVAL_PREVIEW_BUFFER = 4, MIN_WRAP_WIDTH = 10, TEXT_WRAP_GUTTER = 6, DIFF_WRAP_GUTTER = 12;
98323
99215
  var init_App2 = __esm(async () => {
98324
99216
  init_error();
98325
99217
  init_check_approval();
@@ -98341,12 +99233,10 @@ var init_App2 = __esm(async () => {
98341
99233
  init_diff2();
98342
99234
  init_errorContext();
98343
99235
  init_errorFormatter();
98344
- init_formatArgsDisplay();
98345
99236
  init_messageQueueBridge();
98346
99237
  init_pasteRegistry();
98347
99238
  init_planName();
98348
99239
  init_queuedMessageParts();
98349
- init_subagentAggregation();
98350
99240
  init_subagentState();
98351
99241
  init_thinkingMessages();
98352
99242
  init_useSyncedState();
@@ -98404,9 +99294,13 @@ var init_App2 = __esm(async () => {
98404
99294
  init_WelcomeScreen(),
98405
99295
  init_accumulator(),
98406
99296
  init_approvalClassification(),
99297
+ init_formatArgsDisplay(),
98407
99298
  init_memoryReminder(),
98408
99299
  init_sessionContext(),
98409
99300
  init_stream(),
99301
+ init_subagentAggregation(),
99302
+ init_toolNameMapping(),
99303
+ init_toolNameMapping(),
98410
99304
  init_useSuspend()
98411
99305
  ]);
98412
99306
  import_react91 = __toESM(require_react(), 1);
@@ -98466,7 +99360,7 @@ import {
98466
99360
  readFileSync as readFileSync10,
98467
99361
  writeFileSync as writeFileSync7
98468
99362
  } from "node:fs";
98469
- import { homedir as homedir17, platform as platform5 } from "node:os";
99363
+ import { homedir as homedir18, platform as platform5 } from "node:os";
98470
99364
  import { dirname as dirname14, join as join29 } from "node:path";
98471
99365
  function detectTerminalType2() {
98472
99366
  if (process.env.CURSOR_TRACE_ID || process.env.CURSOR_CHANNEL) {
@@ -98499,7 +99393,7 @@ function getKeybindingsPath2(terminal) {
98499
99393
  }[terminal];
98500
99394
  const os6 = platform5();
98501
99395
  if (os6 === "darwin") {
98502
- return join29(homedir17(), "Library", "Application Support", appName, "User", "keybindings.json");
99396
+ return join29(homedir18(), "Library", "Application Support", appName, "User", "keybindings.json");
98503
99397
  }
98504
99398
  if (os6 === "win32") {
98505
99399
  const appData = process.env.APPDATA;
@@ -98508,7 +99402,7 @@ function getKeybindingsPath2(terminal) {
98508
99402
  return join29(appData, appName, "User", "keybindings.json");
98509
99403
  }
98510
99404
  if (os6 === "linux") {
98511
- return join29(homedir17(), ".config", appName, "User", "keybindings.json");
99405
+ return join29(homedir18(), ".config", appName, "User", "keybindings.json");
98512
99406
  }
98513
99407
  return null;
98514
99408
  }
@@ -98665,10 +99559,10 @@ function getWezTermConfigPath2() {
98665
99559
  if (existsSync18(xdgPath))
98666
99560
  return xdgPath;
98667
99561
  }
98668
- const configPath = join29(homedir17(), ".config", "wezterm", "wezterm.lua");
99562
+ const configPath = join29(homedir18(), ".config", "wezterm", "wezterm.lua");
98669
99563
  if (existsSync18(configPath))
98670
99564
  return configPath;
98671
- return join29(homedir17(), ".wezterm.lua");
99565
+ return join29(homedir18(), ".wezterm.lua");
98672
99566
  }
98673
99567
  function wezTermDeleteFixExists2(configPath) {
98674
99568
  if (!existsSync18(configPath))
@@ -98766,10 +99660,10 @@ __export(exports_settings2, {
98766
99660
  loadProjectSettings: () => loadProjectSettings2,
98767
99661
  getSetting: () => getSetting2
98768
99662
  });
98769
- import { homedir as homedir18 } from "node:os";
99663
+ import { homedir as homedir19 } from "node:os";
98770
99664
  import { join as join30 } from "node:path";
98771
99665
  function getSettingsPath2() {
98772
- return join30(homedir18(), ".letta", "settings.json");
99666
+ return join30(homedir19(), ".letta", "settings.json");
98773
99667
  }
98774
99668
  async function loadSettings2() {
98775
99669
  const settingsPath = getSettingsPath2();
@@ -99278,6 +100172,7 @@ var init_create3 = __esm(async () => {
99278
100172
  // src/agent/import.ts
99279
100173
  var exports_import2 = {};
99280
100174
  __export(exports_import2, {
100175
+ importAgentFromRegistry: () => importAgentFromRegistry2,
99281
100176
  importAgentFromFile: () => importAgentFromFile2,
99282
100177
  extractSkillsFromAf: () => extractSkillsFromAf2
99283
100178
  });
@@ -99385,6 +100280,45 @@ async function downloadGitHubDirectory2(entries, destDir, owner, repo, branch, b
99385
100280
  }
99386
100281
  }
99387
100282
  }
100283
+ function parseRegistryHandle2(handle) {
100284
+ const normalized = handle.startsWith("@") ? handle.slice(1) : handle;
100285
+ const parts = normalized.split("/");
100286
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
100287
+ throw new Error(`Invalid import handle "${handle}". Use format: @author/agentname`);
100288
+ }
100289
+ return { author: parts[0], name: parts[1] };
100290
+ }
100291
+ async function importAgentFromRegistry2(options) {
100292
+ const { tmpdir: tmpdir4 } = await import("node:os");
100293
+ const { join: join31 } = await import("node:path");
100294
+ const { writeFile: writeFile6, unlink: unlink2 } = await import("node:fs/promises");
100295
+ const { author, name } = parseRegistryHandle2(options.handle);
100296
+ const rawUrl = `https://raw.githubusercontent.com/${AGENT_REGISTRY_OWNER2}/${AGENT_REGISTRY_REPO2}/refs/heads/${AGENT_REGISTRY_BRANCH2}/agents/@${author}/${name}/${name}.af`;
100297
+ const response = await fetch(rawUrl);
100298
+ if (!response.ok) {
100299
+ if (response.status === 404) {
100300
+ throw new Error(`Agent @${author}/${name} not found in registry. Check that the agent exists at https://github.com/${AGENT_REGISTRY_OWNER2}/${AGENT_REGISTRY_REPO2}/tree/${AGENT_REGISTRY_BRANCH2}/agents/@${author}/${name}`);
100301
+ }
100302
+ throw new Error(`Failed to download agent @${author}/${name}: ${response.statusText}`);
100303
+ }
100304
+ const afContent = await response.text();
100305
+ const tempPath = join31(tmpdir4(), `letta-import-${author}-${name}-${Date.now()}.af`);
100306
+ await writeFile6(tempPath, afContent, "utf-8");
100307
+ try {
100308
+ const result = await importAgentFromFile2({
100309
+ filePath: tempPath,
100310
+ modelOverride: options.modelOverride,
100311
+ stripMessages: options.stripMessages ?? true,
100312
+ stripSkills: options.stripSkills ?? false
100313
+ });
100314
+ return result;
100315
+ } finally {
100316
+ try {
100317
+ await unlink2(tempPath);
100318
+ } catch {}
100319
+ }
100320
+ }
100321
+ var AGENT_REGISTRY_OWNER2 = "letta-ai", AGENT_REGISTRY_REPO2 = "agent-file", AGENT_REGISTRY_BRANCH2 = "main";
99388
100322
  var init_import2 = __esm(async () => {
99389
100323
  init_model();
99390
100324
  await __promiseAll([
@@ -99485,7 +100419,7 @@ function buildModelSettings2(modelHandle, updateArgs) {
99485
100419
  } else {
99486
100420
  settings = {};
99487
100421
  }
99488
- if (typeof updateArgs?.max_output_tokens === "number") {
100422
+ if (typeof updateArgs?.max_output_tokens === "number" && "provider_type" in settings) {
99489
100423
  settings.max_output_tokens = updateArgs.max_output_tokens;
99490
100424
  }
99491
100425
  return settings;
@@ -99498,7 +100432,10 @@ async function updateAgentLLMConfig2(agentId, modelHandle, updateArgs) {
99498
100432
  await client.agents.update(agentId, {
99499
100433
  model: modelHandle,
99500
100434
  ...hasModelSettings && { model_settings: modelSettings },
99501
- ...contextWindow && { context_window_limit: contextWindow }
100435
+ ...contextWindow && { context_window_limit: contextWindow },
100436
+ ...typeof updateArgs?.max_output_tokens === "number" && {
100437
+ max_tokens: updateArgs.max_output_tokens
100438
+ }
99502
100439
  });
99503
100440
  const finalAgent = await client.agents.retrieve(agentId);
99504
100441
  return finalAgent.llm_config;
@@ -103326,6 +104263,8 @@ function releaseSwitchLock2() {
103326
104263
  lock.resolve = null;
103327
104264
  }
103328
104265
  }
104266
+ var EXTERNAL_TOOLS_KEY2 = Symbol.for("@letta/externalTools");
104267
+ var EXTERNAL_EXECUTOR_KEY2 = Symbol.for("@letta/externalToolExecutor");
103329
104268
  function replaceRegistry2(newTools) {
103330
104269
  toolRegistry2.clear();
103331
104270
  for (const [key, value] of newTools) {
@@ -103524,6 +104463,7 @@ OPTIONS
103524
104463
  --skills <path> Custom path to skills directory (default: .skills in current directory)
103525
104464
  --sleeptime Enable sleeptime memory management (only for new agents)
103526
104465
  --from-af <path> Create agent from an AgentFile (.af) template
104466
+ Use @author/name to import from the agent registry
103527
104467
  --memfs Enable memory filesystem for this agent
103528
104468
  --no-memfs Disable memory filesystem for this agent
103529
104469
 
@@ -103973,6 +104913,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
103973
104913
  process.exit(1);
103974
104914
  }
103975
104915
  }
104916
+ let isRegistryImport = false;
103976
104917
  if (fromAfFile) {
103977
104918
  if (specifiedAgentId) {
103978
104919
  console.error("Error: --from-af cannot be used with --agent");
@@ -103990,12 +104931,22 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
103990
104931
  console.error("Error: --from-af cannot be used with --new");
103991
104932
  process.exit(1);
103992
104933
  }
103993
- const { resolve: resolve23 } = await import("path");
103994
- const { existsSync: existsSync19 } = await import("fs");
103995
- const resolvedPath = resolve23(fromAfFile);
103996
- if (!existsSync19(resolvedPath)) {
103997
- console.error(`Error: AgentFile not found: ${resolvedPath}`);
103998
- process.exit(1);
104934
+ if (fromAfFile.startsWith("@")) {
104935
+ isRegistryImport = true;
104936
+ const normalized = fromAfFile.slice(1);
104937
+ const parts = normalized.split("/");
104938
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
104939
+ console.error(`Error: Invalid registry handle "${fromAfFile}". Use format: @author/agentname`);
104940
+ process.exit(1);
104941
+ }
104942
+ } else {
104943
+ const { resolve: resolve23 } = await import("path");
104944
+ const { existsSync: existsSync19 } = await import("fs");
104945
+ const resolvedPath = resolve23(fromAfFile);
104946
+ if (!existsSync19(resolvedPath)) {
104947
+ console.error(`Error: AgentFile not found: ${resolvedPath}`);
104948
+ process.exit(1);
104949
+ }
103999
104950
  }
104000
104951
  }
104001
104952
  if (specifiedAgentName) {
@@ -104139,7 +105090,8 @@ Error: ${message}`);
104139
105090
  systemPromptPreset: systemPromptPreset2,
104140
105091
  toolset,
104141
105092
  skillsDirectory: skillsDirectory2,
104142
- fromAfFile: fromAfFile2
105093
+ fromAfFile: fromAfFile2,
105094
+ isRegistryImport: isRegistryImport2
104143
105095
  }) {
104144
105096
  const [showKeybindingSetup, setShowKeybindingSetup] = useState45(null);
104145
105097
  const [loadingState, setLoadingState] = useState45("selecting");
@@ -104436,13 +105388,24 @@ Error: ${message}`);
104436
105388
  let isNewlyCreatedAgent = false;
104437
105389
  if (fromAfFile2) {
104438
105390
  setLoadingState("importing");
104439
- const { importAgentFromFile: importAgentFromFile3 } = await init_import2().then(() => exports_import2);
104440
- const result = await importAgentFromFile3({
104441
- filePath: fromAfFile2,
104442
- modelOverride: model,
104443
- stripMessages: true,
104444
- stripSkills: false
104445
- });
105391
+ let result;
105392
+ if (isRegistryImport2) {
105393
+ const { importAgentFromRegistry: importAgentFromRegistry3 } = await init_import2().then(() => exports_import2);
105394
+ result = await importAgentFromRegistry3({
105395
+ handle: fromAfFile2,
105396
+ modelOverride: model,
105397
+ stripMessages: true,
105398
+ stripSkills: false
105399
+ });
105400
+ } else {
105401
+ const { importAgentFromFile: importAgentFromFile3 } = await init_import2().then(() => exports_import2);
105402
+ result = await importAgentFromFile3({
105403
+ filePath: fromAfFile2,
105404
+ modelOverride: model,
105405
+ stripMessages: true,
105406
+ stripSkills: false
105407
+ });
105408
+ }
104446
105409
  agent = result.agent;
104447
105410
  isNewlyCreatedAgent = true;
104448
105411
  setAgentProvenance({
@@ -104802,11 +105765,12 @@ Error during initialization: ${message}`);
104802
105765
  systemPromptPreset,
104803
105766
  toolset: specifiedToolset,
104804
105767
  skillsDirectory,
104805
- fromAfFile
105768
+ fromAfFile,
105769
+ isRegistryImport
104806
105770
  }), {
104807
105771
  exitOnCtrlC: false
104808
105772
  });
104809
105773
  }
104810
105774
  main();
104811
105775
 
104812
- //# debugId=8B604F61633624E964756E2164756E21
105776
+ //# debugId=333508E0ED33A45564756E2164756E21