diff --git a/navi/core/agent.py b/navi/core/agent.py index 7b6b716..79814dc 100644 --- a/navi/core/agent.py +++ b/navi/core/agent.py @@ -241,6 +241,7 @@ profile_id: str, max_iterations: int = 20, exclude_tools: list[str] | None = None, + briefing: str | None = None, custom_system_prompt: str | None = None, context_transfer: str | None = None, timeout_seconds: float = 300.0, @@ -256,10 +257,13 @@ Tools listed in exclude_tools are stripped from the tool list (use this to prevent recursion: exclude 'spawn_agent'). - custom_system_prompt: injected after the profile's system prompt (overrides - profile.subagent_system_prompt if provided). - context_transfer: text passed from the parent's scratchpad context_transfer - section, injected as a priming exchange before the task message. + System prompt structure (completely separate from the parent's system prompt): + 1. profile.subagent_system_prompt — focused executor persona + 2. custom_system_prompt — optional role specialisation for this task + 3. briefing — task context (credentials, paths, instructions) + + context_transfer: text from the parent's scratchpad context_transfer section, + injected as a priming exchange before the task message. timeout_seconds: wall-clock timeout for the entire sub-agent run. """ import time as _time @@ -281,13 +285,20 @@ mem = await self._memory_msg() - # Build subagent system prompt: profile.system_prompt + subagent-specific prompt. - # No persona, no profiles block — subagents are focused executors. - effective_subagent_prompt = custom_system_prompt or profile.subagent_system_prompt - subagent_sys_content = profile.system_prompt - if effective_subagent_prompt: - subagent_sys_content = subagent_sys_content + "\n\n---\n\n" + effective_subagent_prompt - subagent_sys_msg = Message(role="system", content=subagent_sys_content) + # Build subagent system prompt — completely separate from the parent's system prompt. + # No persona, no orchestrator instructions, no profiles block. + # Structure: executor persona → role specialisation → task context (briefing) + sys_parts: list[str] = [] + if profile.subagent_system_prompt: + sys_parts.append(profile.subagent_system_prompt) + if custom_system_prompt: + sys_parts.append(custom_system_prompt) + if briefing: + sys_parts.append(f"## Task context\n\n{briefing}") + if not sys_parts: + # Fallback if profile has no subagent_system_prompt defined + sys_parts.append(profile.system_prompt) + subagent_sys_msg = Message(role="system", content="\n\n---\n\n".join(sys_parts)) # Build initial context. # If context_transfer is provided, inject it as a priming exchange so the diff --git a/navi/tools/spawn_agent.py b/navi/tools/spawn_agent.py index b58b94e..2e54083 100644 --- a/navi/tools/spawn_agent.py +++ b/navi/tools/spawn_agent.py @@ -41,9 +41,10 @@ "briefing": { "type": "string", "description": ( - "All context the sub-agent needs — it has zero knowledge of your conversation. " + "All context the sub-agent needs, injected as a system-level instruction. " "Include: IPs, credentials, file paths, prior findings, constraints, " - "step-by-step instructions if helpful." + "step-by-step instructions. The sub-agent has zero knowledge of your " + "conversation — include everything it needs to act autonomously." ), }, "profile_id": { @@ -91,19 +92,13 @@ from navi.tools.scratchpad import get_section task = params["task"].strip() - briefing = (params.get("briefing") or "").strip() + briefing = (params.get("briefing") or "").strip() or None custom_system_prompt = (params.get("system_prompt") or "").strip() or None max_iterations = int(params.get("max_iterations") or 20) - # Compose user message: briefing provides context, task states the goal. - if briefing: - user_message = ( - f"## Context\n\n{briefing}\n\n" - f"---\n\n" - f"## Task\n\n{task}" - ) - else: - user_message = task + # task → user message (what to do) + # briefing → system prompt (context, credentials, instructions) + user_message = task # Resolve profile: explicit override → parent session's profile → first available profile_id = params.get("profile_id", "").strip() @@ -117,7 +112,7 @@ log.info("spawn_agent.start", profile_id=profile_id, max_iterations=max_iterations, task_preview=task[:80], has_briefing=bool(briefing), has_context_transfer=bool(context_transfer), - has_custom_prompt=bool(custom_system_prompt)) + has_system_prompt=bool(custom_system_prompt)) agent = Agent( session_store=None, # ephemeral — no DB access @@ -134,6 +129,7 @@ profile_id=profile_id, max_iterations=max_iterations, exclude_tools=["spawn_agent"], # prevent recursion + briefing=briefing, custom_system_prompt=custom_system_prompt, context_transfer=context_transfer or None, timeout_seconds=300.0,