Skip to content

Generic-ACP agents get no credentials and no way to declare any (root cause behind #4281) #4390

Description

@dhruv0811

What happens

A generic-ACP agent configured the documented way — an entry under acp.agents:
in ~/.omnigent/config.yaml, or via omnigent setupCustom ACP agent — is
spawned with no provider credentials at all, and there is no way to give it
any. The agent starts unauthenticated, stalls during the ACP handshake, and the
turn fails.

This is the underlying failure behind #4281 (whose visible symptom, the blank
inner executor error: , is the separate reporting bug PR #4362 addresses).
It explains every clue in that report: the failure is not agent-specific, it is
unaffected by omnigent_mcp: false, and the same agent is healthy when driven
directly over stdio — where it inherits the user's real shell environment.

Root cause

AcpExecutor._build_spawn_env filters deny-by-default with an empty prefix
family (omnigent/inner/acp_executor.py):

return clean_agent_env(
    allow_prefixes=(),
    extra_allowed=declared_passthrough(self._os_env),
)

allow_prefixes=() is a deliberate choice from #3479 — the executor drives an
arbitrary agent, so it cannot know which vendor family that agent
authenticates with, and guessing would re-widen the leak that issue closed. The
commit message says so explicitly:

acp none - base + env_passthrough only, the agent is arbitrary

The gap is the other half of that sentence. env_passthrough is the intended
escape hatch, and for generic ACP it is unreachable from the agent's own
config
:

  • AcpAgentConfig has no env field.
  • AcpAgentEntry (the parsed acp.agents: row) has no env field, so
    acp_agents() cannot read one and acp_agents_settings() cannot persist one.
  • _build_acp_spawn_env therefore has nothing to forward.
  • acp_harness._resolve_os_env() falls back to
    OSEnvSandboxSpec(type="none"), i.e. env_passthrough=None.

os_env.sandbox.env_passthrough only exists on a full agent spec. A user
configuring an ACP agent through acp.agents: never authors one, so in practice
the hatch does not exist for the documented configuration path.

Contrast the sibling executors, which each get their family plus a
credential resolver:

executor allow_prefixes provider env
goose_executor ("GOOSE_",) _provider_env()
qwen_executor ("QWEN_", "OPENAI_", "DASHSCOPE_") _resolve_gateway_env()
acp_executor () none

Reproduction

Measured against a realistic environment — only HOME, PATH, TERM survive:

from omnigent.inner.agent_env import clean_agent_env
fake = {"HOME": "/h", "PATH": "/usr/bin", "TERM": "xterm",
        "XAI_API_KEY": "...", "OPENAI_API_KEY": "...", "ANTHROPIC_API_KEY": "...",
        "GOOSE_PROVIDER": "anthropic", "GOOSE_MODEL": "sonnet"}
clean_agent_env(allow_prefixes=(), extra_allowed=(), source=fake)
# -> {'HOME': ..., 'PATH': ..., 'TERM': ...}
# stripped: ANTHROPIC_API_KEY, GOOSE_MODEL, GOOSE_PROVIDER, OPENAI_API_KEY, XAI_API_KEY

End to end against a fake ACP agent that answers initialize offline but
contacts its provider on session/new (so it stalls without a key), with the
user's key exported in their shell — one variable changed:

default (no env_passthrough)       FAILED -> 'inner executor error: '   (blank)
env_passthrough=['XAI_API_KEY']    SUCCEEDED (turn completed)

Impact

Every generic-ACP agent configured through acp.agents: / omnigent setup is
unusable if it authenticates from an environment variable, with no in-product
workaround. Also blocks #3460 (the ACP model picker): the advertised model list
never arrives because the turn dies at session/new.

There is currently no test covering the generic-ACP spawn env, which is how
this regressed silently.

Proposed fix

Keep deny-by-default; make the escape hatch reachable per agent. Add an
explicit, user-declared allowlist to the ACP agent config, threaded through the
existing plumbing:

acp:
  agents:
    - name: Grok Build
      command: grok agent stdio
      env_passthrough: [XAI_API_KEY]

Names only, never values — the variable is read from the host environment at
spawn, so no secret is written into config.yaml. AcpAgentEntry
_build_acp_spawn_env → a new HARNESS_ACP_ENV_PASSTHROUGHAcpAgentConfig
_build_spawn_env's extra_allowed, unioned with any spec-declared
os_env.sandbox.env_passthrough.

Rejected alternative: widening BASE_ALLOW_* or adding a guessed prefix family
for ACP. That hands every configured ACP agent every other provider's key and
re-opens #3445 for the one executor that cannot know what it is running.

Environment

  • Omnigent main @ 9dab48b46

Metadata

Metadata

Assignees

Labels

BugSomething isn't workingP1-highPriority: major feature broken, no workaroundcomp:harness-t2Mainline harnessestriagedIssue has been triaged by the botvalidated:reproducedRepro agent validated the issue can be reproduced

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions