fix(acp): launch ACP/stdio mode for managed agents with no runtime preset - #3804
fix(acp): launch ACP/stdio mode for managed agents with no runtime preset#3804bradhallett wants to merge 3 commits into
Conversation
def8c83 to
e0853e8
Compare
|
good for #3729. maybe add a log line of the resolved launch args at spawn so failed initialize is easier to debug |
|
@Chessing234 Implemented in b9825af and pushed to the PR branch. |
|
Maintainer approval requested: PR #3905 overlaps with #3804 on command-only preset argument resolution. I recommend keeping #3804 as the canonical fix:
#3905 additionally explores loaded-registry matching and runtime-ID/command mismatch diagnostics. I am happy to port the mismatch diagnostic if command-only custom-harness support is required, but I recommend not merging both implementations. Can we proceed with #3804 as the canonical PR and close #3905 in favor of it? |
b9825af to
8b2db59
Compare
|
We are running a headless Grok ACP worker under systemd and hit the same failure described in #3457: launching the bare Grok command enters its interactive path without a controlling TTY, while supplying the runtime ACP/stdio arguments works reliably. The preset-argument fallback in this PR addresses the problem at the right layer and the visible checks are green. +1 for merging this as the canonical fix and including it in the next Buzz release. |
8b2db59 to
ba3b969
Compare
|
Rebased onto current Recap for review:
This is the canonical fix; #3905 overlaps and I'd close it in favor of this one. Could we get a review approval so it can land? |
|
Looks like this got hit by the gh actions outage, CI needs a kick here (pushing empty commit was easiest for me, no CI was published to retry) fyi @bradhallett Oh and another +1 from me, I'm building out an ACP adapter layer for https://github.com/scottopell/phoenix-ide and ran into this |
ba3b969 to
9a90678
Compare
9a90678 to
26b617c
Compare
|
Rebased onto current @scottopell re: the CI kick — pushing didn't publish a run to retry; both workflows show The rebase also dismissed the approvals above — no code changes since your reviews beyond the conflict resolution and the fixture field main added ( |
wesbillman
left a comment
There was a problem hiding this comment.
Requesting changes for two merge blockers; the preset-argument fallback itself is correctly placed and its precedence is sound.
-
Security: do not write raw arbitrary
agent_argsto the runtime log (desktop/src-tauri/src/managed_agents/runtime.rs:497-503).agent_argsis user-controlled and may legally contain credentials such as--token=.... This repository's spawn-snapshot redaction policy explicitly treatsargsasMaskedBarefor exactly that reason (spawn_snapshot/diff.rs:82-86), with a secrecy test using--token=.... The new{:?}log line bypasses that protection and makes the secret retrievable throughget_managed_agent_log. Log a redacted representation (or only argument count / known preset mode), with a regression test proving a token-bearing arg is absent. -
Required CI/file-size guard is failing. The PR grows already-oversized
readiness.rsfrom 1741 to 1850 lines andruntime.rsfrom 997 to 1004;Detect Changed Pathsfails and consequently skips Desktop Core, Rust lint, builds, and E2E. Repository guidance says oversized files may not grow and files crossing 1000 lines must be split rather than bypassing the limit. Move the large fixture/test into an existing or new test submodule and place the logging helper/test where it does not pushruntime.rsover the ceiling.
Source verdict on the functional fix: matching the normalized effective command against PRESET_HARNESSES, after explicit instance args and resolved harness-definition args, preserves precedence and restores ACP/stdio args from the existing source of truth. Unknown commands and empty-arg adapters remain unchanged. I would approve after the disclosure and mandatory CI failures are fixed.
Review scope: source/diff + CI only, per this channel's strict review contract; no local runtime execution.
wesbillman
left a comment
There was a problem hiding this comment.
Carl, an automated reviewer, commenting via Wes’s GitHub account.
The core preset-argument fallback is correct, but this head has two blockers:
-
spawn_agent_childnow persists the completeagent_argsvector with{:?}(desktop/src-tauri/src/managed_agents/runtime.rs:497-503). These arguments are arbitrary user-controlled CLI input and may legally contain--token=...; the repository's snapshot redaction policy explicitly masks allargsbecause of that (desktop/src-tauri/src/managed_agents/spawn_snapshot/diff.rs:72-86). Runtime logs are also created by genericopen_log_filewithout owner-only creation mode (storage.rs:654-660), unlike credential-bearing install logs (storage.rs:693-716). Please do not serialize raw argv. Logging the resolved command plus argument count/source is sufficient; hardening runtime-log creation to0600is worthwhile defense in depth but does not make persisting secrets appropriate. -
Required CI fails the file-size ratchet:
readiness.rsgrows from 1741 to 1850 lines andruntime.rsfrom 997 to 1004. That causes the substantive Desktop/Rust/build/E2E jobs to be skipped and their aggregate checks to fail. Please move the large regression fixture/test into an existing sibling test module (or otherwise split it) and keep production files within the ratchet.
The functional resolution order itself looks sound: explicit instance args still win, then a resolved harness definition, then normalized command matching against the single-source PRESET_HARNESSES; unknown commands remain unchanged. Once the raw-argv logging and file-size failures are resolved and required CI runs, I expect this to be mergeable.
Managed agents pinned to a known ACP runtime via agent_command_override (e.g. `omp`, `grok`, `opencode`, `kimi`) but with no `runtime` id resolved the effective command (e.g. `omp`) yet launched it with empty args. With no controlling TTY under headless Buzz, the agent's interactive TUI started instead of its ACP/stdio mode, so the JSON-RPC `initialize` handshake never completed and the agent timed out at startup (omp/opencode/kimi: Request timeout; grok: ENXIO "Device not configured"). Root cause: resolve_effective_harness_descriptor only filled args when the record carried explicit instance args or a resolvable harness preset (record.runtime -> lookup_loaded_harness_by_id). A command-override agent with runtime=None hit neither, so descriptor.args stayed empty. Fix: when no preset resolves, fall back to the PRESET_HARNESSES entry whose command matches the effective command (new preset_args_for_command, mirroring the existing single-source preset_harness_ids helper and living alongside it in the managed_agents::discovery::presets submodule). The PresetHarness table stays the single source of truth for per-runtime launch args, so this covers omp, grok, opencode, kimi, cursor, openclaw, and devin at once with no hand-mirroring, and descriptor.args now flows correctly to the spawn env (BUZZ_ACP_AGENT_ARGS), the config hash, and the agent summary. No regressions: explicit instance args still win; goose still resolves via default_agent_args; amp/hermes presets (empty args) are unchanged; custom harness ids collide with reserved preset ids so cannot shadow them. Resolves block#3399 Resolves block#3457 Resolves block#3729 Signed-off-by: Brad Hallett <53977268+bradhallett@users.noreply.github.com>
Signed-off-by: Brad Hallett <53977268+bradhallett@users.noreply.github.com>
…chet Review item block#1 (security): spawn_agent_child logged the raw agent_args vector with `{:?}` into the runtime log retrievable end-to-end via get_managed_agent_log. Args may legally carry credentials (--token=...), so no argument value may ever be serialized. The resolved-launch marker now records only the resolved command and the argument count, mirroring spawn_snapshot::diff's MaskedBare policy. The marker writer lives in storage.rs as append_resolved_launch_marker(path, command, args): it resolves the command to a full path (DMG launches have a minimal PATH), appends the redacted marker, and returns the resolved command for the BUZZ_ACP_AGENT_COMMAND spawn env, replacing runtime.rs's inline resolution. open_log_file now creates runtime logs with mode 0o600 at creation time (Unix), so the permissions hold regardless of umask; agent stdout/stderr can echo installer/CLI credentials. Regression tests: append_resolved_launch_marker_never_writes_argument_ values asserts the marker carries the command and args_count only -- never token/argument values -- and that unresolvable commands fall through verbatim; open_log_file_creates_owner_only asserts 0o600. File-size ratchet: the two effective-launch tests moved from readiness.rs into readiness_effective_launch_tests.rs (external #[path] module, following readiness_goose_file_config_tests.rs), and runtime.rs stays at/below the ratchet cap after folding command resolution into the storage helper. Unblocks Detect Changed Paths so the required CI jobs run. Signed-off-by: Brad Hallett <53977268+bradhallett@users.noreply.github.com>
9da6cbe to
d518e76
Compare
|
Addressed both blockers from #3804 (review) in d518e76 (rebased onto current main): 1. Raw 2. File-size ratchet. The two effective-launch tests moved from Local verification: |
|
Note: the force-push (rebase onto current main) reset the workflow approval gate — both |
Summary
Managed agents pinned to a known ACP runtime via
agent_command_override(omp,grok,opencode,kimi, …) but with noruntimeid launched the agent's interactive TUI instead of its ACP/stdio mode. Under headless Buzz there is no controlling TTY, so the JSON-RPCinitializehandshake never completed and the agent timed out at startup:Request timeoutENXIO: Device not configuredRoot cause
resolve_effective_harness_descriptor(readiness.rs) only resolved launch args when the record carried explicit instance args or a resolvable harness preset (record.runtime→lookup_loaded_harness_by_id). A command-override agent withruntime = Nonesatisfied neither, sodescriptor.argsstayed empty — while the effective command (e.g.omp) was still correct. The bare command then ran the TUI.The correct per-runtime args already existed as the single source of truth in
PRESET_HARNESSES(now indiscovery/presets.rs); they just weren't consulted on this path.Fix
In
resolve_effective_harness_descriptor's finalelsebranch, fall back to thePRESET_HARNESSESentry whose command matches the effective command, via a newpreset_args_for_command()helper that mirrors the existing single-sourcepreset_harness_ids()helper and lives alongside it in themanaged_agents::discovery::presetssubmodule (re-exported throughdiscovery.rs).PRESET_HARNESSES; no hand-mirrored copy.descriptor.argsnow flows correctly to the spawn env (BUZZ_ACP_AGENT_ARGS), the config hash, and the agent summary.} else if let Some(ref def) = harness_def { normalize_agent_args(&effective_command, def.args.clone()) + } else if let Some(preset_args) = + crate::managed_agents::discovery::preset_args_for_command(&effective_command) + { + normalize_agent_args(&effective_command, preset_args) } else { normalize_agent_args(&effective_command, record_args) }Why not in
buzz-acpAn earlier iteration mirrored the args into
buzz-acp'sdefault_agent_args. That duplicated the preset table in a third place and still left the desktop'sdescriptor.args(env/hash/summary) empty — relying on the child to repair the parent's omission. Fixing atresolve_effective_harness_descriptorcorrects the source and needs nobuzz-acpchange;buzz-acp's existingdefault_value = "acp"still covers standalone CLI use with an unset env.Verification
resolve_effective_harness_descriptor_uses_preset_args_for_pinned_command— asserts omp/grok/opencode/kimi/cursor/openclaw/devin resolve correctly, explicit args win, unknown commands stay empty. ✅cargo fmt --checkclean (pinned1.95.0toolchain); fullmanaged_agents::lib suite: 1001 passed, 0 failed (rebased onto v0.5.9main).block/buzzmain(resolves the conflict with Add Devin as a preset ACP harness #3225, which relocatedPRESET_HARNESSESand its helpers intodiscovery/presets.rs; this helper follows it there).ompmanaged agent initializes without the ACP timeout.No regressions
goose(not inPRESET_HARNESSES) still resolves viadefault_agent_args.amp-acp/hermes-acppresets carry empty args — unchanged.Resolves #3457
Refs #3399
Issue scope (corrected — an earlier draft also listed
Resolves #3399/Resolves #3729):grok(TUI) instead ofgrok agent --always-approve stdio— agents fail with ENXIO #3457 — command-override agents (e.g. Grok) launching the bare TUI withempty args. Directly fixed here.
omp. This fixes the launch-argssymptom (an
ompoverride now resolves["acp"]); the broaderconfigurable-registry / UI-picker ask is a separate feature, so it is
Refsrather than
Resolves.session/set_config_option. Already fixedon
mainand shipped in v0.5.9 via theagent_supports_mode()guard inbuzz-acp(PR feat(acp): idle-based timeout with dual-deadline architecture #188), which skips the call for agents that don't advertise themode. This PR does not touch
buzz-acp; the earlierResolves #3729wasinaccurate and is removed.