diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 08322c0f15..ba73e5d116 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -8,6 +8,7 @@ - Restored bare `prime-agent --resume` opening the agents view and the `/resume [id|path]` slash command; bare commands open the agents view and an argument resumes that session in place. - Fixed URLs not opening on click in fullscreen mode on terminals such as Ghostty; clicking a link in the transcript, dock, or overlays now opens it in the browser. - Fixed ctrl+p ("Toggle agent message expansion") only toggling received agent messages; it now expands and collapses sent agent messages together with received ones. +- Fixed inherited root-controller prompts overriding the role of recursive child agents. ## [0.7.2] - 2026-08-11 diff --git a/packages/coding-agent/src/core/prompts/rlm.ts b/packages/coding-agent/src/core/prompts/rlm.ts index 7e023d5278..8292e0dbf6 100644 --- a/packages/coding-agent/src/core/prompts/rlm.ts +++ b/packages/coding-agent/src/core/prompts/rlm.ts @@ -9,6 +9,8 @@ export interface RlmPromptOptions { depth?: number; parentAgent?: string; activeTools?: string[]; + /** Whether to include child-role doctrine inline. Disable when a higher-level prompt builder appends it last. */ + includeChildDoctrine?: boolean; } const IPYTHON_CONTROL_PROMPT = [ @@ -79,9 +81,11 @@ export function buildRlmPrompt(options: RlmPromptOptions): string { "Install additional packages with `uv pip install ` (this is a uv-managed venv with no pip module).", ]; - const childDoctrine = buildChildAgentDoctrine(options); - if (childDoctrine) { - parts.push("", childDoctrine); + if (options.includeChildDoctrine ?? true) { + const childDoctrine = buildChildAgentDoctrine(options); + if (childDoctrine) { + parts.push("", childDoctrine); + } } const skillLines: string[] = []; diff --git a/packages/coding-agent/src/core/system-prompt.ts b/packages/coding-agent/src/core/system-prompt.ts index 23b78de367..a78a571643 100644 --- a/packages/coding-agent/src/core/system-prompt.ts +++ b/packages/coding-agent/src/core/system-prompt.ts @@ -68,6 +68,12 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions): string { const visibleSkills = skills.filter((skill) => !skill.disableModelInvocation); const visiblePythonSkillImportNames = getPythonSkillRuntimeInfo(visibleSkills).map((skill) => skill.importName); const hasRefineSkill = visibleSkills.some((skill) => skill.name === REFINE_SKILL_NAME); + const childDoctrine = buildChildAgentDoctrine({ + depth: options.rlmDepth, + parentAgent: options.rlmParentAgent, + installedSkills: visiblePythonSkillImportNames, + activeTools: tools, + }); if (customPrompt) { let prompt = customPrompt; @@ -92,16 +98,6 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions): string { prompt += `\nCurrent date: ${date}`; prompt += `\nCurrent working directory: ${promptCwd}`; - const childDoctrine = buildChildAgentDoctrine({ - depth: options.rlmDepth, - parentAgent: options.rlmParentAgent, - installedSkills: visiblePythonSkillImportNames, - activeTools: tools, - }); - if (childDoctrine) { - prompt += `\n\n${childDoctrine}`; - } - if (harnessState) { prompt += `\n\n${formatHarnessStateForPrompt(harnessState, { includeIpythonExamples: hasIpython, includeShellExamples: hasBash, includeRefineExamples: hasIpython && hasRefineSkill })}`; } @@ -110,6 +106,10 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions): string { prompt += appendSection; } + if (childDoctrine) { + prompt += `\n\n${childDoctrine}`; + } + return prompt; } @@ -121,6 +121,7 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions): string { allowRecursion, depth: options.rlmDepth, parentAgent: options.rlmParentAgent, + includeChildDoctrine: false, }); // Appended AFTER the trained buildRlmPrompt prefix, and before the harness-state @@ -165,6 +166,10 @@ export function buildSystemPrompt(options: BuildSystemPromptOptions): string { prompt += appendSection; } + if (childDoctrine) { + prompt += `\n\n${childDoctrine}`; + } + return prompt; } diff --git a/packages/coding-agent/test/system-prompt.test.ts b/packages/coding-agent/test/system-prompt.test.ts index bbb128ef45..6c077e5f70 100644 --- a/packages/coding-agent/test/system-prompt.test.ts +++ b/packages/coding-agent/test/system-prompt.test.ts @@ -597,9 +597,10 @@ describe("buildSystemPrompt", () => { expect(prompt.indexOf("# Continual Harness State")).toBeLessThan(prompt.indexOf("custom append")); }); - test("adds child reply doctrine to custom prompts when messaging is available", () => { + test("gives child doctrine final precedence over inherited controller prompts", () => { const prompt = buildSystemPrompt({ - customPrompt: "custom body", + customPrompt: "You are the root controller.", + appendSystemPrompt: "Spawn a child and poll it until completion.", selectedTools: ["ipython"], contextFiles: [], skills: [pythonSkill("agent-message")], @@ -608,11 +609,33 @@ describe("buildSystemPrompt", () => { rlmParentAgent: "orchestrator", }); + expect(prompt).toContain("You are the root controller."); + expect(prompt).toContain("Spawn a child and poll it until completion."); expect(prompt).toContain("You are a child agent spawned by orchestrator"); + expect(prompt.indexOf("Spawn a child and poll it until completion.")).toBeLessThan( + prompt.indexOf("You are a child agent spawned by orchestrator"), + ); expect(prompt).toContain('await agent_message.send(message, receiver_role="parent")'); expect(prompt).not.toContain("You are a general purpose agent that uses code to solve tasks."); }); + test("keeps recursive delegation available when child doctrine follows appended instructions", () => { + const prompt = buildSystemPrompt({ + selectedTools: ["ipython"], + appendSystemPrompt: "You are the root controller.", + contextFiles: [], + skills: [], + cwd: "/repo", + allowRecursion: true, + rlmDepth: 1, + }); + + expect(prompt).toContain("await rlm('sub-task')"); + expect(prompt.indexOf("You are the root controller.")).toBeLessThan( + prompt.indexOf("You are a child agent spawned by your parent agent"), + ); + }); + test("gates custom-prompt child reply doctrine on IPython and agent messaging", () => { const build = (selectedTools: string[], skills: Skill[]) => buildSystemPrompt({