@letta-ai/letta-code 0.1.19 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +103 -0
  2. package/letta.js +1465 -476
  3. package/package.json +2 -2
package/letta.js CHANGED
@@ -67,13 +67,13 @@ var init_tslib = () => {};
67
67
 
68
68
  // node_modules/@letta-ai/letta-client/internal/utils/uuid.mjs
69
69
  var uuid4 = function() {
70
- const { crypto } = globalThis;
71
- if (crypto?.randomUUID) {
72
- uuid4 = crypto.randomUUID.bind(crypto);
73
- return crypto.randomUUID();
70
+ const { crypto: crypto2 } = globalThis;
71
+ if (crypto2?.randomUUID) {
72
+ uuid4 = crypto2.randomUUID.bind(crypto2);
73
+ return crypto2.randomUUID();
74
74
  }
75
75
  const u8 = new Uint8Array(1);
76
- const randomByte = crypto ? () => crypto.getRandomValues(u8)[0] : () => Math.random() * 255 & 255;
76
+ const randomByte = crypto2 ? () => crypto2.getRandomValues(u8)[0] : () => Math.random() * 255 & 255;
77
77
  return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, (c) => (+c ^ randomByte() & 15 >> +c / 4).toString(16));
78
78
  };
79
79
 
@@ -240,7 +240,7 @@ var init_values = __esm(() => {
240
240
  var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
241
241
 
242
242
  // node_modules/@letta-ai/letta-client/version.mjs
243
- var VERSION = "1.0.0-alpha.14";
243
+ var VERSION = "1.0.0-alpha.15";
244
244
 
245
245
  // node_modules/@letta-ai/letta-client/internal/detect-platform.mjs
246
246
  function getDetectedPlatform() {
@@ -3054,7 +3054,7 @@ async function requestDeviceCode() {
3054
3054
  }
3055
3055
  return await response.json();
3056
3056
  }
3057
- async function pollForToken(deviceCode, interval = 5, expiresIn = 900) {
3057
+ async function pollForToken(deviceCode, interval = 5, expiresIn = 900, deviceId, deviceName) {
3058
3058
  const startTime = Date.now();
3059
3059
  const expiresInMs = expiresIn * 1000;
3060
3060
  let pollInterval = interval * 1000;
@@ -3067,7 +3067,9 @@ async function pollForToken(deviceCode, interval = 5, expiresIn = 900) {
3067
3067
  body: JSON.stringify({
3068
3068
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
3069
3069
  client_id: OAUTH_CONFIG.clientId,
3070
- device_code: deviceCode
3070
+ device_code: deviceCode,
3071
+ ...deviceId && { device_id: deviceId },
3072
+ ...deviceName && { device_name: deviceName }
3071
3073
  })
3072
3074
  });
3073
3075
  const result = await response.json();
@@ -3412,6 +3414,8 @@ var init_settings_manager = __esm(() => {
3412
3414
  uiMode: "simple",
3413
3415
  lastAgent: null,
3414
3416
  tokenStreaming: false,
3417
+ parallelToolCalls: true,
3418
+ enableSleeptime: false,
3415
3419
  globalSharedBlockIds: {}
3416
3420
  };
3417
3421
  DEFAULT_PROJECT_SETTINGS = {
@@ -6715,7 +6719,7 @@ var package_default;
6715
6719
  var init_package = __esm(() => {
6716
6720
  package_default = {
6717
6721
  name: "@letta-ai/letta-code",
6718
- version: "0.1.19",
6722
+ version: "0.2.0",
6719
6723
  description: "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
6720
6724
  type: "module",
6721
6725
  bin: {
@@ -6737,7 +6741,7 @@ var init_package = __esm(() => {
6737
6741
  access: "public"
6738
6742
  },
6739
6743
  dependencies: {
6740
- "@letta-ai/letta-client": "1.0.0-alpha.14",
6744
+ "@letta-ai/letta-client": "1.0.0-alpha.15",
6741
6745
  "ink-link": "^5.0.0",
6742
6746
  open: "^10.2.0"
6743
6747
  },
@@ -31681,6 +31685,7 @@ var init_open = __esm(() => {
31681
31685
  });
31682
31686
 
31683
31687
  // src/auth/setup-ui.tsx
31688
+ import { hostname } from "os";
31684
31689
  function SetupUI({ onComplete }) {
31685
31690
  const [mode, setMode] = import_react22.useState("menu");
31686
31691
  const [selectedOption, setSelectedOption] = import_react22.useState(0);
@@ -31717,7 +31722,13 @@ function SetupUI({ onComplete }) {
31717
31722
  } catch (openErr) {
31718
31723
  console.error("Failed to auto-open browser:", openErr);
31719
31724
  }
31720
- pollForToken(deviceData.device_code, deviceData.interval, deviceData.expires_in).then((tokens) => {
31725
+ let deviceId = settingsManager.getSetting("deviceId");
31726
+ if (!deviceId) {
31727
+ deviceId = crypto.randomUUID();
31728
+ settingsManager.updateSettings({ deviceId });
31729
+ }
31730
+ const deviceName = hostname();
31731
+ pollForToken(deviceData.device_code, deviceData.interval, deviceData.expires_in, deviceId, deviceName).then((tokens) => {
31721
31732
  const now = Date.now();
31722
31733
  settingsManager.updateSettings({
31723
31734
  env: {
@@ -31929,7 +31940,7 @@ async function requestDeviceCode2() {
31929
31940
  }
31930
31941
  return await response.json();
31931
31942
  }
31932
- async function pollForToken2(deviceCode, interval = 5, expiresIn = 900) {
31943
+ async function pollForToken2(deviceCode, interval = 5, expiresIn = 900, deviceId, deviceName) {
31933
31944
  const startTime = Date.now();
31934
31945
  const expiresInMs = expiresIn * 1000;
31935
31946
  let pollInterval = interval * 1000;
@@ -31942,7 +31953,9 @@ async function pollForToken2(deviceCode, interval = 5, expiresIn = 900) {
31942
31953
  body: JSON.stringify({
31943
31954
  grant_type: "urn:ietf:params:oauth:grant-type:device_code",
31944
31955
  client_id: OAUTH_CONFIG2.clientId,
31945
- device_code: deviceCode
31956
+ device_code: deviceCode,
31957
+ ...deviceId && { device_id: deviceId },
31958
+ ...deviceName && { device_name: deviceName }
31946
31959
  })
31947
31960
  });
31948
31961
  const result = await response.json();
@@ -33727,6 +33740,10 @@ var init_matcher = __esm(() => {
33727
33740
  });
33728
33741
 
33729
33742
  // src/permissions/mode.ts
33743
+ var exports_mode = {};
33744
+ __export(exports_mode, {
33745
+ permissionMode: () => permissionMode2
33746
+ });
33730
33747
  function getGlobalMode2() {
33731
33748
  const global2 = globalThis;
33732
33749
  if (!global2[MODE_KEY2]) {
@@ -34939,6 +34956,15 @@ description: A memory block to store information about this coding project. This
34939
34956
  `;
34940
34957
  var init_project = () => {};
34941
34958
 
34959
+ // src/agent/prompts/skills.mdx
34960
+ var skills_default = `---
34961
+ label: skills
34962
+ description: A memory block to store all available Skills with their metadata (name and description). Whenever a new Skill is discovered / created or an existing Skill is updated, I should store it here. I should always check the \`.skills\` directory for an updated skill list.
34963
+ ---
34964
+
34965
+ [CURRENTLY EMPTY: CREATE A LIST OF AVAILABLE SKILLS (DIRECTORIES WITH SKILL.MD FILE) WITH DESCRIPTIONS IN THIS MEMORY BLOCK. ALWAYS CHECK THE \`.skills\` DIRECTORY TO MAKE SURE YOU HAVE AN UPDATED LIST OF SKILLS]`;
34966
+ var init_skills = () => {};
34967
+
34942
34968
  // src/agent/prompts/style.mdx
34943
34969
  var style_default = `---
34944
34970
  label: style
@@ -34975,7 +35001,21 @@ Your memory consists of memory blocks and external memory:
34975
35001
 
34976
35002
  Memory management tools allow you to edit existing memory blocks and query for external memories.
34977
35003
  Memory blocks are used to modulate and augment your base behavior, follow them closely, and maintain them cleanly.
34978
- They are the foundation which makes you *you*.`;
35004
+ They are the foundation which makes you *you*.
35005
+
35006
+ # Skills
35007
+ You have access to Skills—folders of instructions, scripts, and resources that you can load dynamically to improve performance on specialized tasks. Skills teach you how to complete specific tasks in a repeatable way. Skills work through progressive disclosure—you should determine which Skills are relevant to complete a task and load them, helping to prevent context window overload.
35008
+ Each Skill directory includes:
35009
+ - \`SKILL.md\` file that starts with YAML frontmatter containing required metadata: name and description
35010
+ - Additional files within the Skill directory referenced by name from \`SKILL.md\`. These additional linked files should be navigated and discovered only as needed.
35011
+ How to use Skills:
35012
+ - Skills are automatically discovered on bootup. The Skills directory and any available Skills are stored in the \`skills\` memory block.
35013
+ - Review available Skills from the \`skills\` block when you are asked to complete a task.
35014
+ - If a Skill is relevant, first load the Skill by reading its full \`SKILL.md\` into context.
35015
+ - Then, navigate and discover additional linked files in its directory as needed. Don't load additional files immediately, only load them when needed.
35016
+ - When you load / use Skills, explicitly mention which Skills are loaded / used.
35017
+ - When the task is completed or when you load new Skills, unload irrelevant Skills by removing them from the context.
35018
+ Remember to always keep only the full \`SKILL.md\` into context for all Skills relevant to the current task. Use additional files as needed.`;
34979
35019
  var init_system_prompt = () => {};
34980
35020
 
34981
35021
  // src/agent/promptAssets.ts
@@ -34992,6 +35032,7 @@ var init_promptAssets = __esm(() => {
34992
35032
  init_persona_kawaii();
34993
35033
  init_plan_mode_reminder();
34994
35034
  init_project();
35035
+ init_skills();
34995
35036
  init_style();
34996
35037
  init_system_prompt();
34997
35038
  SYSTEM_PROMPT = system_prompt_default;
@@ -35000,6 +35041,7 @@ var init_promptAssets = __esm(() => {
35000
35041
  "persona.mdx": persona_default,
35001
35042
  "human.mdx": human_default,
35002
35043
  "project.mdx": project_default,
35044
+ "skills.mdx": skills_default,
35003
35045
  "style.mdx": style_default,
35004
35046
  "persona_kawaii.mdx": persona_kawaii_default
35005
35047
  };
@@ -35028,7 +35070,7 @@ function parseMdxFrontmatter(content) {
35028
35070
  }
35029
35071
  async function loadMemoryBlocksFromMdx() {
35030
35072
  const memoryBlocks = [];
35031
- const mdxFiles = ["persona.mdx", "human.mdx", "project.mdx"];
35073
+ const mdxFiles = ["persona.mdx", "human.mdx", "project.mdx", "skills.mdx"];
35032
35074
  for (const filename of mdxFiles) {
35033
35075
  try {
35034
35076
  const content = MEMORY_PROMPTS[filename];
@@ -35109,15 +35151,6 @@ var init_models2 = __esm(() => {
35109
35151
  context_window: 272000
35110
35152
  }
35111
35153
  },
35112
- {
35113
- id: "glm-4.6",
35114
- handle: "openrouter/z-ai/glm-4.6:exacto",
35115
- label: "GLM-4.6",
35116
- description: "The best open weights coding model",
35117
- updateArgs: {
35118
- context_window: 200000
35119
- }
35120
- },
35121
35154
  {
35122
35155
  id: "gpt-5-minimal",
35123
35156
  handle: "openai/gpt-5",
@@ -35162,6 +35195,64 @@ var init_models2 = __esm(() => {
35162
35195
  context_window: 272000
35163
35196
  }
35164
35197
  },
35198
+ {
35199
+ id: "gpt-5-mini-medium",
35200
+ handle: "openai/gpt-5-mini-2025-08-07",
35201
+ label: "GPT-5-Mini (medium)",
35202
+ description: "OpenAI's latest mini model (using their recommended reasoning level)",
35203
+ updateArgs: {
35204
+ reasoning_effort: "medium",
35205
+ verbosity: "medium",
35206
+ context_window: 272000
35207
+ }
35208
+ },
35209
+ {
35210
+ id: "gpt-5-nano-medium",
35211
+ handle: "openai/gpt-5-nano-2025-08-07",
35212
+ label: "GPT-5-Nano (medium)",
35213
+ description: "OpenAI's latest nano model (using their recommended reasoning level)",
35214
+ updateArgs: {
35215
+ reasoning_effort: "medium",
35216
+ verbosity: "medium",
35217
+ context_window: 272000
35218
+ }
35219
+ },
35220
+ {
35221
+ id: "glm-4.6",
35222
+ handle: "openrouter/z-ai/glm-4.6:exacto",
35223
+ label: "GLM-4.6",
35224
+ description: "The best open weights coding model",
35225
+ updateArgs: {
35226
+ context_window: 200000
35227
+ }
35228
+ },
35229
+ {
35230
+ id: "minimax-m2",
35231
+ handle: "openrouter/minimax/minimax-m2",
35232
+ label: "Minimax M2",
35233
+ description: "Minimax's latest model",
35234
+ updateArgs: {
35235
+ context_window: 196000
35236
+ }
35237
+ },
35238
+ {
35239
+ id: "kimi-k2",
35240
+ handle: "openrouter/moonshotai/kimi-k2-0905",
35241
+ label: "Kimi K2",
35242
+ description: "Kimi's latest model",
35243
+ updateArgs: {
35244
+ context_window: 262144
35245
+ }
35246
+ },
35247
+ {
35248
+ id: "deepseek-chat-v3.1",
35249
+ handle: "openrouter/deepseek/deepseek-chat-v3.1",
35250
+ label: "DeepSeek Chat V3.1",
35251
+ description: "DeepSeek V3.1 model",
35252
+ updateArgs: {
35253
+ context_window: 128000
35254
+ }
35255
+ },
35165
35256
  {
35166
35257
  id: "gemini-flash",
35167
35258
  handle: "google_ai/gemini-2.5-flash",
@@ -35181,7 +35272,21 @@ var init_models2 = __esm(() => {
35181
35272
  handle: "openai/gpt-4.1",
35182
35273
  label: "GPT-4.1",
35183
35274
  description: "OpenAI's most recent non-reasoner model",
35184
- updateArgs: { context_window: 180000 }
35275
+ updateArgs: { context_window: 1047576 }
35276
+ },
35277
+ {
35278
+ id: "gpt-4.1-mini",
35279
+ handle: "openai/gpt-4.1-mini-2025-04-14",
35280
+ label: "GPT-4.1-Mini",
35281
+ description: "OpenAI's most recent non-reasoner model (mini version)",
35282
+ updateArgs: { context_window: 1047576 }
35283
+ },
35284
+ {
35285
+ id: "gpt-4.1-nano",
35286
+ handle: "openai/gpt-4.1-nano-2025-04-14",
35287
+ label: "GPT-4.1-Nano",
35288
+ description: "OpenAI's most recent non-reasoner model (nano version)",
35289
+ updateArgs: { context_window: 1047576 }
35185
35290
  },
35186
35291
  {
35187
35292
  id: "o4-mini",
@@ -35250,30 +35355,299 @@ var init_model = __esm(() => {
35250
35355
  // src/agent/modify.ts
35251
35356
  var exports_modify = {};
35252
35357
  __export(exports_modify, {
35253
- updateAgentLLMConfig: () => updateAgentLLMConfig
35358
+ updateAgentLLMConfig: () => updateAgentLLMConfig,
35359
+ unlinkToolsFromAgent: () => unlinkToolsFromAgent,
35360
+ linkToolsToAgent: () => linkToolsToAgent
35254
35361
  });
35255
- async function updateAgentLLMConfig(agentId, modelHandle, updateArgs) {
35362
+ async function updateAgentLLMConfig(agentId, modelHandle, updateArgs, preserveParallelToolCalls) {
35256
35363
  const client = await getClient2();
35257
- await client.agents.modify(agentId, { model: modelHandle });
35258
- const agent = await client.agents.retrieve(agentId);
35259
- let finalConfig = agent.llm_config;
35260
- if (updateArgs && Object.keys(updateArgs).length > 0) {
35261
- const updatedLlmConfig = {
35262
- ...finalConfig,
35263
- ...updateArgs
35364
+ const currentAgent = await client.agents.retrieve(agentId);
35365
+ const originalParallelToolCalls = preserveParallelToolCalls ? currentAgent.llm_config?.parallel_tool_calls ?? undefined : undefined;
35366
+ const updatedLlmConfig = {
35367
+ ...currentAgent.llm_config,
35368
+ ...updateArgs,
35369
+ ...originalParallelToolCalls !== undefined && {
35370
+ parallel_tool_calls: originalParallelToolCalls
35371
+ }
35372
+ };
35373
+ await client.agents.modify(agentId, {
35374
+ llm_config: updatedLlmConfig,
35375
+ parallel_tool_calls: originalParallelToolCalls
35376
+ });
35377
+ const finalAgent = await client.agents.retrieve(agentId);
35378
+ return finalAgent.llm_config;
35379
+ }
35380
+ async function linkToolsToAgent(agentId) {
35381
+ try {
35382
+ const client = await getClient2();
35383
+ const agent = await client.agents.retrieve(agentId);
35384
+ const currentTools = agent.tools || [];
35385
+ const currentToolIds = currentTools.map((t) => t.id).filter((id) => typeof id === "string");
35386
+ const currentToolNames = new Set(currentTools.map((t) => t.name).filter((name) => typeof name === "string"));
35387
+ const lettaCodeToolNames = getToolNames();
35388
+ const toolsToAdd = lettaCodeToolNames.filter((name) => !currentToolNames.has(name));
35389
+ if (toolsToAdd.length === 0) {
35390
+ return {
35391
+ success: true,
35392
+ message: "All Letta Code tools already attached",
35393
+ addedCount: 0
35394
+ };
35395
+ }
35396
+ const toolsToAddIds = [];
35397
+ for (const toolName of toolsToAdd) {
35398
+ const tools = await client.tools.list({ name: toolName });
35399
+ const tool = tools[0];
35400
+ if (tool?.id) {
35401
+ toolsToAddIds.push(tool.id);
35402
+ }
35403
+ }
35404
+ const newToolIds = [...currentToolIds, ...toolsToAddIds];
35405
+ const currentToolRules = agent.tool_rules || [];
35406
+ const newToolRules = [
35407
+ ...currentToolRules,
35408
+ ...toolsToAdd.map((toolName) => ({
35409
+ tool_name: toolName,
35410
+ type: "requires_approval",
35411
+ prompt_template: null
35412
+ }))
35413
+ ];
35414
+ await client.agents.modify(agentId, {
35415
+ tool_ids: newToolIds,
35416
+ tool_rules: newToolRules
35417
+ });
35418
+ return {
35419
+ success: true,
35420
+ message: `Attached ${toolsToAddIds.length} Letta Code tool(s) to agent`,
35421
+ addedCount: toolsToAddIds.length
35422
+ };
35423
+ } catch (error) {
35424
+ return {
35425
+ success: false,
35426
+ message: `Failed: ${error instanceof Error ? error.message : String(error)}`
35427
+ };
35428
+ }
35429
+ }
35430
+ async function unlinkToolsFromAgent(agentId) {
35431
+ try {
35432
+ const client = await getClient2();
35433
+ const agent = await client.agents.retrieve(agentId);
35434
+ const allTools = agent.tools || [];
35435
+ const lettaCodeToolNames = new Set(getToolNames());
35436
+ const remainingTools = allTools.filter((t) => t.name && !lettaCodeToolNames.has(t.name));
35437
+ const removedCount = allTools.length - remainingTools.length;
35438
+ const remainingToolIds = remainingTools.map((t) => t.id).filter((id) => typeof id === "string");
35439
+ const currentToolRules = agent.tool_rules || [];
35440
+ const remainingToolRules = currentToolRules.filter((rule) => rule.type !== "requires_approval" || !lettaCodeToolNames.has(rule.tool_name));
35441
+ await client.agents.modify(agentId, {
35442
+ tool_ids: remainingToolIds,
35443
+ tool_rules: remainingToolRules
35444
+ });
35445
+ return {
35446
+ success: true,
35447
+ message: `Removed ${removedCount} Letta Code tool(s) from agent`,
35448
+ removedCount
35449
+ };
35450
+ } catch (error) {
35451
+ return {
35452
+ success: false,
35453
+ message: `Failed: ${error instanceof Error ? error.message : String(error)}`
35264
35454
  };
35265
- await client.agents.modify(agentId, { llm_config: updatedLlmConfig });
35266
- const finalAgent = await client.agents.retrieve(agentId);
35267
- finalConfig = finalAgent.llm_config;
35268
35455
  }
35269
- return finalConfig;
35270
35456
  }
35271
35457
  var init_modify = __esm(() => {
35458
+ init_manager();
35272
35459
  init_client2();
35273
35460
  });
35274
35461
 
35462
+ // src/agent/skills.ts
35463
+ import { existsSync as existsSync3 } from "node:fs";
35464
+ import { readdir as readdir2, readFile as readFile2 } from "node:fs/promises";
35465
+ import { join as join6 } from "node:path";
35466
+ function parseFrontmatter(content) {
35467
+ const frontmatterRegex = /^---\n([\s\S]*?)\n---\n([\s\S]*)$/;
35468
+ const match2 = content.match(frontmatterRegex);
35469
+ if (!match2 || !match2[1] || !match2[2]) {
35470
+ return { frontmatter: {}, body: content };
35471
+ }
35472
+ const frontmatterText = match2[1];
35473
+ const body = match2[2];
35474
+ const frontmatter = {};
35475
+ const lines = frontmatterText.split(`
35476
+ `);
35477
+ let currentKey = null;
35478
+ let currentArray = [];
35479
+ for (const line of lines) {
35480
+ if (line.trim().startsWith("-") && currentKey) {
35481
+ const value = line.trim().slice(1).trim();
35482
+ currentArray.push(value);
35483
+ continue;
35484
+ }
35485
+ if (currentKey && currentArray.length > 0) {
35486
+ frontmatter[currentKey] = currentArray;
35487
+ currentKey = null;
35488
+ currentArray = [];
35489
+ }
35490
+ const colonIndex = line.indexOf(":");
35491
+ if (colonIndex > 0) {
35492
+ const key = line.slice(0, colonIndex).trim();
35493
+ const value = line.slice(colonIndex + 1).trim();
35494
+ currentKey = key;
35495
+ if (value) {
35496
+ frontmatter[key] = value;
35497
+ currentKey = null;
35498
+ } else {
35499
+ currentArray = [];
35500
+ }
35501
+ }
35502
+ }
35503
+ if (currentKey && currentArray.length > 0) {
35504
+ frontmatter[currentKey] = currentArray;
35505
+ }
35506
+ return { frontmatter, body: body.trim() };
35507
+ }
35508
+ async function discoverSkills(skillsPath = join6(process.cwd(), SKILLS_DIR)) {
35509
+ const errors = [];
35510
+ if (!existsSync3(skillsPath)) {
35511
+ return { skills: [], errors: [] };
35512
+ }
35513
+ const skills = [];
35514
+ try {
35515
+ await findSkillFiles(skillsPath, skillsPath, skills, errors);
35516
+ } catch (error) {
35517
+ errors.push({
35518
+ path: skillsPath,
35519
+ message: `Failed to read skills directory: ${error instanceof Error ? error.message : String(error)}`
35520
+ });
35521
+ }
35522
+ return { skills, errors };
35523
+ }
35524
+ async function findSkillFiles(currentPath, rootPath, skills, errors) {
35525
+ try {
35526
+ const entries = await readdir2(currentPath, { withFileTypes: true });
35527
+ for (const entry of entries) {
35528
+ const fullPath = join6(currentPath, entry.name);
35529
+ if (entry.isDirectory()) {
35530
+ await findSkillFiles(fullPath, rootPath, skills, errors);
35531
+ } else if (entry.isFile() && entry.name.toUpperCase() === "SKILL.MD") {
35532
+ try {
35533
+ const skill = await parseSkillFile(fullPath, rootPath);
35534
+ if (skill) {
35535
+ skills.push(skill);
35536
+ }
35537
+ } catch (error) {
35538
+ errors.push({
35539
+ path: fullPath,
35540
+ message: error instanceof Error ? error.message : String(error)
35541
+ });
35542
+ }
35543
+ }
35544
+ }
35545
+ } catch (error) {
35546
+ errors.push({
35547
+ path: currentPath,
35548
+ message: `Failed to read directory: ${error instanceof Error ? error.message : String(error)}`
35549
+ });
35550
+ }
35551
+ }
35552
+ async function parseSkillFile(filePath, rootPath) {
35553
+ const content = await readFile2(filePath, "utf-8");
35554
+ const { frontmatter, body } = parseFrontmatter(content);
35555
+ const normalizedRoot = rootPath.endsWith("/") ? rootPath.slice(0, -1) : rootPath;
35556
+ const relativePath = filePath.slice(normalizedRoot.length + 1);
35557
+ const dirPath = relativePath.slice(0, -"/SKILL.MD".length);
35558
+ const defaultId = dirPath || "root";
35559
+ const id = (typeof frontmatter.id === "string" ? frontmatter.id : null) || defaultId;
35560
+ const name = (typeof frontmatter.name === "string" ? frontmatter.name : null) || (typeof frontmatter.title === "string" ? frontmatter.title : null) || (id.split("/").pop() ?? "").replace(/-/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
35561
+ let description = typeof frontmatter.description === "string" ? frontmatter.description : null;
35562
+ if (!description) {
35563
+ const firstParagraph = body.trim().split(`
35564
+
35565
+ `)[0];
35566
+ description = firstParagraph || "No description available";
35567
+ }
35568
+ description = description.trim();
35569
+ if (description.startsWith('"') && description.endsWith('"') || description.startsWith("'") && description.endsWith("'")) {
35570
+ description = description.slice(1, -1);
35571
+ }
35572
+ let tags;
35573
+ if (Array.isArray(frontmatter.tags)) {
35574
+ tags = frontmatter.tags;
35575
+ } else if (typeof frontmatter.tags === "string") {
35576
+ tags = [frontmatter.tags];
35577
+ }
35578
+ return {
35579
+ id,
35580
+ name,
35581
+ description,
35582
+ category: typeof frontmatter.category === "string" ? frontmatter.category : undefined,
35583
+ tags,
35584
+ path: filePath
35585
+ };
35586
+ }
35587
+ function formatSkillsForMemory(skills, skillsDirectory) {
35588
+ let output = `Skills Directory: ${skillsDirectory}
35589
+
35590
+ `;
35591
+ if (skills.length === 0) {
35592
+ return `${output}[NO SKILLS AVAILABLE]`;
35593
+ }
35594
+ output += `Available Skills:
35595
+
35596
+ `;
35597
+ const categorized = new Map;
35598
+ const uncategorized = [];
35599
+ for (const skill of skills) {
35600
+ if (skill.category) {
35601
+ const existing = categorized.get(skill.category) || [];
35602
+ existing.push(skill);
35603
+ categorized.set(skill.category, existing);
35604
+ } else {
35605
+ uncategorized.push(skill);
35606
+ }
35607
+ }
35608
+ for (const [category, categorySkills] of categorized) {
35609
+ output += `## ${category}
35610
+
35611
+ `;
35612
+ for (const skill of categorySkills) {
35613
+ output += formatSkill(skill);
35614
+ }
35615
+ output += `
35616
+ `;
35617
+ }
35618
+ if (uncategorized.length > 0) {
35619
+ if (categorized.size > 0) {
35620
+ output += `## Other
35621
+
35622
+ `;
35623
+ }
35624
+ for (const skill of uncategorized) {
35625
+ output += formatSkill(skill);
35626
+ }
35627
+ }
35628
+ return output.trim();
35629
+ }
35630
+ function formatSkill(skill) {
35631
+ let output = `### ${skill.name}
35632
+ `;
35633
+ output += `ID: \`${skill.id}\`
35634
+ `;
35635
+ output += `Description: ${skill.description}
35636
+ `;
35637
+ if (skill.tags && skill.tags.length > 0) {
35638
+ output += `Tags: ${skill.tags.map((t) => `\`${t}\``).join(", ")}
35639
+ `;
35640
+ }
35641
+ output += `
35642
+ `;
35643
+ return output;
35644
+ }
35645
+ var SKILLS_DIR = ".skills";
35646
+ var init_skills2 = () => {};
35647
+
35275
35648
  // src/agent/create.ts
35276
- async function createAgent(name = "letta-cli-agent", model, embeddingModel = "openai/text-embedding-3-small", updateArgs, forceNewBlocks = false) {
35649
+ import { join as join7 } from "node:path";
35650
+ async function createAgent(name = "letta-cli-agent", model, embeddingModel = "openai/text-embedding-3-small", updateArgs, forceNewBlocks = false, skillsDirectory, parallelToolCalls = true, enableSleeptime = false) {
35277
35651
  let modelHandle;
35278
35652
  if (model) {
35279
35653
  const resolved = resolveModel(model);
@@ -35296,6 +35670,22 @@ async function createAgent(name = "letta-cli-agent", model, embeddingModel = "op
35296
35670
  "fetch_webpage"
35297
35671
  ];
35298
35672
  const defaultMemoryBlocks = await getDefaultMemoryBlocks();
35673
+ const resolvedSkillsDirectory = skillsDirectory || join7(process.cwd(), SKILLS_DIR);
35674
+ try {
35675
+ const { skills, errors } = await discoverSkills(resolvedSkillsDirectory);
35676
+ if (errors.length > 0) {
35677
+ console.warn("Errors encountered during skill discovery:");
35678
+ for (const error of errors) {
35679
+ console.warn(` ${error.path}: ${error.message}`);
35680
+ }
35681
+ }
35682
+ const skillsBlock = defaultMemoryBlocks.find((b) => b.label === "skills");
35683
+ if (skillsBlock) {
35684
+ skillsBlock.value = formatSkillsForMemory(skills, resolvedSkillsDirectory);
35685
+ }
35686
+ } catch (error) {
35687
+ console.warn(`Failed to discover skills: ${error instanceof Error ? error.message : String(error)}`);
35688
+ }
35299
35689
  const settings = settingsManager.getSettings();
35300
35690
  const globalSharedBlockIds = settings.globalSharedBlockIds;
35301
35691
  await settingsManager.loadProjectSettings();
@@ -35342,7 +35732,7 @@ async function createAgent(name = "letta-cli-agent", model, embeddingModel = "op
35342
35732
  throw new Error(`Created block ${label} has no ID`);
35343
35733
  }
35344
35734
  blockIds.push(createdBlock.id);
35345
- if (label === "project") {
35735
+ if (label === "project" || label === "skills") {
35346
35736
  newLocalBlockIds[label] = createdBlock.id;
35347
35737
  } else {
35348
35738
  newGlobalBlockIds[label] = createdBlock.id;
@@ -35368,25 +35758,31 @@ async function createAgent(name = "letta-cli-agent", model, embeddingModel = "op
35368
35758
  }
35369
35759
  }, process.cwd());
35370
35760
  }
35761
+ const modelUpdateArgs = getModelUpdateArgs(modelHandle);
35762
+ const contextWindow = modelUpdateArgs?.context_window || 200000;
35371
35763
  const agent = await client.agents.create({
35372
35764
  agent_type: "letta_v1_agent",
35373
35765
  system: SYSTEM_PROMPT,
35374
35766
  name,
35375
35767
  embedding: embeddingModel,
35376
35768
  model: modelHandle,
35377
- context_window_limit: 200000,
35769
+ context_window_limit: contextWindow,
35378
35770
  tools: toolNames,
35379
35771
  block_ids: blockIds,
35380
35772
  tags: ["origin:letta-code"],
35381
35773
  include_base_tools: false,
35382
35774
  include_base_tool_rules: false,
35383
- initial_message_sequence: []
35775
+ initial_message_sequence: [],
35776
+ parallel_tool_calls: parallelToolCalls,
35777
+ enable_sleeptime: enableSleeptime
35384
35778
  });
35385
35779
  if (updateArgs && Object.keys(updateArgs).length > 0) {
35386
- await updateAgentLLMConfig(agent.id, modelHandle, updateArgs);
35387
- return await client.agents.retrieve(agent.id);
35780
+ const { context_window, ...otherArgs } = updateArgs;
35781
+ if (Object.keys(otherArgs).length > 0) {
35782
+ await updateAgentLLMConfig(agent.id, modelHandle, otherArgs, true);
35783
+ }
35388
35784
  }
35389
- return agent;
35785
+ return await client.agents.retrieve(agent.id);
35390
35786
  }
35391
35787
  var init_create = __esm(() => {
35392
35788
  init_settings_manager();
@@ -35396,6 +35792,7 @@ var init_create = __esm(() => {
35396
35792
  init_model();
35397
35793
  init_modify();
35398
35794
  init_promptAssets();
35795
+ init_skills2();
35399
35796
  });
35400
35797
 
35401
35798
  // src/agent/message.ts
@@ -35610,6 +36007,12 @@ function onChunk(b, chunk) {
35610
36007
  if (existing && existing.kind === "reasoning") {
35611
36008
  markAsFinished(b, id);
35612
36009
  id = `${id}-tool`;
36010
+ } else if (existing && existing.kind === "tool_call") {
36011
+ if (toolCallId) {
36012
+ id = `${id}-${toolCallId.slice(-8)}`;
36013
+ } else {
36014
+ id = `${id}-${Date.now().toString(36)}`;
36015
+ }
35613
36016
  }
35614
36017
  }
35615
36018
  handleOtidTransition(b, id ?? undefined);
@@ -35621,7 +36024,7 @@ function onChunk(b, chunk) {
35621
36024
  }
35622
36025
  if (!id)
35623
36026
  break;
35624
- const desiredPhase = chunk.message_type === "approval_request_message" ? "ready" : "streaming";
36027
+ const desiredPhase = "ready";
35625
36028
  const line = ensure(b, id, () => ({
35626
36029
  kind: "tool_call",
35627
36030
  id,
@@ -35642,24 +36045,33 @@ function onChunk(b, chunk) {
35642
36045
  break;
35643
36046
  }
35644
36047
  case "tool_return_message": {
35645
- const toolCallId = chunk.tool_call_id;
35646
- const resultText = chunk.tool_return;
35647
- const status = chunk.status;
35648
- const id = toolCallId ? b.toolCallIdToLineId.get(toolCallId) : undefined;
35649
- if (!id)
35650
- break;
35651
- const line = ensure(b, id, () => ({
35652
- kind: "tool_call",
35653
- id,
35654
- phase: "finished"
35655
- }));
35656
- const updatedLine = {
35657
- ...line,
35658
- resultText,
35659
- phase: "finished",
35660
- resultOk: status === "success"
35661
- };
35662
- b.byId.set(id, updatedLine);
36048
+ const toolReturns = Array.isArray(chunk.tool_returns) && chunk.tool_returns.length > 0 ? chunk.tool_returns : chunk.tool_call_id ? [
36049
+ {
36050
+ tool_call_id: chunk.tool_call_id,
36051
+ status: chunk.status,
36052
+ func_response: chunk.tool_return
36053
+ }
36054
+ ] : [];
36055
+ for (const toolReturn of toolReturns) {
36056
+ const toolCallId = toolReturn.tool_call_id;
36057
+ const resultText = ("func_response" in toolReturn ? toolReturn.func_response : undefined) || ("tool_return" in toolReturn ? toolReturn.tool_return : undefined) || "";
36058
+ const status = toolReturn.status;
36059
+ const id = toolCallId ? b.toolCallIdToLineId.get(toolCallId) : undefined;
36060
+ if (!id)
36061
+ continue;
36062
+ const line = ensure(b, id, () => ({
36063
+ kind: "tool_call",
36064
+ id,
36065
+ phase: "finished"
36066
+ }));
36067
+ const updatedLine = {
36068
+ ...line,
36069
+ resultText,
36070
+ phase: "finished",
36071
+ resultOk: status === "success"
36072
+ };
36073
+ b.byId.set(id, updatedLine);
36074
+ }
35663
36075
  break;
35664
36076
  }
35665
36077
  case "usage_statistics": {
@@ -35711,18 +36123,11 @@ function safeJsonParseOr(json, defaultValue) {
35711
36123
  // src/cli/helpers/stream.ts
35712
36124
  async function drainStream(stream, buffers, refresh, abortSignal) {
35713
36125
  const startTime = performance.now();
35714
- let approvalRequestId = null;
35715
- let toolCallId = null;
35716
- let toolName = null;
35717
- let toolArgs = null;
36126
+ let _approvalRequestId = null;
36127
+ const pendingApprovals = new Map;
35718
36128
  let stopReason = null;
35719
36129
  let lastRunId = null;
35720
36130
  let lastSeqId = null;
35721
- const resetToolState = () => {
35722
- toolCallId = null;
35723
- toolName = null;
35724
- toolArgs = null;
35725
- };
35726
36131
  for await (const chunk of stream) {
35727
36132
  if (abortSignal?.aborted) {
35728
36133
  stopReason = "cancelled";
@@ -35737,28 +36142,30 @@ async function drainStream(stream, buffers, refresh, abortSignal) {
35737
36142
  if (chunk.message_type === "ping")
35738
36143
  continue;
35739
36144
  if (chunk.message_type === "tool_return_message") {
35740
- resetToolState();
36145
+ if (chunk.tool_call_id) {
36146
+ pendingApprovals.delete(chunk.tool_call_id);
36147
+ }
35741
36148
  }
35742
36149
  if (chunk.message_type === "approval_request_message") {
35743
- approvalRequestId = chunk.id;
36150
+ _approvalRequestId = chunk.id;
35744
36151
  }
35745
- if (chunk.message_type === "tool_call_message" || chunk.message_type === "approval_request_message") {
35746
- const toolCall = chunk.tool_call || (Array.isArray(chunk.tool_calls) && chunk.tool_calls.length > 0 ? chunk.tool_calls[0] : null);
35747
- if (toolCall?.tool_call_id) {
35748
- if (toolCallId && toolCall.tool_call_id !== toolCallId) {
35749
- resetToolState();
36152
+ if (chunk.message_type === "approval_request_message") {
36153
+ const toolCalls = Array.isArray(chunk.tool_calls) ? chunk.tool_calls : chunk.tool_call ? [chunk.tool_call] : [];
36154
+ for (const toolCall of toolCalls) {
36155
+ if (!toolCall?.tool_call_id)
36156
+ continue;
36157
+ const existing = pendingApprovals.get(toolCall.tool_call_id) || {
36158
+ toolCallId: toolCall.tool_call_id,
36159
+ toolName: "",
36160
+ toolArgs: ""
36161
+ };
36162
+ if (toolCall.name) {
36163
+ existing.toolName = toolCall.name;
35750
36164
  }
35751
- toolCallId = toolCall.tool_call_id;
35752
- }
35753
- if (toolCall?.name) {
35754
- toolName = toolCall.name;
35755
- }
35756
- if (toolCall?.arguments) {
35757
- if (toolArgs) {
35758
- toolArgs = toolArgs + toolCall.arguments;
35759
- } else {
35760
- toolArgs = toolCall.arguments;
36165
+ if (toolCall.arguments) {
36166
+ existing.toolArgs += toolCall.arguments;
35761
36167
  }
36168
+ pendingApprovals.set(toolCall.tool_call_id, existing);
35762
36169
  }
35763
36170
  }
35764
36171
  onChunk(buffers, chunk);
@@ -35776,26 +36183,28 @@ async function drainStream(stream, buffers, refresh, abortSignal) {
35776
36183
  markCurrentLineAsFinished(buffers);
35777
36184
  queueMicrotask(refresh);
35778
36185
  let approval = null;
36186
+ let approvals = [];
35779
36187
  if (stopReason === "requires_approval") {
35780
- if (!toolCallId || !toolName || !toolArgs || !approvalRequestId) {
35781
- console.error("[drainStream] Incomplete approval state at end of turn:", {
35782
- hasToolCallId: !!toolCallId,
35783
- hasToolName: !!toolName,
35784
- hasToolArgs: !!toolArgs,
35785
- hasApprovalRequestId: !!approvalRequestId
35786
- });
36188
+ const allPending = Array.from(pendingApprovals.values());
36189
+ approvals = allPending.filter((a) => a.toolCallId && a.toolName && a.toolArgs);
36190
+ if (approvals.length === 0) {
36191
+ console.error("[drainStream] No valid approvals collected despite requires_approval stop reason");
36192
+ console.error("[drainStream] Pending approvals map:", allPending);
35787
36193
  } else {
35788
- approval = {
35789
- toolCallId,
35790
- toolName,
35791
- toolArgs
35792
- };
36194
+ approval = approvals[0] || null;
35793
36195
  }
35794
- resetToolState();
35795
- approvalRequestId = null;
36196
+ pendingApprovals.clear();
36197
+ _approvalRequestId = null;
35796
36198
  }
35797
36199
  const apiDurationMs = performance.now() - startTime;
35798
- return { stopReason, approval, lastRunId, lastSeqId, apiDurationMs };
36200
+ return {
36201
+ stopReason,
36202
+ approval,
36203
+ approvals,
36204
+ lastRunId,
36205
+ lastSeqId,
36206
+ apiDurationMs
36207
+ };
35799
36208
  }
35800
36209
  async function drainStreamWithResume(stream, buffers, refresh, abortSignal) {
35801
36210
  const overallStartTime = performance.now();
@@ -35828,11 +36237,19 @@ async function getResumeData2(client, agent) {
35828
36237
  const messagesPage = await client.agents.messages.list(agent.id);
35829
36238
  const messages = messagesPage.items;
35830
36239
  if (!messages || messages.length === 0) {
35831
- return { pendingApproval: null, messageHistory: [] };
36240
+ return {
36241
+ pendingApproval: null,
36242
+ pendingApprovals: [],
36243
+ messageHistory: []
36244
+ };
35832
36245
  }
35833
36246
  const cursorLastMessage = messages[messages.length - 1];
35834
36247
  if (!cursorLastMessage) {
35835
- return { pendingApproval: null, messageHistory: [] };
36248
+ return {
36249
+ pendingApproval: null,
36250
+ pendingApprovals: [],
36251
+ messageHistory: []
36252
+ };
35836
36253
  }
35837
36254
  const inContextLastMessageId = agent.message_ids && agent.message_ids.length > 0 ? agent.message_ids[agent.message_ids.length - 1] : null;
35838
36255
  let messageToCheck = cursorLastMessage;
@@ -35853,18 +36270,17 @@ async function getResumeData2(client, agent) {
35853
36270
  }
35854
36271
  }
35855
36272
  let pendingApproval = null;
36273
+ let pendingApprovals = [];
35856
36274
  if (messageToCheck.message_type === "approval_request_message") {
35857
36275
  const approvalMsg = messageToCheck;
35858
36276
  const toolCalls = Array.isArray(approvalMsg.tool_calls) ? approvalMsg.tool_calls : approvalMsg.tool_call ? [approvalMsg.tool_call] : [];
35859
- if (toolCalls.length > 0) {
35860
- const toolCall = toolCalls[0];
35861
- if (toolCall?.tool_call_id && toolCall.name && toolCall.arguments) {
35862
- pendingApproval = {
35863
- toolCallId: toolCall.tool_call_id,
35864
- toolName: toolCall.name,
35865
- toolArgs: toolCall.arguments
35866
- };
35867
- }
36277
+ pendingApprovals = toolCalls.filter((tc) => tc?.tool_call_id && tc.name && tc.arguments).map((tc) => ({
36278
+ toolCallId: tc.tool_call_id,
36279
+ toolName: tc.name,
36280
+ toolArgs: tc.arguments
36281
+ }));
36282
+ if (pendingApprovals.length > 0) {
36283
+ pendingApproval = pendingApprovals[0] || null;
35868
36284
  }
35869
36285
  }
35870
36286
  const historyCount = Math.min(MESSAGE_HISTORY_LIMIT2, messages.length);
@@ -35872,21 +36288,97 @@ async function getResumeData2(client, agent) {
35872
36288
  if (messageHistory[0]?.message_type === "tool_return_message") {
35873
36289
  messageHistory = messageHistory.slice(1);
35874
36290
  }
35875
- return { pendingApproval, messageHistory };
36291
+ return { pendingApproval, pendingApprovals, messageHistory };
35876
36292
  } catch (error) {
35877
36293
  console.error("Error getting resume data:", error);
35878
- return { pendingApproval: null, messageHistory: [] };
36294
+ return { pendingApproval: null, pendingApprovals: [], messageHistory: [] };
35879
36295
  }
35880
36296
  }
35881
36297
  var MESSAGE_HISTORY_LIMIT2 = 15;
35882
36298
 
36299
+ // src/agent/approval-execution.ts
36300
+ var exports_approval_execution = {};
36301
+ __export(exports_approval_execution, {
36302
+ executeApprovalBatch: () => executeApprovalBatch
36303
+ });
36304
+ async function executeApprovalBatch(decisions, onChunk2) {
36305
+ const results = [];
36306
+ for (const decision of decisions) {
36307
+ if (decision.type === "approve") {
36308
+ try {
36309
+ const parsedArgs = typeof decision.approval.toolArgs === "string" ? JSON.parse(decision.approval.toolArgs) : decision.approval.toolArgs || {};
36310
+ const toolResult = await executeTool(decision.approval.toolName, parsedArgs);
36311
+ if (onChunk2) {
36312
+ onChunk2({
36313
+ message_type: "tool_return_message",
36314
+ id: "dummy",
36315
+ date: new Date().toISOString(),
36316
+ tool_call_id: decision.approval.toolCallId,
36317
+ tool_return: toolResult.toolReturn,
36318
+ status: toolResult.status,
36319
+ stdout: toolResult.stdout,
36320
+ stderr: toolResult.stderr
36321
+ });
36322
+ }
36323
+ results.push({
36324
+ type: "tool",
36325
+ tool_call_id: decision.approval.toolCallId,
36326
+ tool_return: toolResult.toolReturn,
36327
+ status: toolResult.status,
36328
+ stdout: toolResult.stdout,
36329
+ stderr: toolResult.stderr
36330
+ });
36331
+ } catch (e) {
36332
+ const errorMessage = `Error executing tool: ${String(e)}`;
36333
+ if (onChunk2) {
36334
+ onChunk2({
36335
+ message_type: "tool_return_message",
36336
+ id: "dummy",
36337
+ date: new Date().toISOString(),
36338
+ tool_call_id: decision.approval.toolCallId,
36339
+ tool_return: errorMessage,
36340
+ status: "error"
36341
+ });
36342
+ }
36343
+ results.push({
36344
+ type: "tool",
36345
+ tool_call_id: decision.approval.toolCallId,
36346
+ tool_return: errorMessage,
36347
+ status: "error"
36348
+ });
36349
+ }
36350
+ } else {
36351
+ if (onChunk2) {
36352
+ onChunk2({
36353
+ message_type: "tool_return_message",
36354
+ id: "dummy",
36355
+ date: new Date().toISOString(),
36356
+ tool_call_id: decision.approval.toolCallId,
36357
+ tool_return: `Error: request to call tool denied. User reason: ${decision.reason}`,
36358
+ status: "error"
36359
+ });
36360
+ }
36361
+ results.push({
36362
+ type: "approval",
36363
+ tool_call_id: decision.approval.toolCallId,
36364
+ approve: false,
36365
+ reason: decision.reason
36366
+ });
36367
+ }
36368
+ }
36369
+ return results;
36370
+ }
36371
+ var init_approval_execution = __esm(() => {
36372
+ init_manager();
36373
+ });
36374
+
35883
36375
  // src/headless.ts
35884
36376
  var exports_headless = {};
35885
36377
  __export(exports_headless, {
35886
36378
  handleHeadlessCommand: () => handleHeadlessCommand
35887
36379
  });
35888
36380
  import { parseArgs } from "node:util";
35889
- async function handleHeadlessCommand(argv, model) {
36381
+ async function handleHeadlessCommand(argv, model, skillsDirectory) {
35890
36382
  const settings = settingsManager.getSettings();
35891
36383
  const { values, positionals } = parseArgs({
35892
36384
  args: argv,
@@ -35896,7 +36388,18 @@ async function handleHeadlessCommand(argv, model) {
35896
36388
  agent: { type: "string", short: "a" },
35897
36389
  model: { type: "string", short: "m" },
35898
36390
  prompt: { type: "boolean", short: "p" },
35899
- "output-format": { type: "string" }
36391
+ "output-format": { type: "string" },
36392
+ help: { type: "boolean", short: "h" },
36393
+ version: { type: "boolean", short: "v" },
36394
+ run: { type: "boolean" },
36395
+ tools: { type: "string" },
36396
+ allowedTools: { type: "string" },
36397
+ disallowedTools: { type: "string" },
36398
+ "permission-mode": { type: "string" },
36399
+ yolo: { type: "boolean" },
36400
+ skills: { type: "string" },
36401
+ link: { type: "boolean" },
36402
+ unlink: { type: "boolean" }
35900
36403
  },
35901
36404
  strict: false,
35902
36405
  allowPositionals: true
@@ -35929,7 +36432,7 @@ async function handleHeadlessCommand(argv, model) {
35929
36432
  }
35930
36433
  if (!agent && forceNew) {
35931
36434
  const updateArgs = getModelUpdateArgs(model);
35932
- agent = await createAgent(undefined, model, undefined, updateArgs, forceNew);
36435
+ agent = await createAgent(undefined, model, undefined, updateArgs, forceNew, skillsDirectory, settings.parallelToolCalls, settings.enableSleeptime);
35933
36436
  }
35934
36437
  if (!agent) {
35935
36438
  await settingsManager.loadLocalProjectSettings();
@@ -35951,7 +36454,7 @@ async function handleHeadlessCommand(argv, model) {
35951
36454
  }
35952
36455
  if (!agent) {
35953
36456
  const updateArgs = getModelUpdateArgs(model);
35954
- agent = await createAgent(undefined, model, undefined, updateArgs);
36457
+ agent = await createAgent(undefined, model, undefined, updateArgs, false, skillsDirectory, settings.parallelToolCalls, settings.enableSleeptime);
35955
36458
  }
35956
36459
  await settingsManager.loadLocalProjectSettings();
35957
36460
  settingsManager.updateLocalProjectSettings({ lastAgent: agent.id });
@@ -35977,58 +36480,57 @@ async function handleHeadlessCommand(argv, model) {
35977
36480
  while (true) {
35978
36481
  const freshAgent = await client.agents.retrieve(agent.id);
35979
36482
  const resume = await getResumeData3(client, freshAgent);
35980
- if (!resume.pendingApproval)
36483
+ const pendingApprovals = resume.pendingApprovals || [];
36484
+ if (pendingApprovals.length === 0)
35981
36485
  break;
35982
- const { toolCallId, toolName, toolArgs } = resume.pendingApproval;
35983
- const parsedArgs = safeJsonParseOr(toolArgs || "{}", {});
35984
- const permission = await checkToolPermission(toolName, parsedArgs);
35985
- let approvalInput;
35986
- if (permission.decision === "deny" || permission.decision === "ask") {
35987
- const denyReason = permission.decision === "ask" ? "Tool requires approval (headless mode)" : `Permission denied: ${permission.matchedRule || permission.reason}`;
35988
- approvalInput = {
35989
- type: "approval",
35990
- approval_request_id: toolCallId,
35991
- approve: false,
35992
- reason: denyReason
35993
- };
35994
- } else {
36486
+ const decisions = [];
36487
+ for (const currentApproval of pendingApprovals) {
36488
+ const { toolCallId, toolName, toolArgs } = currentApproval;
36489
+ const parsedArgs = safeJsonParseOr(toolArgs || "{}", {});
36490
+ const permission = await checkToolPermission(toolName, parsedArgs);
36491
+ if (permission.decision === "deny" || permission.decision === "ask") {
36492
+ const denyReason = permission.decision === "ask" ? "Tool requires approval (headless mode)" : `Permission denied: ${permission.matchedRule || permission.reason}`;
36493
+ decisions.push({
36494
+ type: "deny",
36495
+ approval: currentApproval,
36496
+ reason: denyReason
36497
+ });
36498
+ continue;
36499
+ }
35995
36500
  const { getToolSchema: getToolSchema2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
35996
36501
  const schema = getToolSchema2(toolName);
35997
36502
  const required = schema?.input_schema?.required || [];
35998
36503
  const missing = required.filter((key) => !(key in parsedArgs) || String(parsedArgs[key] ?? "").length === 0);
35999
36504
  if (missing.length > 0) {
36000
- approvalInput = {
36001
- type: "approval",
36002
- approval_request_id: toolCallId,
36003
- approve: false,
36505
+ decisions.push({
36506
+ type: "deny",
36507
+ approval: currentApproval,
36004
36508
  reason: `Missing required parameter${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`
36005
- };
36006
- } else {
36007
- const toolResult = await executeTool(toolName, parsedArgs);
36008
- if (outputFormat === "stream-json") {
36509
+ });
36510
+ continue;
36511
+ }
36512
+ decisions.push({
36513
+ type: "approve",
36514
+ approval: currentApproval
36515
+ });
36516
+ }
36517
+ const { executeApprovalBatch: executeApprovalBatch2 } = await Promise.resolve().then(() => (init_approval_execution(), exports_approval_execution));
36518
+ if (outputFormat === "stream-json") {
36519
+ for (const decision of decisions) {
36520
+ if (decision.type === "approve") {
36009
36521
  console.log(JSON.stringify({
36010
36522
  type: "auto_approval",
36011
- tool_name: toolName,
36012
- tool_call_id: toolCallId,
36013
- reason: permission.reason,
36014
- matched_rule: permission.matchedRule
36523
+ tool_name: decision.approval.toolName,
36524
+ tool_call_id: decision.approval.toolCallId
36015
36525
  }));
36016
36526
  }
36017
- approvalInput = {
36018
- type: "approval",
36019
- approvals: [
36020
- {
36021
- type: "tool",
36022
- tool_call_id: toolCallId,
36023
- tool_return: toolResult.toolReturn,
36024
- status: toolResult.status,
36025
- stdout: toolResult.stdout,
36026
- stderr: toolResult.stderr
36027
- }
36028
- ]
36029
- };
36030
36527
  }
36031
36528
  }
36529
+ const executedResults = await executeApprovalBatch2(decisions);
36530
+ const approvalInput = {
36531
+ type: "approval",
36532
+ approvals: executedResults
36533
+ };
36032
36534
  const approvalStream = await sendMessageStream(agent.id, [approvalInput]);
36033
36535
  if (outputFormat === "stream-json") {
36034
36536
  for await (const _ of approvalStream) {}
@@ -36038,17 +36540,23 @@ async function handleHeadlessCommand(argv, model) {
36038
36540
  }
36039
36541
  };
36040
36542
  await resolveAllPendingApprovals();
36543
+ const { permissionMode: permissionMode3 } = await Promise.resolve().then(() => (init_mode(), exports_mode));
36544
+ let messageContent = prompt;
36545
+ if (permissionMode3.getMode() === "plan") {
36546
+ const { PLAN_MODE_REMINDER: PLAN_MODE_REMINDER2 } = await Promise.resolve().then(() => (init_promptAssets(), exports_promptAssets));
36547
+ messageContent = PLAN_MODE_REMINDER2 + prompt;
36548
+ }
36041
36549
  let currentInput = [
36042
36550
  {
36043
36551
  role: "user",
36044
- content: [{ type: "text", text: prompt }]
36552
+ content: [{ type: "text", text: messageContent }]
36045
36553
  }
36046
36554
  ];
36047
36555
  try {
36048
36556
  while (true) {
36049
36557
  const stream = await sendMessageStream(agent.id, currentInput);
36050
36558
  let stopReason;
36051
- let approval = null;
36559
+ let approvals = [];
36052
36560
  let apiDurationMs;
36053
36561
  let lastRunId = null;
36054
36562
  if (outputFormat === "stream-json") {
@@ -36091,40 +36599,47 @@ async function handleHeadlessCommand(argv, model) {
36091
36599
  const chunkWithTools = chunk;
36092
36600
  const toolCalls = Array.isArray(chunkWithTools.tool_calls) ? chunkWithTools.tool_calls : chunkWithTools.tool_call ? [chunkWithTools.tool_call] : [];
36093
36601
  for (const toolCall of toolCalls) {
36094
- if (toolCall?.tool_call_id && toolCall?.name) {
36095
- const id = toolCall.tool_call_id;
36096
- _lastApprovalId = id;
36097
- const prev = approvalRequests.get(id);
36098
- const base2 = prev && prev.args !== "{}" ? prev.args : "";
36099
- const incomingArgs = toolCall.arguments && toolCall.arguments.trim().length > 0 ? `${base2}${toolCall.arguments}` : base2 || "{}";
36100
- approvalRequests.set(id, {
36101
- toolName: toolCall.name,
36102
- args: incomingArgs
36103
- });
36104
- approval = {
36105
- toolCallId: id,
36106
- toolName: toolCall.name,
36107
- toolArgs: incomingArgs
36108
- };
36109
- if (!autoApprovalEmitted.has(id)) {
36110
- const parsedArgs = safeJsonParseOr(incomingArgs || "{}", null);
36111
- const permission = await checkToolPermission(toolCall.name, parsedArgs || {});
36112
- if (permission.decision === "allow" && parsedArgs) {
36113
- const { getToolSchema: getToolSchema2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
36114
- const schema = getToolSchema2(toolCall.name);
36115
- const required = schema?.input_schema?.required || [];
36116
- const missing = required.filter((key) => !(key in parsedArgs) || String(parsedArgs[key] ?? "").length === 0);
36117
- if (missing.length === 0) {
36118
- shouldOutputChunk = false;
36119
- console.log(JSON.stringify({
36120
- type: "auto_approval",
36121
- tool_name: toolCall.name,
36122
- tool_call_id: id,
36123
- reason: permission.reason,
36124
- matched_rule: permission.matchedRule
36125
- }));
36126
- autoApprovalEmitted.add(id);
36127
- }
36602
+ const id = toolCall?.tool_call_id;
36603
+ if (!id)
36604
+ continue;
36605
+ _lastApprovalId = id;
36606
+ const prev = approvalRequests.get(id);
36607
+ const base2 = prev?.args ?? "";
36608
+ const incomingArgs = toolCall?.arguments && toolCall.arguments.trim().length > 0 ? base2 + toolCall.arguments : base2;
36609
+ const nextName = toolCall?.name || prev?.toolName || "";
36610
+ approvalRequests.set(id, {
36611
+ toolName: nextName,
36612
+ args: incomingArgs
36613
+ });
36614
+ const existingIndex = approvals.findIndex((a) => a.toolCallId === id);
36615
+ const approvalObj = {
36616
+ toolCallId: id,
36617
+ toolName: nextName,
36618
+ toolArgs: incomingArgs
36619
+ };
36620
+ if (existingIndex >= 0) {
36621
+ approvals[existingIndex] = approvalObj;
36622
+ } else {
36623
+ approvals.push(approvalObj);
36624
+ }
36625
+ if (!autoApprovalEmitted.has(id) && nextName) {
36626
+ const parsedArgs = safeJsonParseOr(incomingArgs || "{}", null);
36627
+ const permission = await checkToolPermission(nextName, parsedArgs || {});
36628
+ if (permission.decision === "allow" && parsedArgs) {
36629
+ const { getToolSchema: getToolSchema2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
36630
+ const schema = getToolSchema2(nextName);
36631
+ const required = schema?.input_schema?.required || [];
36632
+ const missing = required.filter((key) => !(key in parsedArgs) || String(parsedArgs[key] ?? "").length === 0);
36633
+ if (missing.length === 0) {
36634
+ shouldOutputChunk = false;
36635
+ console.log(JSON.stringify({
36636
+ type: "auto_approval",
36637
+ tool_name: nextName,
36638
+ tool_call_id: id,
36639
+ reason: permission.reason,
36640
+ matched_rule: permission.matchedRule
36641
+ }));
36642
+ autoApprovalEmitted.add(id);
36128
36643
  }
36129
36644
  }
36130
36645
  }
@@ -36150,7 +36665,7 @@ async function handleHeadlessCommand(argv, model) {
36150
36665
  } else {
36151
36666
  const result = await drainStreamWithResume(stream, buffers, () => {});
36152
36667
  stopReason = result.stopReason;
36153
- approval = result.approval || null;
36668
+ approvals = result.approvals || [];
36154
36669
  apiDurationMs = result.apiDurationMs;
36155
36670
  lastRunId = result.lastRunId || null;
36156
36671
  }
@@ -36159,65 +36674,55 @@ async function handleHeadlessCommand(argv, model) {
36159
36674
  break;
36160
36675
  }
36161
36676
  if (stopReason === "requires_approval") {
36162
- if (!approval) {
36163
- console.error("Unexpected null approval");
36677
+ if (approvals.length === 0) {
36678
+ console.error("Unexpected empty approvals array");
36164
36679
  process.exit(1);
36165
36680
  }
36166
- const { toolCallId, toolName, toolArgs } = approval;
36167
- const parsedArgs = safeJsonParseOr(toolArgs, {});
36168
- const permission = await checkToolPermission(toolName, parsedArgs);
36169
- if (permission.decision === "deny") {
36170
- const denyReason = `Permission denied: ${permission.matchedRule || permission.reason}`;
36171
- currentInput = [
36172
- {
36173
- type: "approval",
36174
- approval_request_id: toolCallId,
36175
- approve: false,
36681
+ const decisions = [];
36682
+ for (const currentApproval of approvals) {
36683
+ const { toolCallId, toolName, toolArgs } = currentApproval;
36684
+ const parsedArgs = safeJsonParseOr(toolArgs, {});
36685
+ const permission = await checkToolPermission(toolName, parsedArgs);
36686
+ if (permission.decision === "deny") {
36687
+ const denyReason = `Permission denied: ${permission.matchedRule || permission.reason}`;
36688
+ decisions.push({
36689
+ type: "deny",
36690
+ approval: currentApproval,
36176
36691
  reason: denyReason
36177
- }
36178
- ];
36179
- continue;
36180
- }
36181
- if (permission.decision === "ask") {
36182
- currentInput = [
36183
- {
36184
- type: "approval",
36185
- approval_request_id: toolCallId,
36186
- approve: false,
36692
+ });
36693
+ continue;
36694
+ }
36695
+ if (permission.decision === "ask") {
36696
+ decisions.push({
36697
+ type: "deny",
36698
+ approval: currentApproval,
36187
36699
  reason: "Tool requires approval (headless mode)"
36188
- }
36189
- ];
36190
- continue;
36191
- }
36192
- const { getToolSchema: getToolSchema2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
36193
- const schema = getToolSchema2(toolName);
36194
- const required = schema?.input_schema?.required || [];
36195
- const missing = required.filter((key) => !(key in parsedArgs) || String(parsedArgs[key] ?? "").length === 0);
36196
- if (missing.length > 0) {
36197
- currentInput = [
36198
- {
36199
- type: "approval",
36200
- approval_request_id: toolCallId,
36201
- approve: false,
36700
+ });
36701
+ continue;
36702
+ }
36703
+ const { getToolSchema: getToolSchema2 } = await Promise.resolve().then(() => (init_manager(), exports_manager));
36704
+ const schema = getToolSchema2(toolName);
36705
+ const required = schema?.input_schema?.required || [];
36706
+ const missing = required.filter((key) => !(key in parsedArgs) || String(parsedArgs[key] ?? "").length === 0);
36707
+ if (missing.length > 0) {
36708
+ decisions.push({
36709
+ type: "deny",
36710
+ approval: currentApproval,
36202
36711
  reason: `Missing required parameter${missing.length > 1 ? "s" : ""}: ${missing.join(", ")}`
36203
- }
36204
- ];
36205
- continue;
36712
+ });
36713
+ continue;
36714
+ }
36715
+ decisions.push({
36716
+ type: "approve",
36717
+ approval: currentApproval
36718
+ });
36206
36719
  }
36207
- const toolResult = await executeTool(toolName, parsedArgs);
36720
+ const { executeApprovalBatch: executeApprovalBatch2 } = await Promise.resolve().then(() => (init_approval_execution(), exports_approval_execution));
36721
+ const executedResults = await executeApprovalBatch2(decisions);
36208
36722
  currentInput = [
36209
36723
  {
36210
36724
  type: "approval",
36211
- approvals: [
36212
- {
36213
- type: "tool",
36214
- tool_call_id: toolCallId,
36215
- tool_return: toolResult.toolReturn,
36216
- status: toolResult.status,
36217
- stdout: toolResult.stdout,
36218
- stderr: toolResult.stderr
36219
- }
36220
- ]
36725
+ approvals: executedResults
36221
36726
  }
36222
36727
  ];
36223
36728
  continue;
@@ -37457,15 +37962,15 @@ var init_colors = __esm(() => {
37457
37962
  header: brandColors.primaryAccent
37458
37963
  },
37459
37964
  code: {
37460
- inline: brandColors.statusSuccess
37965
+ inline: "green"
37461
37966
  },
37462
37967
  link: {
37463
- text: brandColors.primaryAccent,
37464
- url: brandColors.primaryAccent
37968
+ text: "cyan",
37969
+ url: "blue"
37465
37970
  },
37466
37971
  heading: {
37467
- primary: brandColors.primaryAccent,
37468
- secondary: brandColors.blue
37972
+ primary: "cyan",
37973
+ secondary: "blue"
37469
37974
  },
37470
37975
  status: {
37471
37976
  error: brandColors.statusError,
@@ -38114,7 +38619,7 @@ var init_build4 = __esm(async () => {
38114
38619
 
38115
38620
  // src/cli/helpers/clipboard.ts
38116
38621
  import { execFileSync as execFileSync2 } from "node:child_process";
38117
- import { existsSync as existsSync3, readFileSync as readFileSync2, statSync } from "node:fs";
38622
+ import { existsSync as existsSync4, readFileSync as readFileSync2, statSync } from "node:fs";
38118
38623
  import { basename as basename3, extname, isAbsolute as isAbsolute7, resolve as resolve7 } from "node:path";
38119
38624
  function countLines2(text) {
38120
38625
  return (text.match(/\r\n|\r|\n/g) || []).length + 1;
@@ -38165,7 +38670,7 @@ function translatePasteForImages(paste) {
38165
38670
  if (!isAbsolute7(filePath))
38166
38671
  filePath = resolve7(process.cwd(), filePath);
38167
38672
  const ext2 = extname(filePath || "").toLowerCase();
38168
- if (IMAGE_EXTS.has(ext2) && existsSync3(filePath) && statSync(filePath).isFile()) {
38673
+ if (IMAGE_EXTS.has(ext2) && existsSync4(filePath) && statSync(filePath).isFile()) {
38169
38674
  const buf = readFileSync2(filePath);
38170
38675
  const b64 = buf.toString("base64");
38171
38676
  const mt = ext2 === ".png" ? "image/png" : ext2 === ".jpg" || ext2 === ".jpeg" ? "image/jpeg" : ext2 === ".gif" ? "image/gif" : ext2 === ".webp" ? "image/webp" : ext2 === ".bmp" ? "image/bmp" : ext2 === ".svg" ? "image/svg+xml" : ext2 === ".tif" || ext2 === ".tiff" ? "image/tiff" : ext2 === ".heic" ? "image/heic" : ext2 === ".heif" ? "image/heif" : ext2 === ".avif" ? "image/avif" : "application/octet-stream";
@@ -38640,34 +39145,46 @@ var init_ApprovalDialogRich = __esm(async () => {
38640
39145
  });
38641
39146
  OptionsRenderer.displayName = "OptionsRenderer";
38642
39147
  ApprovalDialog = import_react28.memo(function ApprovalDialog2({
38643
- approvalRequest,
38644
- approvalContext,
38645
- onApprove,
39148
+ approvals,
39149
+ approvalContexts,
39150
+ progress,
39151
+ totalTools,
39152
+ isExecuting,
39153
+ onApproveAll,
38646
39154
  onApproveAlways,
38647
- onDeny
39155
+ onDenyAll
38648
39156
  }) {
38649
39157
  const [selectedOption, setSelectedOption] = import_react28.useState(0);
38650
39158
  const [isEnteringReason, setIsEnteringReason] = import_react28.useState(false);
38651
39159
  const [denyReason, setDenyReason] = import_react28.useState("");
39160
+ const approvalRequest = approvals[0];
39161
+ const approvalContext = approvalContexts[0] || null;
39162
+ import_react28.useEffect(() => {
39163
+ setSelectedOption(0);
39164
+ setIsEnteringReason(false);
39165
+ setDenyReason("");
39166
+ }, [progress?.current]);
38652
39167
  const options = import_react28.useMemo(() => {
38653
- const opts = [{ label: "Yes, just this once", action: onApprove }];
39168
+ const approvalLabel = progress && progress.total > 1 ? "Yes, approve this tool" : "Yes, just this once";
39169
+ const opts = [{ label: approvalLabel, action: onApproveAll }];
38654
39170
  if (approvalContext?.allowPersistence) {
38655
39171
  opts.push({
38656
39172
  label: approvalContext.approveAlwaysText,
38657
39173
  action: () => onApproveAlways(approvalContext.defaultScope === "user" ? "session" : approvalContext.defaultScope)
38658
39174
  });
38659
39175
  }
39176
+ const denyLabel = progress && progress.total > 1 ? "No, deny this tool (esc)" : "No, and tell Letta what to do differently (esc)";
38660
39177
  opts.push({
38661
- label: "No, and tell Letta what to do differently (esc)",
39178
+ label: denyLabel,
38662
39179
  action: () => {}
38663
39180
  });
38664
39181
  return opts;
38665
- }, [approvalContext, onApprove, onApproveAlways]);
39182
+ }, [progress, approvalContext, onApproveAll, onApproveAlways]);
38666
39183
  use_input_default((_input, key) => {
38667
39184
  if (isEnteringReason) {
38668
39185
  if (key.return) {
38669
39186
  const resolvedReason = resolvePlaceholders(denyReason);
38670
- onDeny(resolvedReason);
39187
+ onDenyAll(resolvedReason);
38671
39188
  } else if (key.escape) {
38672
39189
  setIsEnteringReason(false);
38673
39190
  setDenyReason("");
@@ -38707,10 +39224,10 @@ var init_ApprovalDialogRich = __esm(async () => {
38707
39224
  });
38708
39225
  let parsedArgs = null;
38709
39226
  try {
38710
- parsedArgs = JSON.parse(approvalRequest.toolArgs);
39227
+ parsedArgs = approvalRequest?.toolArgs ? JSON.parse(approvalRequest.toolArgs) : null;
38711
39228
  } catch {}
38712
39229
  const precomputedDiff = import_react28.useMemo(() => {
38713
- if (!parsedArgs)
39230
+ if (!parsedArgs || !approvalRequest)
38714
39231
  return null;
38715
39232
  const toolName = approvalRequest.toolName.toLowerCase();
38716
39233
  if (toolName === "write") {
@@ -38739,6 +39256,9 @@ var init_ApprovalDialogRich = __esm(async () => {
38739
39256
  }
38740
39257
  return null;
38741
39258
  }, [approvalRequest, parsedArgs]);
39259
+ if (!approvalRequest) {
39260
+ return null;
39261
+ }
38742
39262
  const headerLabel = getHeaderLabel(approvalRequest.toolName);
38743
39263
  if (isEnteringReason) {
38744
39264
  return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
@@ -38791,7 +39311,22 @@ var init_ApprovalDialogRich = __esm(async () => {
38791
39311
  /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
38792
39312
  bold: true,
38793
39313
  color: colors.approval.header,
38794
- children: headerLabel
39314
+ children: progress && progress.total > 1 ? `${progress.total} tools require approval${totalTools && totalTools > progress.total ? ` (${totalTools} total)` : ""}` : headerLabel
39315
+ }, undefined, false, undefined, this),
39316
+ progress && progress.total > 1 && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
39317
+ dimColor: true,
39318
+ children: [
39319
+ "(",
39320
+ progress.current - 1,
39321
+ " reviewed,",
39322
+ " ",
39323
+ progress.total - (progress.current - 1),
39324
+ " remaining)"
39325
+ ]
39326
+ }, undefined, true, undefined, this),
39327
+ isExecuting && progress && progress.total > 1 && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
39328
+ dimColor: true,
39329
+ children: "Executing tool..."
38795
39330
  }, undefined, false, undefined, this),
38796
39331
  /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
38797
39332
  height: 1
@@ -42207,6 +42742,24 @@ var init_registry = __esm(() => {
42207
42742
  handler: () => {
42208
42743
  return "Clearing credentials...";
42209
42744
  }
42745
+ },
42746
+ "/link": {
42747
+ desc: "Attach Letta Code tools to current agent",
42748
+ handler: () => {
42749
+ return "Attaching tools...";
42750
+ }
42751
+ },
42752
+ "/unlink": {
42753
+ desc: "Remove Letta Code tools from current agent",
42754
+ handler: () => {
42755
+ return "Removing tools...";
42756
+ }
42757
+ },
42758
+ "/rename": {
42759
+ desc: "Rename the current agent",
42760
+ handler: () => {
42761
+ return "Renaming agent...";
42762
+ }
42210
42763
  }
42211
42764
  };
42212
42765
  });
@@ -42215,12 +42768,14 @@ var init_registry = __esm(() => {
42215
42768
  function CommandPreview({
42216
42769
  currentInput,
42217
42770
  agentId,
42771
+ agentName,
42218
42772
  serverUrl
42219
42773
  }) {
42220
42774
  if (!currentInput.startsWith("/")) {
42221
42775
  return null;
42222
42776
  }
42223
- const showAgentUrl = agentId && agentId !== "loading" && serverUrl?.includes("api.letta.com");
42777
+ const isCloudUser = serverUrl?.includes("api.letta.com");
42778
+ const showBottomBar = agentId && agentId !== "loading";
42224
42779
  return /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
42225
42780
  flexDirection: "column",
42226
42781
  borderStyle: "round",
@@ -42228,31 +42783,46 @@ function CommandPreview({
42228
42783
  paddingX: 1,
42229
42784
  children: [
42230
42785
  commandList.map((item) => /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
42231
- justifyContent: "space-between",
42232
- width: 40,
42233
- children: [
42234
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
42235
- children: item.cmd
42236
- }, undefined, false, undefined, this),
42237
- /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
42238
- dimColor: true,
42239
- children: item.desc
42240
- }, undefined, false, undefined, this)
42241
- ]
42242
- }, item.cmd, true, undefined, this)),
42243
- showAgentUrl && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
42786
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
42787
+ children: [
42788
+ item.cmd.padEnd(15),
42789
+ " ",
42790
+ /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
42791
+ dimColor: true,
42792
+ children: item.desc
42793
+ }, undefined, false, undefined, this)
42794
+ ]
42795
+ }, undefined, true, undefined, this)
42796
+ }, item.cmd, false, undefined, this)),
42797
+ showBottomBar && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Box_default, {
42244
42798
  marginTop: 1,
42245
42799
  paddingTop: 1,
42246
42800
  borderTop: true,
42247
42801
  borderColor: "gray",
42248
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(dist_default4, {
42249
- url: `https://app.letta.com/agents/${agentId}`,
42250
- children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
42802
+ flexDirection: "column",
42803
+ children: [
42804
+ agentName && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
42251
42805
  dimColor: true,
42252
- children: "View agent:"
42253
- }, undefined, false, undefined, this)
42254
- }, undefined, false, undefined, this)
42255
- }, undefined, false, undefined, this)
42806
+ children: [
42807
+ "Agent: ",
42808
+ agentName
42809
+ ]
42810
+ }, undefined, true, undefined, this),
42811
+ isCloudUser ? /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(dist_default4, {
42812
+ url: `https://app.letta.com/agents/${agentId}`,
42813
+ children: /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
42814
+ dimColor: true,
42815
+ children: "View agent in ADE"
42816
+ }, undefined, false, undefined, this)
42817
+ }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
42818
+ dimColor: true,
42819
+ children: [
42820
+ "Connected to agent located at ",
42821
+ serverUrl
42822
+ ]
42823
+ }, undefined, true, undefined, this)
42824
+ ]
42825
+ }, undefined, true, undefined, this)
42256
42826
  ]
42257
42827
  }, undefined, true, undefined, this);
42258
42828
  }
@@ -42273,7 +42843,7 @@ var init_CommandPreview = __esm(async () => {
42273
42843
 
42274
42844
  // src/cli/helpers/fileSearch.ts
42275
42845
  import { readdirSync, statSync as statSync2 } from "node:fs";
42276
- import { join as join6, resolve as resolve8 } from "node:path";
42846
+ import { join as join8, resolve as resolve8 } from "node:path";
42277
42847
  function searchDirectoryRecursive(dir, pattern, maxResults = 200, results = []) {
42278
42848
  if (results.length >= maxResults) {
42279
42849
  return results;
@@ -42285,7 +42855,7 @@ function searchDirectoryRecursive(dir, pattern, maxResults = 200, results = [])
42285
42855
  continue;
42286
42856
  }
42287
42857
  try {
42288
- const fullPath = join6(dir, entry);
42858
+ const fullPath = join8(dir, entry);
42289
42859
  const stats = statSync2(fullPath);
42290
42860
  const matches = pattern.length === 0 || entry.toLowerCase().includes(pattern.toLowerCase());
42291
42861
  if (matches) {
@@ -42334,7 +42904,7 @@ async function searchFiles(query, deep = false) {
42334
42904
  const matchingEntries = searchPattern.length === 0 ? entries : entries.filter((entry) => entry.toLowerCase().includes(searchPattern.toLowerCase()));
42335
42905
  for (const entry of matchingEntries.slice(0, 50)) {
42336
42906
  try {
42337
- const fullPath = join6(searchDir, entry);
42907
+ const fullPath = join8(searchDir, entry);
42338
42908
  const stats = statSync2(fullPath);
42339
42909
  const relativePath = fullPath.startsWith(process.cwd()) ? fullPath.slice(process.cwd().length + 1) : fullPath;
42340
42910
  results.push({
@@ -42556,6 +43126,7 @@ function InputAssist({
42556
43126
  onFileSelect,
42557
43127
  onAutocompleteActiveChange,
42558
43128
  agentId,
43129
+ agentName,
42559
43130
  serverUrl
42560
43131
  }) {
42561
43132
  if (currentInput.includes("@")) {
@@ -42570,6 +43141,7 @@ function InputAssist({
42570
43141
  return /* @__PURE__ */ jsx_dev_runtime13.jsxDEV(CommandPreview, {
42571
43142
  currentInput,
42572
43143
  agentId,
43144
+ agentName,
42573
43145
  serverUrl
42574
43146
  }, undefined, false, undefined, this);
42575
43147
  }
@@ -42622,7 +43194,8 @@ function Input({
42622
43194
  onExit,
42623
43195
  onInterrupt,
42624
43196
  interruptRequested = false,
42625
- agentId
43197
+ agentId,
43198
+ agentName
42626
43199
  }) {
42627
43200
  const [value, setValue] = import_react34.useState("");
42628
43201
  const [escapePressed, setEscapePressed] = import_react34.useState(false);
@@ -42969,6 +43542,7 @@ function Input({
42969
43542
  onFileSelect: handleFileSelect,
42970
43543
  onAutocompleteActiveChange: setIsAutocompleteActive,
42971
43544
  agentId,
43545
+ agentName,
42972
43546
  serverUrl
42973
43547
  }, undefined, false, undefined, this),
42974
43548
  /* @__PURE__ */ jsx_dev_runtime15.jsxDEV(Box_default, {
@@ -43654,7 +44228,7 @@ var init_ToolCallMessageRich = __esm(async () => {
43654
44228
  ]
43655
44229
  }, undefined, true, undefined, this);
43656
44230
  }
43657
- const displayResultText = clipToolReturn(line.resultText);
44231
+ const displayResultText = clipToolReturn(line.resultText).replace(/\n+$/, "");
43658
44232
  const isTodoTool = rawName === "todo_write" || rawName === "TodoWrite" || displayName === "TODO";
43659
44233
  if (isTodoTool && line.resultOk !== false && line.argsText) {
43660
44234
  try {
@@ -43853,6 +44427,12 @@ function WelcomeScreen({
43853
44427
  if (loadingState === "upserting") {
43854
44428
  return "Upserting tools...";
43855
44429
  }
44430
+ if (loadingState === "linking") {
44431
+ return "Attaching Letta Code tools...";
44432
+ }
44433
+ if (loadingState === "unlinking") {
44434
+ return "Removing Letta Code tools...";
44435
+ }
43856
44436
  if (loadingState === "checking") {
43857
44437
  return "Checking for pending approvals...";
43858
44438
  }
@@ -44016,42 +44596,63 @@ function backfillBuffers(buffers, history) {
44016
44596
  case "tool_call_message":
44017
44597
  case "approval_request_message": {
44018
44598
  const toolCalls = Array.isArray(msg.tool_calls) ? msg.tool_calls : msg.tool_call ? [msg.tool_call] : [];
44019
- if (toolCalls.length > 0 && toolCalls[0]?.tool_call_id) {
44020
- const toolCall = toolCalls[0];
44599
+ for (let i = 0;i < toolCalls.length; i++) {
44600
+ const toolCall = toolCalls[i];
44601
+ if (!toolCall?.tool_call_id)
44602
+ continue;
44021
44603
  const toolCallId = toolCall.tool_call_id;
44022
44604
  if (!toolCallId || !toolCall.name || !toolCall.arguments)
44023
- break;
44024
- const exists2 = buffers.byId.has(lineId);
44025
- buffers.byId.set(lineId, {
44605
+ continue;
44606
+ let uniqueLineId = lineId;
44607
+ if (buffers.byId.has(lineId)) {
44608
+ const existing = buffers.byId.get(lineId);
44609
+ if (existing && existing.kind === "tool_call") {
44610
+ uniqueLineId = `${lineId}-${toolCallId.slice(-8)}`;
44611
+ }
44612
+ }
44613
+ const exists2 = buffers.byId.has(uniqueLineId);
44614
+ buffers.byId.set(uniqueLineId, {
44026
44615
  kind: "tool_call",
44027
- id: lineId,
44616
+ id: uniqueLineId,
44028
44617
  toolCallId,
44029
44618
  name: toolCall.name,
44030
44619
  argsText: toolCall.arguments,
44031
44620
  phase: "ready"
44032
44621
  });
44033
44622
  if (!exists2)
44034
- buffers.order.push(lineId);
44035
- buffers.toolCallIdToLineId.set(toolCallId, lineId);
44623
+ buffers.order.push(uniqueLineId);
44624
+ buffers.toolCallIdToLineId.set(toolCallId, uniqueLineId);
44036
44625
  }
44037
44626
  break;
44038
44627
  }
44039
44628
  case "tool_return_message": {
44040
- const toolCallId = msg.tool_call_id;
44041
- if (!toolCallId)
44042
- break;
44043
- const toolCallLineId = buffers.toolCallIdToLineId.get(toolCallId);
44044
- if (!toolCallLineId)
44045
- break;
44046
- const existingLine = buffers.byId.get(toolCallLineId);
44047
- if (!existingLine || existingLine.kind !== "tool_call")
44048
- break;
44049
- buffers.byId.set(toolCallLineId, {
44050
- ...existingLine,
44051
- resultText: msg.tool_return,
44052
- resultOk: msg.status === "success",
44053
- phase: "finished"
44054
- });
44629
+ const toolReturns = Array.isArray(msg.tool_returns) && msg.tool_returns.length > 0 ? msg.tool_returns : msg.tool_call_id ? [
44630
+ {
44631
+ tool_call_id: msg.tool_call_id,
44632
+ status: msg.status,
44633
+ func_response: msg.tool_return,
44634
+ stdout: msg.stdout,
44635
+ stderr: msg.stderr
44636
+ }
44637
+ ] : [];
44638
+ for (const toolReturn of toolReturns) {
44639
+ const toolCallId = toolReturn.tool_call_id;
44640
+ if (!toolCallId)
44641
+ continue;
44642
+ const toolCallLineId = buffers.toolCallIdToLineId.get(toolCallId);
44643
+ if (!toolCallLineId)
44644
+ continue;
44645
+ const existingLine = buffers.byId.get(toolCallLineId);
44646
+ if (!existingLine || existingLine.kind !== "tool_call")
44647
+ continue;
44648
+ const resultText = ("func_response" in toolReturn ? toolReturn.func_response : undefined) || ("tool_return" in toolReturn ? toolReturn.tool_return : undefined) || "";
44649
+ buffers.byId.set(toolCallLineId, {
44650
+ ...existingLine,
44651
+ resultText,
44652
+ resultOk: toolReturn.status === "success",
44653
+ phase: "finished"
44654
+ });
44655
+ }
44055
44656
  break;
44056
44657
  }
44057
44658
  default:
@@ -44125,17 +44726,23 @@ function App2({
44125
44726
  loadingState = "ready",
44126
44727
  continueSession = false,
44127
44728
  startupApproval = null,
44729
+ startupApprovals = [],
44128
44730
  messageHistory = [],
44129
44731
  tokenStreaming = true
44130
44732
  }) {
44131
44733
  const [streaming, setStreaming] = import_react40.useState(false);
44132
44734
  const [interruptRequested, setInterruptRequested] = import_react40.useState(false);
44133
44735
  const [commandRunning, setCommandRunning] = import_react40.useState(false);
44134
- const [pendingApproval, setPendingApproval] = import_react40.useState(null);
44135
- const [approvalContext, setApprovalContext] = import_react40.useState(null);
44736
+ const [pendingApprovals, setPendingApprovals] = import_react40.useState([]);
44737
+ const [approvalContexts, setApprovalContexts] = import_react40.useState([]);
44738
+ const [approvalResults, setApprovalResults] = import_react40.useState([]);
44739
+ const [isExecutingTool, setIsExecutingTool] = import_react40.useState(false);
44740
+ const [autoHandledResults, setAutoHandledResults] = import_react40.useState([]);
44741
+ const [autoDeniedApprovals, setAutoDeniedApprovals] = import_react40.useState([]);
44136
44742
  const [planApprovalPending, setPlanApprovalPending] = import_react40.useState(null);
44137
44743
  const [modelSelectorOpen, setModelSelectorOpen] = import_react40.useState(false);
44138
44744
  const [llmConfig, setLlmConfig] = import_react40.useState(null);
44745
+ const [agentName, setAgentName] = import_react40.useState(null);
44139
44746
  const [tokenStreamingEnabled, setTokenStreamingEnabled] = import_react40.useState(tokenStreaming);
44140
44747
  const [tokenCount, setTokenCount] = import_react40.useState(0);
44141
44748
  const [thinkingMessage, setThinkingMessage] = import_react40.useState(getRandomThinkingMessage());
@@ -44207,30 +44814,34 @@ function App2({
44207
44814
  }
44208
44815
  }, [refreshDerived]);
44209
44816
  import_react40.useEffect(() => {
44210
- if (loadingState === "ready" && startupApproval) {
44211
- if (startupApproval.toolName === "ExitPlanMode") {
44212
- const parsedArgs = safeJsonParseOr(startupApproval.toolArgs, {});
44817
+ const approvals = startupApprovals?.length > 0 ? startupApprovals : startupApproval ? [startupApproval] : [];
44818
+ if (loadingState === "ready" && approvals.length > 0) {
44819
+ const planApproval = approvals.find((a) => a.toolName === "ExitPlanMode");
44820
+ if (planApproval) {
44821
+ const parsedArgs = safeJsonParseOr(planApproval.toolArgs, {});
44213
44822
  const plan = parsedArgs.plan || "No plan provided";
44214
44823
  setPlanApprovalPending({
44215
44824
  plan,
44216
- toolCallId: startupApproval.toolCallId,
44217
- toolArgs: startupApproval.toolArgs
44825
+ toolCallId: planApproval.toolCallId,
44826
+ toolArgs: planApproval.toolArgs
44218
44827
  });
44219
44828
  } else {
44220
- setPendingApproval(startupApproval);
44221
- const analyzeStartupApproval = async () => {
44829
+ setPendingApprovals(approvals);
44830
+ const analyzeStartupApprovals = async () => {
44222
44831
  try {
44223
- const parsedArgs = safeJsonParseOr(startupApproval.toolArgs, {});
44224
- const context = await analyzeToolApproval(startupApproval.toolName, parsedArgs);
44225
- setApprovalContext(context);
44832
+ const contexts = await Promise.all(approvals.map(async (approval) => {
44833
+ const parsedArgs = safeJsonParseOr(approval.toolArgs, {});
44834
+ return await analyzeToolApproval(approval.toolName, parsedArgs);
44835
+ }));
44836
+ setApprovalContexts(contexts);
44226
44837
  } catch (error) {
44227
- console.error("Failed to analyze startup approval:", error);
44838
+ console.error("Failed to analyze startup approvals:", error);
44228
44839
  }
44229
44840
  };
44230
- analyzeStartupApproval();
44841
+ analyzeStartupApprovals();
44231
44842
  }
44232
44843
  }
44233
- }, [loadingState, startupApproval]);
44844
+ }, [loadingState, startupApproval, startupApprovals]);
44234
44845
  import_react40.useEffect(() => {
44235
44846
  if (loadingState === "ready" && messageHistory.length > 0 && !hasBackfilledRef.current) {
44236
44847
  hasBackfilledRef.current = true;
@@ -44270,8 +44881,9 @@ function App2({
44270
44881
  const client = await getClient3();
44271
44882
  const agent = await client.agents.retrieve(agentId);
44272
44883
  setLlmConfig(agent.llm_config);
44884
+ setAgentName(agent.name);
44273
44885
  } catch (error) {
44274
- console.error("Error fetching llm_config:", error);
44886
+ console.error("Error fetching agent config:", error);
44275
44887
  }
44276
44888
  };
44277
44889
  fetchConfig();
@@ -44288,13 +44900,13 @@ function App2({
44288
44900
  refreshDerived();
44289
44901
  }, [refreshDerived]);
44290
44902
  const processConversation = import_react40.useCallback(async (initialInput) => {
44291
- let currentInput = initialInput;
44903
+ const currentInput = initialInput;
44292
44904
  try {
44293
44905
  setStreaming(true);
44294
44906
  abortControllerRef.current = new AbortController;
44295
44907
  while (true) {
44296
44908
  const stream = await sendMessageStream(agentId, currentInput);
44297
- const { stopReason, approval, apiDurationMs, lastRunId } = await drainStreamWithResume(stream, buffersRef.current, refreshDerivedThrottled, abortControllerRef.current.signal);
44909
+ const { stopReason, approval, approvals, apiDurationMs, lastRunId } = await drainStreamWithResume(stream, buffersRef.current, refreshDerivedThrottled, abortControllerRef.current.signal);
44298
44910
  sessionStatsRef.current.endTurn(apiDurationMs);
44299
44911
  sessionStatsRef.current.updateUsageFromBuffers(buffersRef.current);
44300
44912
  refreshDerived();
@@ -44308,69 +44920,91 @@ function App2({
44308
44920
  return;
44309
44921
  }
44310
44922
  if (stopReason === "requires_approval") {
44311
- if (!approval) {
44312
- appendError(`Unexpected null approval with stop reason: ${stopReason}`);
44923
+ const approvalsToProcess = approvals && approvals.length > 0 ? approvals : approval ? [approval] : [];
44924
+ if (approvalsToProcess.length === 0) {
44925
+ appendError(`Unexpected empty approvals with stop reason: ${stopReason}`);
44313
44926
  setStreaming(false);
44314
44927
  return;
44315
44928
  }
44316
- const { toolCallId, toolName, toolArgs } = approval;
44317
- if (toolName === "ExitPlanMode") {
44318
- const parsedArgs2 = safeJsonParseOr(toolArgs, {});
44319
- const plan = parsedArgs2.plan || "No plan provided";
44320
- setPlanApprovalPending({ plan, toolCallId, toolArgs });
44929
+ const planApproval = approvalsToProcess.find((a) => a.toolName === "ExitPlanMode");
44930
+ if (planApproval) {
44931
+ const parsedArgs = safeJsonParseOr(planApproval.toolArgs, {});
44932
+ const plan = parsedArgs.plan || "No plan provided";
44933
+ setPlanApprovalPending({
44934
+ plan,
44935
+ toolCallId: planApproval.toolCallId,
44936
+ toolArgs: planApproval.toolArgs
44937
+ });
44321
44938
  setStreaming(false);
44322
44939
  return;
44323
44940
  }
44324
- const parsedArgs = safeJsonParseOr(toolArgs, {});
44325
- const permission = await checkToolPermission(toolName, parsedArgs);
44326
- if (permission.decision === "deny") {
44327
- const denyReason = `Permission denied by rule: ${permission.matchedRule || permission.reason}`;
44941
+ const approvalResults2 = await Promise.all(approvalsToProcess.map(async (approvalItem) => {
44942
+ const parsedArgs = safeJsonParseOr(approvalItem.toolArgs, {});
44943
+ const permission = await checkToolPermission(approvalItem.toolName, parsedArgs);
44944
+ const context = await analyzeToolApproval(approvalItem.toolName, parsedArgs);
44945
+ return { approval: approvalItem, permission, context };
44946
+ }));
44947
+ const needsUserInput = approvalResults2.filter((ac) => ac.permission.decision === "ask");
44948
+ const autoDenied = approvalResults2.filter((ac) => ac.permission.decision === "deny");
44949
+ const autoAllowed = approvalResults2.filter((ac) => ac.permission.decision === "allow");
44950
+ const autoAllowedResults = await Promise.all(autoAllowed.map(async (ac) => {
44951
+ const parsedArgs = safeJsonParseOr(ac.approval.toolArgs, {});
44952
+ const result = await executeTool(ac.approval.toolName, parsedArgs);
44953
+ onChunk(buffersRef.current, {
44954
+ message_type: "tool_return_message",
44955
+ id: "dummy",
44956
+ date: new Date().toISOString(),
44957
+ tool_call_id: ac.approval.toolCallId,
44958
+ tool_return: result.toolReturn,
44959
+ status: result.status,
44960
+ stdout: result.stdout,
44961
+ stderr: result.stderr
44962
+ });
44963
+ return {
44964
+ toolCallId: ac.approval.toolCallId,
44965
+ result
44966
+ };
44967
+ }));
44968
+ const autoDeniedResults = autoDenied.map((ac) => ({
44969
+ approval: ac.approval,
44970
+ reason: `Permission denied by rule: ${ac.permission.matchedRule || ac.permission.reason}`
44971
+ }));
44972
+ if (needsUserInput.length === 0) {
44328
44973
  setThinkingMessage(getRandomThinkingMessage());
44974
+ refreshDerived();
44975
+ const allResults = [
44976
+ ...autoAllowedResults.map((ar) => ({
44977
+ type: "tool",
44978
+ tool_call_id: ar.toolCallId,
44979
+ tool_return: ar.result.toolReturn,
44980
+ status: ar.result.status,
44981
+ stdout: ar.result.stdout,
44982
+ stderr: ar.result.stderr
44983
+ })),
44984
+ ...autoDeniedResults.map((ad) => ({
44985
+ type: "tool",
44986
+ tool_call_id: ad.approval.toolCallId,
44987
+ tool_return: JSON.stringify({
44988
+ status: "error",
44989
+ message: ad.reason
44990
+ }),
44991
+ status: "error"
44992
+ }))
44993
+ ];
44329
44994
  await processConversation([
44330
44995
  {
44331
44996
  type: "approval",
44332
- approval_request_id: toolCallId,
44333
- approve: false,
44334
- reason: denyReason
44997
+ approvals: allResults
44335
44998
  }
44336
44999
  ]);
44337
45000
  return;
44338
45001
  }
44339
- if (permission.decision === "ask") {
44340
- const context = await analyzeToolApproval(toolName, parsedArgs);
44341
- setPendingApproval({ toolCallId, toolName, toolArgs });
44342
- setApprovalContext(context);
44343
- setStreaming(false);
44344
- return;
44345
- }
44346
- const toolResult = await executeTool(toolName, parsedArgs);
44347
- onChunk(buffersRef.current, {
44348
- message_type: "tool_return_message",
44349
- id: "dummy",
44350
- date: new Date().toISOString(),
44351
- tool_call_id: toolCallId,
44352
- tool_return: toolResult.toolReturn,
44353
- status: toolResult.status,
44354
- stdout: toolResult.stdout,
44355
- stderr: toolResult.stderr
44356
- });
44357
- refreshDerived();
44358
- currentInput = [
44359
- {
44360
- type: "approval",
44361
- approvals: [
44362
- {
44363
- type: "tool",
44364
- tool_call_id: toolCallId,
44365
- tool_return: toolResult.toolReturn,
44366
- status: toolResult.status,
44367
- stdout: toolResult.stdout,
44368
- stderr: toolResult.stderr
44369
- }
44370
- ]
44371
- }
44372
- ];
44373
- continue;
45002
+ setPendingApprovals(needsUserInput.map((ac) => ac.approval));
45003
+ setApprovalContexts(needsUserInput.map((ac) => ac.context));
45004
+ setAutoHandledResults(autoAllowedResults);
45005
+ setAutoDeniedApprovals(autoDeniedResults);
45006
+ setStreaming(false);
45007
+ return;
44374
45008
  }
44375
45009
  markIncompleteToolsAsCancelled(buffersRef.current);
44376
45010
  let errorDetails = `Unexpected stop reason: ${stopReason}`;
@@ -44598,6 +45232,138 @@ ${detail}` : "";
44598
45232
  }
44599
45233
  return { submitted: true };
44600
45234
  }
45235
+ if (msg.trim() === "/link") {
45236
+ const cmdId2 = uid("cmd");
45237
+ buffersRef.current.byId.set(cmdId2, {
45238
+ kind: "command",
45239
+ id: cmdId2,
45240
+ input: msg,
45241
+ output: "Attaching Letta Code tools to agent...",
45242
+ phase: "running"
45243
+ });
45244
+ buffersRef.current.order.push(cmdId2);
45245
+ refreshDerived();
45246
+ setCommandRunning(true);
45247
+ try {
45248
+ const result = await linkToolsToAgent(agentId);
45249
+ buffersRef.current.byId.set(cmdId2, {
45250
+ kind: "command",
45251
+ id: cmdId2,
45252
+ input: msg,
45253
+ output: result.message,
45254
+ phase: "finished",
45255
+ success: result.success
45256
+ });
45257
+ refreshDerived();
45258
+ } catch (error) {
45259
+ buffersRef.current.byId.set(cmdId2, {
45260
+ kind: "command",
45261
+ id: cmdId2,
45262
+ input: msg,
45263
+ output: `Failed: ${error instanceof Error ? error.message : String(error)}`,
45264
+ phase: "finished",
45265
+ success: false
45266
+ });
45267
+ refreshDerived();
45268
+ } finally {
45269
+ setCommandRunning(false);
45270
+ }
45271
+ return { submitted: true };
45272
+ }
45273
+ if (msg.trim() === "/unlink") {
45274
+ const cmdId2 = uid("cmd");
45275
+ buffersRef.current.byId.set(cmdId2, {
45276
+ kind: "command",
45277
+ id: cmdId2,
45278
+ input: msg,
45279
+ output: "Removing Letta Code tools from agent...",
45280
+ phase: "running"
45281
+ });
45282
+ buffersRef.current.order.push(cmdId2);
45283
+ refreshDerived();
45284
+ setCommandRunning(true);
45285
+ try {
45286
+ const result = await unlinkToolsFromAgent(agentId);
45287
+ buffersRef.current.byId.set(cmdId2, {
45288
+ kind: "command",
45289
+ id: cmdId2,
45290
+ input: msg,
45291
+ output: result.message,
45292
+ phase: "finished",
45293
+ success: result.success
45294
+ });
45295
+ refreshDerived();
45296
+ } catch (error) {
45297
+ buffersRef.current.byId.set(cmdId2, {
45298
+ kind: "command",
45299
+ id: cmdId2,
45300
+ input: msg,
45301
+ output: `Failed: ${error instanceof Error ? error.message : String(error)}`,
45302
+ phase: "finished",
45303
+ success: false
45304
+ });
45305
+ refreshDerived();
45306
+ } finally {
45307
+ setCommandRunning(false);
45308
+ }
45309
+ return { submitted: true };
45310
+ }
45311
+ if (msg.trim().startsWith("/rename")) {
45312
+ const parts = msg.trim().split(/\s+/);
45313
+ const newName = parts.slice(1).join(" ");
45314
+ if (!newName) {
45315
+ const cmdId3 = uid("cmd");
45316
+ buffersRef.current.byId.set(cmdId3, {
45317
+ kind: "command",
45318
+ id: cmdId3,
45319
+ input: msg,
45320
+ output: "Please provide a new name: /rename <name>",
45321
+ phase: "finished",
45322
+ success: false
45323
+ });
45324
+ buffersRef.current.order.push(cmdId3);
45325
+ refreshDerived();
45326
+ return { submitted: true };
45327
+ }
45328
+ const cmdId2 = uid("cmd");
45329
+ buffersRef.current.byId.set(cmdId2, {
45330
+ kind: "command",
45331
+ id: cmdId2,
45332
+ input: msg,
45333
+ output: `Renaming agent to "${newName}"...`,
45334
+ phase: "running"
45335
+ });
45336
+ buffersRef.current.order.push(cmdId2);
45337
+ refreshDerived();
45338
+ setCommandRunning(true);
45339
+ try {
45340
+ const client = await getClient2();
45341
+ await client.agents.modify(agentId, { name: newName });
45342
+ setAgentName(newName);
45343
+ buffersRef.current.byId.set(cmdId2, {
45344
+ kind: "command",
45345
+ id: cmdId2,
45346
+ input: msg,
45347
+ output: `Agent renamed to "${newName}"`,
45348
+ phase: "finished",
45349
+ success: true
45350
+ });
45351
+ refreshDerived();
45352
+ } catch (error) {
45353
+ buffersRef.current.byId.set(cmdId2, {
45354
+ kind: "command",
45355
+ id: cmdId2,
45356
+ input: msg,
45357
+ output: `Failed: ${error instanceof Error ? error.message : String(error)}`,
45358
+ phase: "finished",
45359
+ success: false
45360
+ });
45361
+ refreshDerived();
45362
+ } finally {
45363
+ setCommandRunning(false);
45364
+ }
45365
+ return { submitted: true };
45366
+ }
44601
45367
  const cmdId = uid("cmd");
44602
45368
  buffersRef.current.byId.set(cmdId, {
44603
45369
  kind: "command",
@@ -44660,10 +45426,10 @@ ${detail}` : "";
44660
45426
  const { pendingApproval: existingApproval } = await getResumeData2(client, agent);
44661
45427
  if (existingApproval) {
44662
45428
  setStreaming(false);
44663
- setPendingApproval(existingApproval);
45429
+ setPendingApprovals([existingApproval]);
44664
45430
  const parsedArgs = safeJsonParseOr(existingApproval.toolArgs, {});
44665
45431
  const context = await analyzeToolApproval(existingApproval.toolName, parsedArgs);
44666
- setApprovalContext(context);
45432
+ setApprovalContexts([context]);
44667
45433
  return { submitted: false };
44668
45434
  }
44669
45435
  } catch (error) {
@@ -44688,48 +45454,85 @@ ${detail}` : "";
44688
45454
  agentId,
44689
45455
  handleExit
44690
45456
  ]);
44691
- const handleApprove = import_react40.useCallback(async () => {
44692
- if (!pendingApproval)
45457
+ const sendAllResults = import_react40.useCallback(async (additionalDecision) => {
45458
+ const allDecisions = [
45459
+ ...approvalResults,
45460
+ ...additionalDecision ? [additionalDecision] : []
45461
+ ];
45462
+ const { executeApprovalBatch: executeApprovalBatch2 } = await Promise.resolve().then(() => (init_approval_execution(), exports_approval_execution));
45463
+ const executedResults = await executeApprovalBatch2(allDecisions, (chunk) => {
45464
+ onChunk(buffersRef.current, chunk);
45465
+ if (chunk.status === "error" && chunk.message_type === "tool_return_message") {
45466
+ const isToolError = chunk.tool_return?.startsWith("Error executing tool:");
45467
+ if (isToolError) {
45468
+ appendError(chunk.tool_return);
45469
+ }
45470
+ }
45471
+ });
45472
+ const allResults = [
45473
+ ...autoHandledResults.map((ar) => ({
45474
+ type: "tool",
45475
+ tool_call_id: ar.toolCallId,
45476
+ tool_return: ar.result.toolReturn,
45477
+ status: ar.result.status,
45478
+ stdout: ar.result.stdout,
45479
+ stderr: ar.result.stderr
45480
+ })),
45481
+ ...autoDeniedApprovals.map((ad) => ({
45482
+ type: "approval",
45483
+ tool_call_id: ad.approval.toolCallId,
45484
+ approve: false,
45485
+ reason: ad.reason
45486
+ })),
45487
+ ...executedResults
45488
+ ];
45489
+ setPendingApprovals([]);
45490
+ setApprovalContexts([]);
45491
+ setApprovalResults([]);
45492
+ setAutoHandledResults([]);
45493
+ setAutoDeniedApprovals([]);
45494
+ setThinkingMessage(getRandomThinkingMessage());
45495
+ refreshDerived();
45496
+ await processConversation([
45497
+ {
45498
+ type: "approval",
45499
+ approvals: allResults
45500
+ }
45501
+ ]);
45502
+ }, [
45503
+ approvalResults,
45504
+ autoHandledResults,
45505
+ autoDeniedApprovals,
45506
+ processConversation,
45507
+ refreshDerived,
45508
+ appendError
45509
+ ]);
45510
+ const handleApproveCurrent = import_react40.useCallback(async () => {
45511
+ const currentIndex = approvalResults.length;
45512
+ const currentApproval = pendingApprovals[currentIndex];
45513
+ if (!currentApproval)
44693
45514
  return;
44694
- const { toolCallId, toolName, toolArgs } = pendingApproval;
44695
- setPendingApproval(null);
44696
45515
  try {
44697
- const parsedArgs = safeJsonParseOr(toolArgs, {});
44698
- const toolResult = await executeTool(toolName, parsedArgs);
44699
- onChunk(buffersRef.current, {
44700
- message_type: "tool_return_message",
44701
- id: "dummy",
44702
- date: new Date().toISOString(),
44703
- tool_call_id: toolCallId,
44704
- tool_return: toolResult.toolReturn,
44705
- status: toolResult.status,
44706
- stdout: toolResult.stdout,
44707
- stderr: toolResult.stderr
44708
- });
44709
- setThinkingMessage(getRandomThinkingMessage());
44710
- refreshDerived();
44711
- await processConversation([
44712
- {
44713
- type: "approval",
44714
- approvals: [
44715
- {
44716
- type: "tool",
44717
- tool_call_id: toolCallId,
44718
- tool_return: toolResult.toolReturn,
44719
- status: toolResult.status,
44720
- stdout: toolResult.stdout,
44721
- stderr: toolResult.stderr
44722
- }
44723
- ]
44724
- }
44725
- ]);
45516
+ const decision = {
45517
+ type: "approve",
45518
+ approval: currentApproval
45519
+ };
45520
+ if (currentIndex + 1 >= pendingApprovals.length) {
45521
+ await sendAllResults(decision);
45522
+ } else {
45523
+ setApprovalResults((prev) => [...prev, decision]);
45524
+ }
44726
45525
  } catch (e) {
44727
45526
  appendError(String(e));
44728
45527
  setStreaming(false);
44729
45528
  }
44730
- }, [pendingApproval, processConversation, appendError, refreshDerived]);
45529
+ }, [pendingApprovals, approvalResults, sendAllResults, appendError]);
44731
45530
  const handleApproveAlways = import_react40.useCallback(async (scope) => {
44732
- if (!pendingApproval || !approvalContext)
45531
+ if (pendingApprovals.length === 0 || approvalContexts.length === 0)
45532
+ return;
45533
+ const currentIndex = approvalResults.length;
45534
+ const approvalContext = approvalContexts[currentIndex];
45535
+ if (!approvalContext)
44733
45536
  return;
44734
45537
  const rule = approvalContext.recommendedRule;
44735
45538
  const actualScope = scope || approvalContext.defaultScope;
@@ -44744,29 +45547,36 @@ ${detail}` : "";
44744
45547
  });
44745
45548
  buffersRef.current.order.push(cmdId);
44746
45549
  refreshDerived();
44747
- setApprovalContext(null);
44748
- await handleApprove();
44749
- }, [pendingApproval, approvalContext, handleApprove, refreshDerived]);
44750
- const handleDeny = import_react40.useCallback(async (reason) => {
44751
- if (!pendingApproval)
45550
+ await handleApproveCurrent();
45551
+ }, [
45552
+ approvalResults,
45553
+ approvalContexts,
45554
+ pendingApprovals,
45555
+ handleApproveCurrent,
45556
+ refreshDerived
45557
+ ]);
45558
+ const handleDenyCurrent = import_react40.useCallback(async (reason) => {
45559
+ const currentIndex = approvalResults.length;
45560
+ const currentApproval = pendingApprovals[currentIndex];
45561
+ if (!currentApproval)
44752
45562
  return;
44753
- const { toolCallId } = pendingApproval;
44754
- setPendingApproval(null);
44755
45563
  try {
44756
- setThinkingMessage(getRandomThinkingMessage());
44757
- await processConversation([
44758
- {
44759
- type: "approval",
44760
- approval_request_id: toolCallId,
44761
- approve: false,
44762
- reason: reason || "User denied the tool execution"
44763
- }
44764
- ]);
45564
+ const decision = {
45565
+ type: "deny",
45566
+ approval: currentApproval,
45567
+ reason: reason || "User denied the tool execution"
45568
+ };
45569
+ if (currentIndex + 1 >= pendingApprovals.length) {
45570
+ setThinkingMessage(getRandomThinkingMessage());
45571
+ await sendAllResults(decision);
45572
+ } else {
45573
+ setApprovalResults((prev) => [...prev, decision]);
45574
+ }
44765
45575
  } catch (e) {
44766
45576
  appendError(String(e));
44767
45577
  setStreaming(false);
44768
45578
  }
44769
- }, [pendingApproval, processConversation, appendError]);
45579
+ }, [pendingApprovals, approvalResults, sendAllResults, appendError]);
44770
45580
  const handleModelSelect = import_react40.useCallback(async (modelId) => {
44771
45581
  setModelSelectorOpen(false);
44772
45582
  let cmdId = null;
@@ -44898,8 +45708,6 @@ ${detail}` : "";
44898
45708
  return ln.phase === "running";
44899
45709
  }
44900
45710
  if (ln.kind === "tool_call") {
44901
- if (!tokenStreamingEnabled && ln.phase === "streaming")
44902
- return false;
44903
45711
  return ln.phase !== "finished";
44904
45712
  }
44905
45713
  if (!tokenStreamingEnabled && ln.phase === "streaming")
@@ -44968,7 +45776,7 @@ ${detail}` : "";
44968
45776
  }, undefined, false, undefined, this),
44969
45777
  loadingState === "ready" && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(jsx_dev_runtime24.Fragment, {
44970
45778
  children: [
44971
- liveItems.length > 0 && !pendingApproval && !planApprovalPending && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
45779
+ liveItems.length > 0 && pendingApprovals.length === 0 && !planApprovalPending && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
44972
45780
  flexDirection: "column",
44973
45781
  children: liveItems.map((ln) => /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
44974
45782
  marginTop: 1,
@@ -44995,7 +45803,7 @@ ${detail}` : "";
44995
45803
  agentId
44996
45804
  }, undefined, false, undefined, this),
44997
45805
  /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Input, {
44998
- visible: !showExitStats && !pendingApproval && !modelSelectorOpen && !planApprovalPending,
45806
+ visible: !showExitStats && pendingApprovals.length === 0 && !modelSelectorOpen && !planApprovalPending,
44999
45807
  streaming,
45000
45808
  commandRunning,
45001
45809
  tokenCount,
@@ -45006,7 +45814,8 @@ ${detail}` : "";
45006
45814
  onExit: handleExit,
45007
45815
  onInterrupt: handleInterrupt,
45008
45816
  interruptRequested,
45009
- agentId
45817
+ agentId,
45818
+ agentName
45010
45819
  }, undefined, false, undefined, this),
45011
45820
  modelSelectorOpen && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(ModelSelector, {
45012
45821
  currentModel: llmConfig?.model,
@@ -45026,17 +45835,23 @@ ${detail}` : "";
45026
45835
  }, undefined, false, undefined, this)
45027
45836
  ]
45028
45837
  }, undefined, true, undefined, this),
45029
- pendingApproval && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(jsx_dev_runtime24.Fragment, {
45838
+ pendingApprovals.length > 0 && /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(jsx_dev_runtime24.Fragment, {
45030
45839
  children: [
45031
45840
  /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(Box_default, {
45032
45841
  height: 1
45033
45842
  }, undefined, false, undefined, this),
45034
45843
  /* @__PURE__ */ jsx_dev_runtime24.jsxDEV(ApprovalDialog, {
45035
- approvalRequest: pendingApproval,
45036
- approvalContext,
45037
- onApprove: handleApprove,
45844
+ approvals: pendingApprovals[approvalResults.length] ? [pendingApprovals[approvalResults.length]] : [],
45845
+ approvalContexts: approvalContexts[approvalResults.length] ? [approvalContexts[approvalResults.length]] : [],
45846
+ progress: {
45847
+ current: approvalResults.length + 1,
45848
+ total: pendingApprovals.length
45849
+ },
45850
+ totalTools: autoHandledResults.length + pendingApprovals.length,
45851
+ isExecuting: isExecutingTool,
45852
+ onApproveAll: handleApproveCurrent,
45038
45853
  onApproveAlways: handleApproveAlways,
45039
- onDeny: handleDeny
45854
+ onDenyAll: handleDenyCurrent
45040
45855
  }, undefined, false, undefined, this)
45041
45856
  ]
45042
45857
  }, undefined, true, undefined, this)
@@ -45052,6 +45867,7 @@ var init_App2 = __esm(async () => {
45052
45867
  init_error();
45053
45868
  init_client2();
45054
45869
  init_message();
45870
+ init_modify();
45055
45871
  init_mode();
45056
45872
  init_manager();
45057
45873
  init_pasteRegistry();
@@ -45077,12 +45893,120 @@ var init_App2 = __esm(async () => {
45077
45893
  jsx_dev_runtime24 = __toESM(require_jsx_dev_runtime(), 1);
45078
45894
  });
45079
45895
 
45896
+ // src/agent/modify.ts
45897
+ var exports_modify2 = {};
45898
+ __export(exports_modify2, {
45899
+ updateAgentLLMConfig: () => updateAgentLLMConfig2,
45900
+ unlinkToolsFromAgent: () => unlinkToolsFromAgent2,
45901
+ linkToolsToAgent: () => linkToolsToAgent2
45902
+ });
45903
+ async function updateAgentLLMConfig2(agentId, modelHandle, updateArgs, preserveParallelToolCalls) {
45904
+ const client = await getClient2();
45905
+ const currentAgent = await client.agents.retrieve(agentId);
45906
+ const originalParallelToolCalls = preserveParallelToolCalls ? currentAgent.llm_config?.parallel_tool_calls ?? undefined : undefined;
45907
+ const updatedLlmConfig = {
45908
+ ...currentAgent.llm_config,
45909
+ ...updateArgs,
45910
+ ...originalParallelToolCalls !== undefined && {
45911
+ parallel_tool_calls: originalParallelToolCalls
45912
+ }
45913
+ };
45914
+ await client.agents.modify(agentId, {
45915
+ llm_config: updatedLlmConfig,
45916
+ parallel_tool_calls: originalParallelToolCalls
45917
+ });
45918
+ const finalAgent = await client.agents.retrieve(agentId);
45919
+ return finalAgent.llm_config;
45920
+ }
45921
+ async function linkToolsToAgent2(agentId) {
45922
+ try {
45923
+ const client = await getClient2();
45924
+ const agent = await client.agents.retrieve(agentId);
45925
+ const currentTools = agent.tools || [];
45926
+ const currentToolIds = currentTools.map((t) => t.id).filter((id) => typeof id === "string");
45927
+ const currentToolNames = new Set(currentTools.map((t) => t.name).filter((name) => typeof name === "string"));
45928
+ const lettaCodeToolNames = getToolNames();
45929
+ const toolsToAdd = lettaCodeToolNames.filter((name) => !currentToolNames.has(name));
45930
+ if (toolsToAdd.length === 0) {
45931
+ return {
45932
+ success: true,
45933
+ message: "All Letta Code tools already attached",
45934
+ addedCount: 0
45935
+ };
45936
+ }
45937
+ const toolsToAddIds = [];
45938
+ for (const toolName of toolsToAdd) {
45939
+ const tools = await client.tools.list({ name: toolName });
45940
+ const tool = tools[0];
45941
+ if (tool?.id) {
45942
+ toolsToAddIds.push(tool.id);
45943
+ }
45944
+ }
45945
+ const newToolIds = [...currentToolIds, ...toolsToAddIds];
45946
+ const currentToolRules = agent.tool_rules || [];
45947
+ const newToolRules = [
45948
+ ...currentToolRules,
45949
+ ...toolsToAdd.map((toolName) => ({
45950
+ tool_name: toolName,
45951
+ type: "requires_approval",
45952
+ prompt_template: null
45953
+ }))
45954
+ ];
45955
+ await client.agents.modify(agentId, {
45956
+ tool_ids: newToolIds,
45957
+ tool_rules: newToolRules
45958
+ });
45959
+ return {
45960
+ success: true,
45961
+ message: `Attached ${toolsToAddIds.length} Letta Code tool(s) to agent`,
45962
+ addedCount: toolsToAddIds.length
45963
+ };
45964
+ } catch (error) {
45965
+ return {
45966
+ success: false,
45967
+ message: `Failed: ${error instanceof Error ? error.message : String(error)}`
45968
+ };
45969
+ }
45970
+ }
45971
+ async function unlinkToolsFromAgent2(agentId) {
45972
+ try {
45973
+ const client = await getClient2();
45974
+ const agent = await client.agents.retrieve(agentId);
45975
+ const allTools = agent.tools || [];
45976
+ const lettaCodeToolNames = new Set(getToolNames());
45977
+ const remainingTools = allTools.filter((t) => t.name && !lettaCodeToolNames.has(t.name));
45978
+ const removedCount = allTools.length - remainingTools.length;
45979
+ const remainingToolIds = remainingTools.map((t) => t.id).filter((id) => typeof id === "string");
45980
+ const currentToolRules = agent.tool_rules || [];
45981
+ const remainingToolRules = currentToolRules.filter((rule) => rule.type !== "requires_approval" || !lettaCodeToolNames.has(rule.tool_name));
45982
+ await client.agents.modify(agentId, {
45983
+ tool_ids: remainingToolIds,
45984
+ tool_rules: remainingToolRules
45985
+ });
45986
+ return {
45987
+ success: true,
45988
+ message: `Removed ${removedCount} Letta Code tool(s) from agent`,
45989
+ removedCount
45990
+ };
45991
+ } catch (error) {
45992
+ return {
45993
+ success: false,
45994
+ message: `Failed: ${error instanceof Error ? error.message : String(error)}`
45995
+ };
45996
+ }
45997
+ }
45998
+ var init_modify2 = __esm(() => {
45999
+ init_manager();
46000
+ init_client2();
46001
+ });
46002
+
45080
46003
  // src/agent/create.ts
45081
46004
  var exports_create = {};
45082
46005
  __export(exports_create, {
45083
46006
  createAgent: () => createAgent2
45084
46007
  });
45085
- async function createAgent2(name = "letta-cli-agent", model, embeddingModel = "openai/text-embedding-3-small", updateArgs, forceNewBlocks = false) {
46008
+ import { join as join9 } from "node:path";
46009
+ async function createAgent2(name = "letta-cli-agent", model, embeddingModel = "openai/text-embedding-3-small", updateArgs, forceNewBlocks = false, skillsDirectory, parallelToolCalls = true, enableSleeptime = false) {
45086
46010
  let modelHandle;
45087
46011
  if (model) {
45088
46012
  const resolved = resolveModel(model);
@@ -45105,6 +46029,22 @@ async function createAgent2(name = "letta-cli-agent", model, embeddingModel = "o
45105
46029
  "fetch_webpage"
45106
46030
  ];
45107
46031
  const defaultMemoryBlocks = await getDefaultMemoryBlocks();
46032
+ const resolvedSkillsDirectory = skillsDirectory || join9(process.cwd(), SKILLS_DIR);
46033
+ try {
46034
+ const { skills, errors } = await discoverSkills(resolvedSkillsDirectory);
46035
+ if (errors.length > 0) {
46036
+ console.warn("Errors encountered during skill discovery:");
46037
+ for (const error of errors) {
46038
+ console.warn(` ${error.path}: ${error.message}`);
46039
+ }
46040
+ }
46041
+ const skillsBlock = defaultMemoryBlocks.find((b) => b.label === "skills");
46042
+ if (skillsBlock) {
46043
+ skillsBlock.value = formatSkillsForMemory(skills, resolvedSkillsDirectory);
46044
+ }
46045
+ } catch (error) {
46046
+ console.warn(`Failed to discover skills: ${error instanceof Error ? error.message : String(error)}`);
46047
+ }
45108
46048
  const settings = settingsManager.getSettings();
45109
46049
  const globalSharedBlockIds = settings.globalSharedBlockIds;
45110
46050
  await settingsManager.loadProjectSettings();
@@ -45151,7 +46091,7 @@ async function createAgent2(name = "letta-cli-agent", model, embeddingModel = "o
45151
46091
  throw new Error(`Created block ${label} has no ID`);
45152
46092
  }
45153
46093
  blockIds.push(createdBlock.id);
45154
- if (label === "project") {
46094
+ if (label === "project" || label === "skills") {
45155
46095
  newLocalBlockIds[label] = createdBlock.id;
45156
46096
  } else {
45157
46097
  newGlobalBlockIds[label] = createdBlock.id;
@@ -45177,25 +46117,31 @@ async function createAgent2(name = "letta-cli-agent", model, embeddingModel = "o
45177
46117
  }
45178
46118
  }, process.cwd());
45179
46119
  }
46120
+ const modelUpdateArgs = getModelUpdateArgs(modelHandle);
46121
+ const contextWindow = modelUpdateArgs?.context_window || 200000;
45180
46122
  const agent = await client.agents.create({
45181
46123
  agent_type: "letta_v1_agent",
45182
46124
  system: SYSTEM_PROMPT,
45183
46125
  name,
45184
46126
  embedding: embeddingModel,
45185
46127
  model: modelHandle,
45186
- context_window_limit: 200000,
46128
+ context_window_limit: contextWindow,
45187
46129
  tools: toolNames,
45188
46130
  block_ids: blockIds,
45189
46131
  tags: ["origin:letta-code"],
45190
46132
  include_base_tools: false,
45191
46133
  include_base_tool_rules: false,
45192
- initial_message_sequence: []
46134
+ initial_message_sequence: [],
46135
+ parallel_tool_calls: parallelToolCalls,
46136
+ enable_sleeptime: enableSleeptime
45193
46137
  });
45194
46138
  if (updateArgs && Object.keys(updateArgs).length > 0) {
45195
- await updateAgentLLMConfig(agent.id, modelHandle, updateArgs);
45196
- return await client.agents.retrieve(agent.id);
46139
+ const { context_window, ...otherArgs } = updateArgs;
46140
+ if (Object.keys(otherArgs).length > 0) {
46141
+ await updateAgentLLMConfig(agent.id, modelHandle, otherArgs, true);
46142
+ }
45197
46143
  }
45198
- return agent;
46144
+ return await client.agents.retrieve(agent.id);
45199
46145
  }
45200
46146
  var init_create3 = __esm(() => {
45201
46147
  init_settings_manager();
@@ -45205,6 +46151,7 @@ var init_create3 = __esm(() => {
45205
46151
  init_model();
45206
46152
  init_modify();
45207
46153
  init_promptAssets();
46154
+ init_skills2();
45208
46155
  });
45209
46156
 
45210
46157
  // src/agent/model.ts
@@ -45271,11 +46218,19 @@ async function getResumeData(client, agent) {
45271
46218
  const messagesPage = await client.agents.messages.list(agent.id);
45272
46219
  const messages = messagesPage.items;
45273
46220
  if (!messages || messages.length === 0) {
45274
- return { pendingApproval: null, messageHistory: [] };
46221
+ return {
46222
+ pendingApproval: null,
46223
+ pendingApprovals: [],
46224
+ messageHistory: []
46225
+ };
45275
46226
  }
45276
46227
  const cursorLastMessage = messages[messages.length - 1];
45277
46228
  if (!cursorLastMessage) {
45278
- return { pendingApproval: null, messageHistory: [] };
46229
+ return {
46230
+ pendingApproval: null,
46231
+ pendingApprovals: [],
46232
+ messageHistory: []
46233
+ };
45279
46234
  }
45280
46235
  const inContextLastMessageId = agent.message_ids && agent.message_ids.length > 0 ? agent.message_ids[agent.message_ids.length - 1] : null;
45281
46236
  let messageToCheck = cursorLastMessage;
@@ -45296,18 +46251,17 @@ async function getResumeData(client, agent) {
45296
46251
  }
45297
46252
  }
45298
46253
  let pendingApproval = null;
46254
+ let pendingApprovals = [];
45299
46255
  if (messageToCheck.message_type === "approval_request_message") {
45300
46256
  const approvalMsg = messageToCheck;
45301
46257
  const toolCalls = Array.isArray(approvalMsg.tool_calls) ? approvalMsg.tool_calls : approvalMsg.tool_call ? [approvalMsg.tool_call] : [];
45302
- if (toolCalls.length > 0) {
45303
- const toolCall = toolCalls[0];
45304
- if (toolCall?.tool_call_id && toolCall.name && toolCall.arguments) {
45305
- pendingApproval = {
45306
- toolCallId: toolCall.tool_call_id,
45307
- toolName: toolCall.name,
45308
- toolArgs: toolCall.arguments
45309
- };
45310
- }
46258
+ pendingApprovals = toolCalls.filter((tc) => tc?.tool_call_id && tc.name && tc.arguments).map((tc) => ({
46259
+ toolCallId: tc.tool_call_id,
46260
+ toolName: tc.name,
46261
+ toolArgs: tc.arguments
46262
+ }));
46263
+ if (pendingApprovals.length > 0) {
46264
+ pendingApproval = pendingApprovals[0] || null;
45311
46265
  }
45312
46266
  }
45313
46267
  const historyCount = Math.min(MESSAGE_HISTORY_LIMIT, messages.length);
@@ -45315,10 +46269,10 @@ async function getResumeData(client, agent) {
45315
46269
  if (messageHistory[0]?.message_type === "tool_return_message") {
45316
46270
  messageHistory = messageHistory.slice(1);
45317
46271
  }
45318
- return { pendingApproval, messageHistory };
46272
+ return { pendingApproval, pendingApprovals, messageHistory };
45319
46273
  } catch (error) {
45320
46274
  console.error("Error getting resume data:", error);
45321
- return { pendingApproval: null, messageHistory: [] };
46275
+ return { pendingApproval: null, pendingApprovals: [], messageHistory: [] };
45322
46276
  }
45323
46277
  }
45324
46278
 
@@ -45443,6 +46397,8 @@ var DEFAULT_SETTINGS2 = {
45443
46397
  uiMode: "simple",
45444
46398
  lastAgent: null,
45445
46399
  tokenStreaming: false,
46400
+ parallelToolCalls: true,
46401
+ enableSleeptime: false,
45446
46402
  globalSharedBlockIds: {}
45447
46403
  };
45448
46404
  var DEFAULT_PROJECT_SETTINGS2 = {
@@ -45806,6 +46762,7 @@ OPTIONS
45806
46762
  -p, --prompt Headless prompt mode
45807
46763
  --output-format <fmt> Output format for headless mode (text, json, stream-json)
45808
46764
  Default: text
46765
+ --skills <path> Custom path to skills directory (default: .skills in current directory)
45809
46766
 
45810
46767
  BEHAVIOR
45811
46768
  By default, letta auto-resumes the last agent used in the current directory
@@ -45856,7 +46813,10 @@ async function main() {
45856
46813
  disallowedTools: { type: "string" },
45857
46814
  "permission-mode": { type: "string" },
45858
46815
  yolo: { type: "boolean" },
45859
- "output-format": { type: "string" }
46816
+ "output-format": { type: "string" },
46817
+ skills: { type: "string" },
46818
+ link: { type: "boolean" },
46819
+ unlink: { type: "boolean" }
45860
46820
  },
45861
46821
  strict: true,
45862
46822
  allowPositionals: true
@@ -45889,6 +46849,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
45889
46849
  const forceNew = values.new ?? false;
45890
46850
  const specifiedAgentId = values.agent ?? null;
45891
46851
  const specifiedModel = values.model ?? undefined;
46852
+ const skillsDirectory = values.skills ?? undefined;
45892
46853
  const isHeadless = values.prompt || values.run || !process.stdin.isTTY;
45893
46854
  const apiKey = process.env.LETTA_API_KEY || settings.env?.LETTA_API_KEY;
45894
46855
  const baseURL = process.env.LETTA_BASE_URL || settings.env?.LETTA_BASE_URL || "https://api.letta.com";
@@ -45958,37 +46919,59 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
45958
46919
  }
45959
46920
  }
45960
46921
  }
46922
+ const shouldLink = values.link;
46923
+ const shouldUnlink = values.unlink;
46924
+ if (shouldLink || shouldUnlink) {
46925
+ if (!specifiedAgentId) {
46926
+ console.error(`Error: --${shouldLink ? "link" : "unlink"} requires --agent <id>`);
46927
+ process.exit(1);
46928
+ }
46929
+ }
45961
46930
  if (isHeadless) {
45962
46931
  await loadTools();
45963
46932
  const client = await getClient();
45964
46933
  await upsertToolsToServer(client);
45965
46934
  const { handleHeadlessCommand: handleHeadlessCommand2 } = await Promise.resolve().then(() => (init_headless(), exports_headless));
45966
- await handleHeadlessCommand2(process.argv, specifiedModel);
46935
+ await handleHeadlessCommand2(process.argv, specifiedModel, skillsDirectory);
45967
46936
  return;
45968
46937
  }
45969
46938
  const React13 = await Promise.resolve().then(() => __toESM(require_react2(), 1));
45970
46939
  const { render: render2 } = await init_build3().then(() => exports_build);
45971
- const { useState: useState15, useEffect: useEffect12 } = React13;
46940
+ const { useState: useState15, useEffect: useEffect13 } = React13;
45972
46941
  const AppModule = await init_App2().then(() => exports_App);
45973
46942
  const App3 = AppModule.default;
45974
46943
  function LoadingApp({
45975
46944
  continueSession,
45976
46945
  forceNew: forceNew2,
45977
46946
  agentIdArg,
45978
- model
46947
+ model,
46948
+ skillsDirectory: skillsDirectory2
45979
46949
  }) {
45980
46950
  const [loadingState, setLoadingState] = useState15("assembling");
45981
46951
  const [agentId, setAgentId] = useState15(null);
45982
46952
  const [agentState, setAgentState] = useState15(null);
45983
46953
  const [resumeData, setResumeData] = useState15(null);
45984
46954
  const [isResumingSession, setIsResumingSession] = useState15(false);
45985
- useEffect12(() => {
46955
+ useEffect13(() => {
45986
46956
  async function init() {
45987
46957
  setLoadingState("assembling");
45988
46958
  await loadTools();
45989
46959
  setLoadingState("upserting");
45990
46960
  const client = await getClient();
45991
46961
  await upsertToolsToServer(client);
46962
+ if (shouldLink || shouldUnlink) {
46963
+ if (!agentIdArg) {
46964
+ console.error("Error: --link/--unlink requires --agent <id>");
46965
+ process.exit(1);
46966
+ }
46967
+ setLoadingState(shouldLink ? "linking" : "unlinking");
46968
+ const { linkToolsToAgent: linkToolsToAgent3, unlinkToolsFromAgent: unlinkToolsFromAgent3 } = await Promise.resolve().then(() => (init_modify2(), exports_modify2));
46969
+ const result = shouldLink ? await linkToolsToAgent3(agentIdArg) : await unlinkToolsFromAgent3(agentIdArg);
46970
+ if (!result.success) {
46971
+ console.error(`\u2717 ${result.message}`);
46972
+ process.exit(1);
46973
+ }
46974
+ }
45992
46975
  setLoadingState("initializing");
45993
46976
  const { createAgent: createAgent3 } = await Promise.resolve().then(() => (init_create3(), exports_create));
45994
46977
  const { getModelUpdateArgs: getModelUpdateArgs3 } = await Promise.resolve().then(() => (init_model2(), exports_model2));
@@ -45997,12 +46980,15 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
45997
46980
  try {
45998
46981
  agent = await client.agents.retrieve(agentIdArg);
45999
46982
  } catch (error) {
46000
- console.error(`Agent ${agentIdArg} not found (error: ${JSON.stringify(error)}), creating new one...`);
46983
+ console.error(`Agent ${agentIdArg} not found (error: ${JSON.stringify(error)})`);
46984
+ console.error("When using --agent, the specified agent ID must exist.");
46985
+ console.error("Run 'letta' without --agent to create a new agent.");
46986
+ process.exit(1);
46001
46987
  }
46002
46988
  }
46003
46989
  if (!agent && forceNew2) {
46004
46990
  const updateArgs = getModelUpdateArgs3(model);
46005
- agent = await createAgent3(undefined, model, undefined, updateArgs, forceNew2);
46991
+ agent = await createAgent3(undefined, model, undefined, updateArgs, forceNew2, skillsDirectory2, settings.parallelToolCalls, settings.enableSleeptime);
46006
46992
  }
46007
46993
  if (!agent) {
46008
46994
  await settingsManager2.loadLocalProjectSettings();
@@ -46024,7 +47010,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
46024
47010
  }
46025
47011
  if (!agent) {
46026
47012
  const updateArgs = getModelUpdateArgs3(model);
46027
- agent = await createAgent3(undefined, model, undefined, updateArgs);
47013
+ agent = await createAgent3(undefined, model, undefined, updateArgs, false, skillsDirectory2, settings.parallelToolCalls, settings.enableSleeptime);
46028
47014
  }
46029
47015
  try {
46030
47016
  settingsManager2.getLocalProjectSettings();
@@ -46054,6 +47040,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
46054
47040
  loadingState,
46055
47041
  continueSession: isResumingSession,
46056
47042
  startupApproval: resumeData?.pendingApproval ?? null,
47043
+ startupApprovals: resumeData?.pendingApprovals ?? [],
46057
47044
  messageHistory: resumeData?.messageHistory ?? [],
46058
47045
  tokenStreaming: settings.tokenStreaming
46059
47046
  });
@@ -46064,6 +47051,7 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
46064
47051
  loadingState,
46065
47052
  continueSession: isResumingSession,
46066
47053
  startupApproval: resumeData?.pendingApproval ?? null,
47054
+ startupApprovals: resumeData?.pendingApprovals ?? [],
46067
47055
  messageHistory: resumeData?.messageHistory ?? [],
46068
47056
  tokenStreaming: settings.tokenStreaming
46069
47057
  });
@@ -46072,11 +47060,12 @@ Note: Flags should use double dashes for full names (e.g., --yolo, not -yolo)`);
46072
47060
  continueSession: shouldContinue,
46073
47061
  forceNew,
46074
47062
  agentIdArg: specifiedAgentId,
46075
- model: specifiedModel
47063
+ model: specifiedModel,
47064
+ skillsDirectory
46076
47065
  }), {
46077
47066
  exitOnCtrlC: false
46078
47067
  });
46079
47068
  }
46080
47069
  main();
46081
47070
 
46082
- //# debugId=2DFAA80260F695BF64756E2164756E21
47071
+ //# debugId=924A0340512AB0DC64756E2164756E21