Summary
The ACP provider defaults currently launch the three ACP backends with unversioned npx -y <package> commands. That means the package version is resolved from npm at agent-launch time, not reviewed at commit time, lock time, or image build time. For ACP agents that run with high-permission modes, this creates an avoidable supply-chain risk: a compromised, poisoned, or unexpectedly broken latest publish would be fetched and executed on the next launch.
This is hardening, not a report of an active exploit. The goal is to bring the npm ACP launchers under the same version discipline the repo already applies elsewhere (the uv exclude-newer = "7 days" guardrail in [tool.uv] covers the Python lock, but not these runtime npx launches).
Where
openhands-sdk/openhands/sdk/settings/acp_providers.py, the launch defaults:
- L100
("npx", "-y", "@agentclientprotocol/claude-agent-acp"), default_session_mode="bypassPermissions"
- L111
("npx", "-y", "@zed-industries/codex-acp"), default_session_mode="full-access"
- L122
("npx", "-y", "@google/gemini-cli", "--acp"), default_session_mode="yolo"
Each provider pairs floating runtime resolution with a permission-disabling session mode, so this is not a low-stakes dev convenience path.
The agent-server Dockerfile already pins these for its bundled install (openhands-agent-server/openhands/agent_server/docker/Dockerfile L174-176: @...@0.30.0, @...@0.11.1, @...@0.38.0), so the build-time version and the version actually launched at runtime can diverge.
Separately, the OpenAPI example at openhands-agent-server/openhands/agent_server/conversation_router_acp.py:53 uses npx -y claude-agent-acp with the unscoped name. That name is unregistered on npm (404 today), so the example is both broken and a dependency-confusion footgun: the name is registerable by anyone, after which npx -y would fetch and run it. It should use the scoped, versioned package like the providers.
Proposed change
Pin each launch command to a reviewed version, aligned with the bundled Dockerfile versions so build and runtime agree (and bring gemini-cli to a patched >= 0.39.1):
("npx", "-y", "@google/gemini-cli@<pinned>", "--acp")
Fix the router example to the scoped, versioned name.
Tradeoff
Version strings in source are not Dependabot-tracked, so they need manual bumps (reasonable for three launchers, and consistent with the Dockerfile already pinning them by hand). The heavier alternative is launching the CLIs from a committed lockfile install rather than npx, which pins the full transitive tree; the version pin is the smaller step that closes the floating-latest gap.
Summary
The ACP provider defaults currently launch the three ACP backends with unversioned
npx -y <package>commands. That means the package version is resolved from npm at agent-launch time, not reviewed at commit time, lock time, or image build time. For ACP agents that run with high-permission modes, this creates an avoidable supply-chain risk: a compromised, poisoned, or unexpectedly brokenlatestpublish would be fetched and executed on the next launch.This is hardening, not a report of an active exploit. The goal is to bring the npm ACP launchers under the same version discipline the repo already applies elsewhere (the uv
exclude-newer = "7 days"guardrail in[tool.uv]covers the Python lock, but not these runtimenpxlaunches).Where
openhands-sdk/openhands/sdk/settings/acp_providers.py, the launch defaults:("npx", "-y", "@agentclientprotocol/claude-agent-acp"),default_session_mode="bypassPermissions"("npx", "-y", "@zed-industries/codex-acp"),default_session_mode="full-access"("npx", "-y", "@google/gemini-cli", "--acp"),default_session_mode="yolo"Each provider pairs floating runtime resolution with a permission-disabling session mode, so this is not a low-stakes dev convenience path.
The agent-server Dockerfile already pins these for its bundled install (
openhands-agent-server/openhands/agent_server/docker/DockerfileL174-176:@...@0.30.0,@...@0.11.1,@...@0.38.0), so the build-time version and the version actually launched at runtime can diverge.Separately, the OpenAPI example at
openhands-agent-server/openhands/agent_server/conversation_router_acp.py:53usesnpx -y claude-agent-acpwith the unscoped name. That name is unregistered on npm (404 today), so the example is both broken and a dependency-confusion footgun: the name is registerable by anyone, after whichnpx -ywould fetch and run it. It should use the scoped, versioned package like the providers.Proposed change
Pin each launch command to a reviewed version, aligned with the bundled Dockerfile versions so build and runtime agree (and bring gemini-cli to a patched
>= 0.39.1):Fix the router example to the scoped, versioned name.
Tradeoff
Version strings in source are not Dependabot-tracked, so they need manual bumps (reasonable for three launchers, and consistent with the Dockerfile already pinning them by hand). The heavier alternative is launching the CLIs from a committed lockfile install rather than
npx, which pins the full transitive tree; the version pin is the smaller step that closes the floating-latestgap.