fix: pre-load skills for subagents and fix dev-mode spawning (#918)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -75,9 +75,9 @@ All subsequent file operations target the worktree:
|
||||
|
||||
### Phase 2: Review Recent Conversation History
|
||||
|
||||
Use the `searching-messages` skill (pre-loaded in
|
||||
`<loaded_skills>`) to search via `letta messages search`
|
||||
and `letta messages list`.
|
||||
Use `letta messages search` and `letta messages list`
|
||||
(documented in `<loaded_skills>` below) to search the
|
||||
parent agent's conversation history.
|
||||
|
||||
**Sliding window through recent history:**
|
||||
|
||||
|
||||
@@ -517,6 +517,11 @@ function buildSubagentArgs(
|
||||
args.push("--max-turns", String(maxTurns));
|
||||
}
|
||||
|
||||
// Pre-load skills specified in the subagent config
|
||||
if (config.skills.length > 0) {
|
||||
args.push("--pre-load-skills", config.skills.join(","));
|
||||
}
|
||||
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -552,7 +557,7 @@ async function executeSubagent(
|
||||
}
|
||||
|
||||
try {
|
||||
const cliArgs = buildSubagentArgs(
|
||||
let cliArgs = buildSubagentArgs(
|
||||
type,
|
||||
config,
|
||||
model,
|
||||
@@ -566,14 +571,19 @@ async function executeSubagent(
|
||||
// Use the same binary as the current process, with fallbacks:
|
||||
// 1. LETTA_CODE_BIN env var (explicit override)
|
||||
// 2. Current process argv[1] if it's a .js file (built letta.js)
|
||||
// 3. ./letta.js if running from dev (src/index.ts)
|
||||
// 3. Dev mode: use process.execPath (bun) with the .ts script as first arg
|
||||
// 4. "letta" (global install)
|
||||
const currentScript = process.argv[1] || "";
|
||||
const lettaCmd =
|
||||
let lettaCmd =
|
||||
process.env.LETTA_CODE_BIN ||
|
||||
(currentScript.endsWith(".js") ? currentScript : null) ||
|
||||
(currentScript.includes("src/index.ts") ? "./letta.js" : null) ||
|
||||
"letta";
|
||||
// In dev mode (running .ts file via bun), use the runtime binary directly
|
||||
// and prepend the script path to the CLI args
|
||||
if (currentScript.endsWith(".ts") && !process.env.LETTA_CODE_BIN) {
|
||||
lettaCmd = process.execPath; // e.g., /path/to/bun
|
||||
cliArgs = [currentScript, ...cliArgs];
|
||||
}
|
||||
// Pass parent agent ID so subagents can access parent's context (e.g., search history)
|
||||
let parentAgentId: string | undefined;
|
||||
try {
|
||||
|
||||
@@ -118,6 +118,7 @@ export async function handleHeadlessCommand(
|
||||
"permission-mode": { type: "string" },
|
||||
yolo: { type: "boolean" },
|
||||
skills: { type: "string" },
|
||||
"pre-load-skills": { type: "string" },
|
||||
sleeptime: { type: "boolean" },
|
||||
"init-blocks": { type: "string" },
|
||||
"base-tools": { type: "string" },
|
||||
@@ -264,6 +265,7 @@ export async function handleHeadlessCommand(
|
||||
const memfsFlag = values.memfs as boolean | undefined;
|
||||
const noMemfsFlag = values["no-memfs"] as boolean | undefined;
|
||||
const fromAfFile = values["from-af"] as string | undefined;
|
||||
const preLoadSkillsRaw = values["pre-load-skills"] as string | undefined;
|
||||
const maxTurnsRaw = values["max-turns"] as string | undefined;
|
||||
|
||||
// Parse and validate max-turns if provided
|
||||
@@ -1104,6 +1106,32 @@ ${SYSTEM_REMINDER_CLOSE}
|
||||
if (skillsReminder) {
|
||||
pushPart(skillsReminder);
|
||||
}
|
||||
|
||||
// Pre-load specific skills' full content (used by subagents with skills: field)
|
||||
if (preLoadSkillsRaw) {
|
||||
const { readFile: readFileAsync } = await import("node:fs/promises");
|
||||
const skillIds = preLoadSkillsRaw
|
||||
.split(",")
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
const loadedContents: string[] = [];
|
||||
for (const skillId of skillIds) {
|
||||
const skill = skills.find((s) => s.id === skillId);
|
||||
if (skill?.path) {
|
||||
try {
|
||||
const content = await readFileAsync(skill.path, "utf-8");
|
||||
loadedContents.push(`<${skillId}>\n${content}\n</${skillId}>`);
|
||||
} catch {
|
||||
// Skill file not readable, skip
|
||||
}
|
||||
}
|
||||
}
|
||||
if (loadedContents.length > 0) {
|
||||
pushPart(
|
||||
`<loaded_skills>\n${loadedContents.join("\n\n")}\n</loaded_skills>`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Skills discovery failed, skip
|
||||
}
|
||||
|
||||
@@ -437,6 +437,7 @@ async function main(): Promise<void> {
|
||||
"include-partial-messages": { type: "boolean" },
|
||||
"from-agent": { type: "string" },
|
||||
skills: { type: "string" },
|
||||
"pre-load-skills": { type: "string" },
|
||||
sleeptime: { type: "boolean" },
|
||||
"from-af": { type: "string" },
|
||||
import: { type: "string" },
|
||||
|
||||
Reference in New Issue
Block a user