@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.
- package/README.md +103 -0
- package/letta.js +1465 -476
- 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 (
|
|
72
|
-
uuid4 =
|
|
73
|
-
return
|
|
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 =
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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:
|
|
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.
|
|
35258
|
-
const
|
|
35259
|
-
|
|
35260
|
-
|
|
35261
|
-
|
|
35262
|
-
|
|
35263
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
35387
|
-
|
|
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 =
|
|
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
|
|
35646
|
-
|
|
35647
|
-
|
|
35648
|
-
|
|
35649
|
-
|
|
35650
|
-
|
|
35651
|
-
|
|
35652
|
-
|
|
35653
|
-
|
|
35654
|
-
|
|
35655
|
-
|
|
35656
|
-
|
|
35657
|
-
|
|
35658
|
-
|
|
35659
|
-
|
|
35660
|
-
|
|
35661
|
-
|
|
35662
|
-
|
|
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
|
|
35715
|
-
|
|
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
|
-
|
|
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
|
-
|
|
36150
|
+
_approvalRequestId = chunk.id;
|
|
35744
36151
|
}
|
|
35745
|
-
if (chunk.message_type === "
|
|
35746
|
-
const
|
|
35747
|
-
|
|
35748
|
-
if (
|
|
35749
|
-
|
|
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
|
-
|
|
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
|
-
|
|
35781
|
-
|
|
35782
|
-
|
|
35783
|
-
|
|
35784
|
-
|
|
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
|
-
|
|
35795
|
-
|
|
36196
|
+
pendingApprovals.clear();
|
|
36197
|
+
_approvalRequestId = null;
|
|
35796
36198
|
}
|
|
35797
36199
|
const apiDurationMs = performance.now() - startTime;
|
|
35798
|
-
return {
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
35860
|
-
|
|
35861
|
-
|
|
35862
|
-
|
|
35863
|
-
|
|
35864
|
-
|
|
35865
|
-
|
|
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
|
-
|
|
36483
|
+
const pendingApprovals = resume.pendingApprovals || [];
|
|
36484
|
+
if (pendingApprovals.length === 0)
|
|
35981
36485
|
break;
|
|
35982
|
-
const
|
|
35983
|
-
const
|
|
35984
|
-
|
|
35985
|
-
|
|
35986
|
-
|
|
35987
|
-
|
|
35988
|
-
|
|
35989
|
-
|
|
35990
|
-
|
|
35991
|
-
|
|
35992
|
-
|
|
35993
|
-
|
|
35994
|
-
|
|
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
|
-
|
|
36001
|
-
type: "
|
|
36002
|
-
|
|
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
|
-
|
|
36007
|
-
|
|
36008
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
36095
|
-
|
|
36096
|
-
|
|
36097
|
-
|
|
36098
|
-
|
|
36099
|
-
|
|
36100
|
-
|
|
36101
|
-
|
|
36102
|
-
|
|
36103
|
-
|
|
36104
|
-
|
|
36105
|
-
|
|
36106
|
-
|
|
36107
|
-
|
|
36108
|
-
|
|
36109
|
-
|
|
36110
|
-
|
|
36111
|
-
|
|
36112
|
-
|
|
36113
|
-
|
|
36114
|
-
|
|
36115
|
-
|
|
36116
|
-
|
|
36117
|
-
|
|
36118
|
-
|
|
36119
|
-
|
|
36120
|
-
|
|
36121
|
-
|
|
36122
|
-
|
|
36123
|
-
|
|
36124
|
-
|
|
36125
|
-
|
|
36126
|
-
|
|
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
|
-
|
|
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 (
|
|
36163
|
-
console.error("Unexpected
|
|
36677
|
+
if (approvals.length === 0) {
|
|
36678
|
+
console.error("Unexpected empty approvals array");
|
|
36164
36679
|
process.exit(1);
|
|
36165
36680
|
}
|
|
36166
|
-
const
|
|
36167
|
-
const
|
|
36168
|
-
|
|
36169
|
-
|
|
36170
|
-
const
|
|
36171
|
-
|
|
36172
|
-
{
|
|
36173
|
-
|
|
36174
|
-
|
|
36175
|
-
|
|
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
|
-
|
|
36180
|
-
|
|
36181
|
-
|
|
36182
|
-
|
|
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
|
-
|
|
36191
|
-
|
|
36192
|
-
|
|
36193
|
-
|
|
36194
|
-
|
|
36195
|
-
|
|
36196
|
-
|
|
36197
|
-
|
|
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
|
-
|
|
36712
|
+
});
|
|
36713
|
+
continue;
|
|
36714
|
+
}
|
|
36715
|
+
decisions.push({
|
|
36716
|
+
type: "approve",
|
|
36717
|
+
approval: currentApproval
|
|
36718
|
+
});
|
|
36206
36719
|
}
|
|
36207
|
-
const
|
|
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:
|
|
37965
|
+
inline: "green"
|
|
37461
37966
|
},
|
|
37462
37967
|
link: {
|
|
37463
|
-
text:
|
|
37464
|
-
url:
|
|
37968
|
+
text: "cyan",
|
|
37969
|
+
url: "blue"
|
|
37465
37970
|
},
|
|
37466
37971
|
heading: {
|
|
37467
|
-
primary:
|
|
37468
|
-
secondary:
|
|
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
|
|
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) &&
|
|
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
|
-
|
|
38644
|
-
|
|
38645
|
-
|
|
39148
|
+
approvals,
|
|
39149
|
+
approvalContexts,
|
|
39150
|
+
progress,
|
|
39151
|
+
totalTools,
|
|
39152
|
+
isExecuting,
|
|
39153
|
+
onApproveAll,
|
|
38646
39154
|
onApproveAlways,
|
|
38647
|
-
|
|
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
|
|
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:
|
|
39178
|
+
label: denyLabel,
|
|
38662
39179
|
action: () => {}
|
|
38663
39180
|
});
|
|
38664
39181
|
return opts;
|
|
38665
|
-
}, [approvalContext,
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
42232
|
-
|
|
42233
|
-
|
|
42234
|
-
|
|
42235
|
-
|
|
42236
|
-
|
|
42237
|
-
|
|
42238
|
-
|
|
42239
|
-
|
|
42240
|
-
|
|
42241
|
-
|
|
42242
|
-
|
|
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
|
-
|
|
42249
|
-
|
|
42250
|
-
|
|
42802
|
+
flexDirection: "column",
|
|
42803
|
+
children: [
|
|
42804
|
+
agentName && /* @__PURE__ */ jsx_dev_runtime11.jsxDEV(Text, {
|
|
42251
42805
|
dimColor: true,
|
|
42252
|
-
children:
|
|
42253
|
-
|
|
42254
|
-
|
|
42255
|
-
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
44020
|
-
const toolCall = toolCalls[
|
|
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
|
-
|
|
44024
|
-
|
|
44025
|
-
buffers.byId.
|
|
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:
|
|
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(
|
|
44035
|
-
buffers.toolCallIdToLineId.set(toolCallId,
|
|
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
|
|
44041
|
-
|
|
44042
|
-
|
|
44043
|
-
|
|
44044
|
-
|
|
44045
|
-
|
|
44046
|
-
|
|
44047
|
-
|
|
44048
|
-
|
|
44049
|
-
|
|
44050
|
-
|
|
44051
|
-
|
|
44052
|
-
|
|
44053
|
-
|
|
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 [
|
|
44135
|
-
const [
|
|
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
|
-
|
|
44211
|
-
|
|
44212
|
-
|
|
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:
|
|
44217
|
-
toolArgs:
|
|
44825
|
+
toolCallId: planApproval.toolCallId,
|
|
44826
|
+
toolArgs: planApproval.toolArgs
|
|
44218
44827
|
});
|
|
44219
44828
|
} else {
|
|
44220
|
-
|
|
44221
|
-
const
|
|
44829
|
+
setPendingApprovals(approvals);
|
|
44830
|
+
const analyzeStartupApprovals = async () => {
|
|
44222
44831
|
try {
|
|
44223
|
-
const
|
|
44224
|
-
|
|
44225
|
-
|
|
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
|
|
44838
|
+
console.error("Failed to analyze startup approvals:", error);
|
|
44228
44839
|
}
|
|
44229
44840
|
};
|
|
44230
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
44312
|
-
|
|
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
|
|
44317
|
-
if (
|
|
44318
|
-
const
|
|
44319
|
-
const plan =
|
|
44320
|
-
setPlanApprovalPending({
|
|
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
|
|
44325
|
-
|
|
44326
|
-
|
|
44327
|
-
const
|
|
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
|
-
|
|
44333
|
-
approve: false,
|
|
44334
|
-
reason: denyReason
|
|
44997
|
+
approvals: allResults
|
|
44335
44998
|
}
|
|
44336
44999
|
]);
|
|
44337
45000
|
return;
|
|
44338
45001
|
}
|
|
44339
|
-
|
|
44340
|
-
|
|
44341
|
-
|
|
44342
|
-
|
|
44343
|
-
|
|
44344
|
-
|
|
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
|
-
|
|
45429
|
+
setPendingApprovals([existingApproval]);
|
|
44664
45430
|
const parsedArgs = safeJsonParseOr(existingApproval.toolArgs, {});
|
|
44665
45431
|
const context = await analyzeToolApproval(existingApproval.toolName, parsedArgs);
|
|
44666
|
-
|
|
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
|
|
44692
|
-
|
|
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
|
|
44698
|
-
|
|
44699
|
-
|
|
44700
|
-
|
|
44701
|
-
|
|
44702
|
-
|
|
44703
|
-
|
|
44704
|
-
|
|
44705
|
-
|
|
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
|
-
}, [
|
|
45529
|
+
}, [pendingApprovals, approvalResults, sendAllResults, appendError]);
|
|
44731
45530
|
const handleApproveAlways = import_react40.useCallback(async (scope) => {
|
|
44732
|
-
if (
|
|
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
|
-
|
|
44748
|
-
|
|
44749
|
-
|
|
44750
|
-
|
|
44751
|
-
|
|
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
|
-
|
|
44757
|
-
|
|
44758
|
-
|
|
44759
|
-
|
|
44760
|
-
|
|
44761
|
-
|
|
44762
|
-
|
|
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
|
-
}, [
|
|
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 &&
|
|
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 &&
|
|
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
|
-
|
|
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
|
-
|
|
45036
|
-
|
|
45037
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
45196
|
-
|
|
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 {
|
|
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 {
|
|
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
|
-
|
|
45303
|
-
|
|
45304
|
-
|
|
45305
|
-
|
|
45306
|
-
|
|
45307
|
-
|
|
45308
|
-
|
|
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:
|
|
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
|
-
|
|
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)})
|
|
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=
|
|
47071
|
+
//# debugId=924A0340512AB0DC64756E2164756E21
|