Skip to content

fix(agents): resolve preset harness args at spawn - #4765

Open
lnv-louis wants to merge 1 commit into
block:mainfrom
lnv-louis:fix/preset-harness-args-dropped-at-spawn
Open

fix(agents): resolve preset harness args at spawn#4765
lnv-louis wants to merge 1 commit into
block:mainfrom
lnv-louis:fix/preset-harness-args-dropped-at-spawn

Conversation

@lnv-louis

Copy link
Copy Markdown

Fixes #4764.

Problem

default_agent_args matched only KNOWN_ACP_RUNTIMES, so every entry in PRESET_HARNESSES fell through to None and the args a preset declares were dropped. Presets have carried their own invocations since #3225devin acp, cursor-agent acp, grok agent --always-approve stdio — but nothing on the spawn path read them.

An agent pinned to a preset by command rather than by runtime id hits this. With record.agent_args empty and record.runtime unset, the id-keyed registry lookup in resolve_effective_harness_descriptor misses and resolution falls to normalize_agent_args, which returns []. runtime.rs:573 then sets BUZZ_ACP_AGENT_ARGS="", which overrides buzz-acp's own [default: acp], and the harness spawns bare:

INFO buzz_acp: buzz-acp starting: ... agent_cmd=/Users/me/.local/bin/devin  mcp_cmd= ... agents=10
ERROR buzz_acp: agent initialize failed: Agent process exited unexpectedly agent=0
...
Error: all 10 agents failed to start — cannot continue

devin with no args opens the interactive TUI on a stdio pipe and exits (Error: Scrollback error: io error). devin acp returns a valid initialize result.

Affects devin, cursor, omp, opencode, kimi, openclaw and grok. Adapter presets (amp, hermes) declare no args and are unaffected in practice.

Relationship to #4631

Same shape, one field over. #4631 fixed preset invisibility for the harness command and added canonical_harness_command with builtins → static presets → loaded registry. The args kept the builtin-only blind spot.

Fix

Add preset_args_for_command in discovery/presets.rs and consult it as tier 2 of default_agent_args, so PRESET_HARNESSES is the single source of truth for its own invocations instead of requiring a parallel entry in a hardcoded match.

Deliberately static-only — no loaded-registry tier. This runs inside preset_catalog_entry while the catalog is being built, so acquiring the registry lock there risks deadlocking against a concurrent warm/refresh. Registry-backed harnesses already resolve their args by runtime id in resolve_effective_harness_descriptor, so the tier would be redundant as well as risky.

Adding "devin" => Some(vec!["acp".to_string()]) to the match would have fixed the reported symptom and left cursor, omp, opencode, kimi, openclaw and grok broken, plus every preset added later. Hence the lookup.

Why existing tests passed

devin_preset_uses_official_native_acp_invocation asserts entry.default_args == vec!["acp"] and passed throughout. preset_catalog_entry passes def.args in explicitly, so the non-empty branch returns them unchanged — the catalog never exercised the empty-args path that spawn takes. The new tests cover that path directly.

Tests

Five new tests in discovery/tests.rs:

  • preset_commands_keep_their_declared_args — devin, cursor-agent, omp, openclaw resolve to ["acp"]; grok keeps all three of its args
  • preset_args_resolve_through_path_prefixes_and_runtime_ids/Users/me/.local/bin/devin and the bare id cursor both resolve
  • explicit_preset_args_win_over_declared_defaults — user args are not clobbered
  • adapter_presets_stay_argless — amp-acp and hermes-acp stay empty; a stray acp is dropped as it is for claude-code-acp
  • unknown_commands_still_resolve_to_no_args — custom harnesses unchanged

Verification

just desktop-tauri-test    2204 passed / 0 failed / 14 ignored
just desktop-tauri-clippy  clean under -D warnings
just desktop-tauri-fmt     clean

Regression check — reverting the one-line change in default_agent_args to _ => None fails exactly the new tests:

preset_args_resolve_through_path_prefixes_and_runtime_ids ... FAILED
adapter_presets_stay_argless ... FAILED
preset_commands_keep_their_declared_args ... FAILED

  left: []
 right: ["acp"]

Restoring it returns the suite to green.

Note for maintainers

Records already written with "agent_args": [] are fixed by this change, since resolution is recomputed at spawn rather than read from the frozen snapshot. No migration needed.

`default_agent_args` matched only `KNOWN_ACP_RUNTIMES`, so every entry in
`PRESET_HARNESSES` fell through to `None` and the args a preset declares
were dropped. Presets have carried their own invocations since block#3225
(`devin acp`, `cursor-agent acp`, `grok agent --always-approve stdio`),
but nothing on the spawn path read them.

An agent pinned to a preset by command rather than by runtime id hits
this: with `record.agent_args` empty and `record.runtime` unset, the
id-keyed registry lookup in `resolve_effective_harness_descriptor` misses
and resolution falls to `normalize_agent_args`, which returns `[]`.
`runtime.rs` then sets `BUZZ_ACP_AGENT_ARGS=""`, overriding buzz-acp's
`[default: acp]`, and the harness spawns bare. `devin` with no args opens
the interactive TUI on a stdio pipe and exits:

    ERROR buzz_acp: agent initialize failed: Agent process exited
    unexpectedly agent=0
    Error: all 10 agents failed to start - cannot continue

Same shape as block#4631, one field over: that fixed preset invisibility for
the harness *command*, this fixes it for the *args*.

Add `preset_args_for_command` and consult it as tier 2, mirroring
`canonical_harness_command`. Static-only by design - no loaded-registry
tier, because this runs inside `preset_catalog_entry` while the catalog
is being built and taking the registry lock there risks deadlocking
against a concurrent warm. Registry-backed harnesses already resolve
args by runtime id.

`devin_preset_uses_official_native_acp_invocation` passed throughout:
`preset_catalog_entry` passes `def.args` in explicitly, so the catalog
never exercised the empty-args path that spawn takes. The new tests
cover that path.

Verified: 2204 passed / 0 failed (`just desktop-tauri-test`), clippy
clean under `-D warnings`. Reverting the one-line change fails
`preset_commands_keep_their_declared_args`,
`preset_args_resolve_through_path_prefixes_and_runtime_ids` and
`adapter_presets_stay_argless` with `left: [] right: ["acp"]`.

Signed-off-by: Louis <louisle236@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Preset harnesses spawn without their declared args (devin, cursor, omp, grok, openclaw)

1 participant