basant307/AI_Governance_Project
048
1# Fork Subagent Design2 3> Implicit fork subagent that inherits the parent's full conversation context and shares prompt cache for cost-efficient parallel task execution.4 5## Overview6 7When the Agent tool is called without `subagent_type`, it triggers an implicit **fork** — a background subagent that inherits the parent's conversation history, system prompt, and tool definitions. The fork uses `CacheSafeParams` to ensure its API requests share the same prefix as the parent's, enabling DashScope prompt cache hits.8 9## Architecture10 11```12Parent conversation: [SystemPrompt | Tools | Msg1 | Msg2 | ... | MsgN (model)]13 ↑ identical prefix for all forks ↑14 15Fork A: [...MsgN | placeholder results | "Research A"] ← shared cache16Fork B: [...MsgN | placeholder results | "Modify B"] ← shared cache17Fork C: [...MsgN | placeholder results | "Test C"] ← shared cache18```19 20## Key Components21 22### 1. FORK_AGENT (`forkSubagent.ts`)23 24Synthetic agent config, not registered in `builtInAgents`. Has a fallback `systemPrompt` but in practice uses the parent's rendered system prompt via `generationConfigOverride`.25 26### 2. CacheSafeParams Integration (`agent.ts` + `forkedQuery.ts`)27 28```29agent.ts (fork path)30 │31 ├── getCacheSafeParams() ← parent's generationConfig snapshot32 │ ├── generationConfig ← systemInstruction + tools + temp/topP33 │ └── history ← (not used — we build extraHistory instead)34 │35 ├── forkGenerationConfig ← passed as generationConfigOverride36 └── forkToolsOverride ← FunctionDeclaration[] extracted from tools37 │38 ▼39 AgentHeadless.execute(context, signal, {40 extraHistory, ← parent conversation history41 generationConfigOverride, ← parent's exact systemInstruction + tools42 toolsOverride, ← parent's exact tool declarations43 })44 │45 ▼46 AgentCore.createChat(context, {47 extraHistory,48 generationConfigOverride, ← bypasses buildChatSystemPrompt()49 }) AND skips getInitialChatHistory()50 │ (extraHistory already has env context)51 ▼52 new GeminiChat(config, generationConfig, startHistory)53 ↑ byte-identical to parent's config54```55 56### 3. History Construction (`agent.ts` + `forkSubagent.ts`)57 58The fork's `extraHistory` must end with a model message to maintain Gemini API's user/model alternation when `agent-headless` sends the `task_prompt`.59 60Three cases:61 62| Parent history ends with | extraHistory construction | task_prompt |63| ----------------------------- | ---------------------------------------------------------------------- | ------------------------------ |64| `model` (no function calls) | `[...rawHistory]` (unchanged) | `buildChildMessage(directive)` |65| `model` (with function calls) | `[...rawHistory, model(clone), user(responses+directive), model(ack)]` | `'Begin.'` |66| `user` (unusual) | `rawHistory.slice(0, -1)` (drop trailing user) | `buildChildMessage(directive)` |67 68### 4. Recursive Fork Prevention (`forkSubagent.ts`)69 70`isInForkChild()` scans conversation history for the `<fork-boilerplate>` tag. If found, the fork attempt is rejected with an error message.71 72### 5. Background Execution (`agent.ts`)73 74Fork uses `void executeSubagent()` (fire-and-forget) and returns `FORK_PLACEHOLDER_RESULT` immediately to the parent. Errors in the background task are caught, logged, and reflected in the display state.75 76## Data Flow77 78```791. Model calls Agent tool (no subagent_type)802. agent.ts: import forkSubagent.js813. agent.ts: getCacheSafeParams() → forkGenerationConfig + forkToolsOverride824. agent.ts: build extraHistory from parent's getHistory(true)835. agent.ts: build forkTaskPrompt (directive or 'Begin.')846. agent.ts: createAgentHeadless(FORK_AGENT, ...)857. agent.ts: void executeSubagent() — background868. agent.ts: return FORK_PLACEHOLDER_RESULT to parent immediately879. Background:88 a. AgentHeadless.execute(context, signal, {extraHistory, generationConfigOverride, toolsOverride})89 b. AgentCore.createChat() — uses parent's generationConfig (cache-shared)90 c. runReasoningLoop() — uses parent's tool declarations91 d. Fork executes tools, produces result92 e. updateDisplay() with final status93```94 95## Graceful Degradation96 97If `getCacheSafeParams()` returns null (first turn, no history yet), the fork falls back to:98 99- `FORK_AGENT.systemPrompt` for system instruction100- `prepareTools()` for tool declarations101 102This ensures the fork always works, even without cache sharing.103 104## Files105 106| File | Role |107| ---------------------------------------------------- | ------------------------------------------------------------------------------------- |108| `packages/core/src/agents/runtime/forkSubagent.ts` | FORK_AGENT config, buildForkedMessages(), isInForkChild(), buildChildMessage() |109| `packages/core/src/tools/agent.ts` | Fork path: CacheSafeParams retrieval, extraHistory construction, background execution |110| `packages/core/src/agents/runtime/agent-headless.ts` | execute() options: generationConfigOverride, toolsOverride |111| `packages/core/src/agents/runtime/agent-core.ts` | CreateChatOptions.generationConfigOverride |112| `packages/core/src/followup/forkedQuery.ts` | CacheSafeParams infrastructure (existing, no changes) |113 