From c9d8fbd76621877ff24a78c7359b9ce32a8217e6 Mon Sep 17 00:00:00 2001 From: Raghav Chari Date: Mon, 3 Aug 2026 15:05:19 -0400 Subject: [PATCH] =?UTF-8?q?perf(agents):=20hand=20the=20agent=20amico-run'?= =?UTF-8?q?s=20absolute=20path=20=E2=80=94=2028x=20faster=20on=20the=20tas?= =?UTF-8?q?k=20that=20needed=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AGENTS.md said bare `amico-run` and relied on PATH. When PATH missed, the agent went looking for the binary, and looking is expensive: profiling one session's history found 128 unbounded filesystem searches totalling 417 seconds, individual ones at 123s. A new {{AMICO_RUN}} substitution (alongside the existing {{JULIA_PROJECT}} and {{TEMPLATE_PATH}}) resolves to the launcher's absolute path at session prep, and the guidance forbids `which` and the find/pip fallbacks. Degrades safely: with no resolvable bin dir it substitutes the bare command, i.e. today's behaviour. MEASURED, A/B, same model (bedrock moonshotai.kimi-k2.5), same prompt, only AGENTS.md differing — 4 baseline vs 7 treatment trials on "run amico-run and show its usage": turns tool calls wall (median) `which` calls timeouts baseline 6 10 226.6s 4 2 of 4 treatment 2 1 8.0s 0 0 of 7 ~28x on median wall, 3x fewer turns, 10x fewer tool calls, and the baseline failed to finish at all in half its trials. The mechanism is visible in the transcripts: baseline ran `which amico-run`, then `ls /usr/local/bin`, `ls ~/.amico/bin`, `find /usr`, `find /opt`, `pip list`…; treatment ran the absolute path once. WHAT DID NOT WORK, and is trimmed accordingly: an earlier draft carried a long batching instruction with the profiling numbers in it. A/B on the six-independent- reads task showed NO measurable effect, because both models already batch — kimi-k2.5 emitted [6,0] in all six trials either way, and deepseek-v4-flash-free was noise (turns 3,3,2 baseline vs 4,2,3 treatment). Since it ships on every turn, it is cut to one sentence: +160 tok/turn instead of +313, keeping the intent without paying for prose that bought nothing measurable. Caveat on effect size: under the trimmed wording, one of three re-runs regressed to 5 turns (from 2). The substitution is what does the work; the prose is a backstop. `which` calls stayed at 0 across all 7 treatment trials under both wordings. Tests: 3 new — the substitution, the safe degradation to a bare command, and the two instructions. A pre-existing guard ("leaves no unknown {{...}} placeholder") caught the new placeholder immediately and now covers it. 853 passed, tsc clean. Co-Authored-By: Claude Opus 5 (1M context) --- packages/extension/AGENTS.md | 30 +++++++++---- packages/extension/src/extension.ts | 6 +++ packages/extension/src/opencode_config.ts | 14 +++++- packages/extension/test/agents_md.test.ts | 55 ++++++++++++++++++++--- 4 files changed, 88 insertions(+), 17 deletions(-) diff --git a/packages/extension/AGENTS.md b/packages/extension/AGENTS.md index aa8eff95..1d552039 100644 --- a/packages/extension/AGENTS.md +++ b/packages/extension/AGENTS.md @@ -38,14 +38,14 @@ Sound like it — not a generic assistant. ## Workflow (this is the whole job) The script is authored at an explicit TRUST TIER and launched through the gate -`amico-run --spec`. All paths below use the active Problem workspace +`{{AMICO_RUN}} --spec`. All paths below use the active Problem workspace `~/.amico/problems//` (open/create/rename with `amicode_problem`; the workspace owns `solve.jl` — never author in `/tmp`). 1. **Resolve the tier** once the System + Formulation are recorded. From the Formulation, run: ```bash - amico-run resolve --platform --kind --size + {{AMICO_RUN}} resolve --platform --kind --size ``` It prints JSON: `{tier, source?, template_path?|exemplar_path?, packages, blocked_higher?}`. 2. **Author `solve.jl` per the tier** into `~/.amico/problems//solve.jl`: @@ -66,7 +66,7 @@ workspace owns `solve.jl` — never author in `/tmp`). 4. **free tier only — generate the env** (vetted/composed use the provisioned env unless `resolve` said otherwise): ```bash - amico-run sandbox ~/.amico/problems/ --packages + {{AMICO_RUN}} sandbox ~/.amico/problems/ --packages # then run the printed JULIA_PKG_USE_CLI_GIT=true julia --project=… Pkg.instantiate() line ``` 5. **Estimate, confirm routing, then assemble `~/.amico/problems//solvespec.json`.** @@ -79,7 +79,7 @@ workspace owns `solve.jl` — never author in `/tmp`). it specifies and do **not** ask where the solve should run. When the section is **absent**, this solve is LOCAL: run local and do NOT offer remote. - **Estimate (informs, never decides).** Run - `amico-run estimate ~/.amico/problems//solve.jl` — it prints ONE JSON line + `{{AMICO_RUN}} estimate ~/.amico/problems//solve.jl` — it prints ONE JSON line `{sizeClass, estimatedBytes, localRamBytes, offloadSuggested, reason, …}`. Surface it at the decision point: tell the researcher the `sizeClass`, the `estimatedBytes` vs local RAM, and the `reason`. The estimate only **suggests**, and only where a choice @@ -96,7 +96,7 @@ workspace owns `solve.jl` — never author in `/tmp`). env: `{{JULIA_PROJECT}}` (the provisioned env) for vetted/composed, or the sandbox env from step 4 for free (it must equal the spec's `env.project`). ```bash - ( nohup amico-run --spec ~/.amico/problems//solvespec.json \ + ( nohup {{AMICO_RUN}} --spec ~/.amico/problems//solvespec.json \ --project {{JULIA_PROJECT}} --lab default \ ~/.amico/problems//solve.jl \ > ~/.amico/problems//solve.log 2>&1 < /dev/null & ) @@ -116,9 +116,9 @@ workspace owns `solve.jl` — never author in `/tmp`). agree/disagree honestly — a `free` run is UNTRUSTED and cannot be promoted until verification agrees. -There is **no MCP server**. The solve runs through `amico-run` via bash; the +There is **no MCP server**. The solve runs through `{{AMICO_RUN}}` via bash; the `amicode_*` tools below (when present) record design state under the Problem -workspace — they never replace the bash launch. `amico-run --help` prints usage. +workspace — they never replace the bash launch. `{{AMICO_RUN}} --help` prints usage. ### Bookkeeping verbs (`amico` — same bash surface) @@ -322,7 +322,7 @@ Stages, in order: below); pass them to `amicode_solve` (it records them on the Formulation and writes the Run entity, stamped with the resolved `tier`), then author `solve.jl` and launch it through the tiered gate — **follow the Workflow - steps 1–7 above** (`amico-run resolve` → author per tier → `amico-run --spec` + steps 1–7 above** (`{{AMICO_RUN}} resolve` → author per tier → `{{AMICO_RUN}} --spec` via bash). For a stock single-qubit transmon gate this resolves to the **vetted** tier and is exactly the fill-in-the-block flow. 7. **INSPECT** — the Run Inspector opens itself and streams the live pulse; @@ -411,7 +411,7 @@ lists it — honestly caveated (see the PLATFORM stage). ## The run-dir contract your script MUST emit -`amico-run` writes `run.toml` (first) and `FINISHED` (last) itself. Your +`{{AMICO_RUN}}` writes `run.toml` (first) and `FINISHED` (last) itself. Your script, running with cwd = the run dir, must emit: - `AMICODE_ITER iter= f= inf_pr=<…> inf_du=<…>` to stdout, flushed, @@ -493,6 +493,18 @@ correct loader in this Piccolo. **{{JULIA_PROJECT}}**. Always pass it. +## Tool use + +- **The tools you need are at absolute paths already. Never hunt for them.** Use + `{{AMICO_RUN}}` and `{{JULIA_PROJECT}}` verbatim. Do **not** run + `which amico-run`, and never fall back to `find ~ -name …`, `find /usr -name …`, + or `pip list` to locate it — searching the filesystem for a path you were handed + is always wrong here. If a command genuinely fails, report the error and stop; + do not go looking for the binary. + +- Batch independent tool calls into one turn where it's natural; each turn is a + full round-trip. Serialize only when a later call needs an earlier result. + ## Style & formatting The user is a quantum-control researcher — skip the basics, keep the physics diff --git a/packages/extension/src/extension.ts b/packages/extension/src/extension.ts index fbc3920a..36d9238f 100644 --- a/packages/extension/src/extension.ts +++ b/packages/extension/src/extension.ts @@ -423,7 +423,11 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { const parsed = parseLibraryRootSpecs(raw); return parsed.length ? parsed : undefined; }; + // Absolute launcher path for AGENTS.md's {{AMICO_RUN}} — so the agent never + // needs `which`, and never falls back to scanning $HOME (417s measured waste). + const amicoRunPath = amicoRunBinDir ? path.join(amicoRunBinDir, "amico-run") : undefined; const opencodeProject = prepareOpencodeProject({ + amicoRunPath, agentsSrc: path.resolve(ctx.extensionPath, "AGENTS.md"), // MODE-SELECTED vetted template: HP sessions get the Piccolissimo variant // (same run-dir contract, spline solver layer). An AGENTS.md instruction @@ -573,6 +577,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { opencodeChannel.appendLine(`[solver] switching → ${mode}`); applyEntitlementForMode(mode, path.join(os.homedir(), ".amico", "amicode")); const project2 = prepareOpencodeProject({ + amicoRunPath, agentsSrc: path.resolve(ctx.extensionPath, "AGENTS.md"), // Same mode-selection as boot; `mode` is the requested target of THIS // switch (the state file still reads status:"switching" here). @@ -707,6 +712,7 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { } const port = vscode.workspace.getConfiguration("amicode").get("opencodePort", 0); const project2 = prepareOpencodeProject({ + amicoRunPath, agentsSrc: path.resolve(ctx.extensionPath, "AGENTS.md"), templateSrc: path.resolve( ctx.extensionPath, diff --git a/packages/extension/src/opencode_config.ts b/packages/extension/src/opencode_config.ts index b543d3f3..6d3d561d 100644 --- a/packages/extension/src/opencode_config.ts +++ b/packages/extension/src/opencode_config.ts @@ -464,6 +464,14 @@ export interface OpencodeConfigOptions { /** Julia project (--project) the agent should use; already resolved (see * resolveJuliaProject). Substituted into AGENTS.md as {{JULIA_PROJECT}}. */ juliaProject: string | undefined; + /** Absolute path to the `amico-run` launcher, substituted into AGENTS.md as + * {{AMICO_RUN}}. PATH augmentation already happens at spawn time, but when it + * fails the agent fell back to `which amico-run || find ~ -name amico-run`, + * and an unbounded scan of $HOME costs ~2 minutes EACH: 128 such calls totalling + * 417s were measured in one session's history (2026-08-03). Handing over the + * absolute path removes the reason to search at all. Undefined leaves the + * placeholder resolving to the bare command (previous behaviour). */ + amicoRunPath?: string; /** Scores repertoire root (SCORE.md manifests). Default: the bundled scores/. */ scoresRoot?: string; /** Dir holding the user's entitlements.toml (access-code stub). Default: ~/.amico/amicode. */ @@ -524,7 +532,11 @@ export function prepareOpencodeProject(opts: OpencodeConfigOptions): OpencodePro : "# Amicode\nRead the template at {{TEMPLATE_PATH}}, fill params, run `amico-run