# Open optimization ideas Extension memory/CPU and subagent token input. The measured baseline and several proposals from the original note have landed; what remains is below. ## Landed - **Per-agent child preparation is cached** (`9f6ea76`): `discoverAgents()` keeps a bounded mtime-keyed parse cache instead of re-reading every agent card on each tool call. - **Children get trimmed context** (`c8b5dd8`): the whole user `AGENTS.md` is no longer shipped to every worker; prompt tokens per request fell from an average 28,811 to 23,769. - **Gemini model discovery is lazy** (`8f8a8d8`): the model table is resolved on first use instead of at extension load, removing startup CPU and a spawned subprocess. - **Gemini progress state is bounded**: streamed text is capped (`MAX_PROGRESS_TEXT`) and activity history is evicted past `MAX_ACTIVITY_LINES`. ## 1. Bound the Gemini step-tracker maps (extension memory) `createAgyStepTracker()` (`extensions/shared/gemini/step-tracker.ts:187`) keeps two maps that are re-inserted on every update rather than trimmed: - `contexts` (keyed by step key) grows with the number of distinct steps in a run; - `stepIndexKeys` (keyed by `step_index`) grows with every index seen. Long reviews with many tool steps therefore hold every step's context for the life of the run. The progress history is capped but these maps are not. Bound them the way the activity history is bounded — keep the most recent N step contexts and drop index keys whose context has been evicted — or delete a context on completion rather than re-inserting it with `state: "done"`. **Impact.** Low risk, bounded memory per run. **Effort.** ~1 hour plus a state-eviction test. ## 2. Cap per-arm transcript retention in parallel fan-out (extension memory) `extensions/subagent/index.ts` keeps the full `messages: Message[]` transcript plus accumulated `streamedText` for every arm of a parallel batch in `allResults[]` until the group finishes. The per-task output is capped at `PER_TASK_OUTPUT_CAP` (50 KiB) but the transcript that produces it is not, so peak memory scales with arms × transcript length. With a high concurrency limit and long tool-call transcripts this is the largest avoidable allocation in the extension. Options, cheapest first: keep only the last N messages per arm once a result is final; or accumulate the rendered output incrementally and drop the transcript when the arm completes. Both need care: `getResultOutput()` and the guardrail checks read the transcript while it is still live. **Impact.** Meaningful peak-memory reduction on wide fan-out; no behaviour change expected. **Effort.** ~2 hours plus tests for the guardrail paths that read messages.