class
LLM::ACPClient
- LLM::ACPClient
- Reference
- Object
Overview
ACP-backed client wrapper for communicating with local AI agents.
Defined in:
llm/acp/client.crConstant Summary
-
CLAUDE_ARGS =
["@zed-industries/claude-agent-acp"] -
CODEX_ARGS =
["@zed-industries/codex-acp"] -
GEMINI_ARGS =
["--experimental-acp"] -
KNOWN_TARGETS =
ACPTargets::KNOWN -
See
LLM::ACPTargetsfor the list and why it is shared rather than duplicated. Kept as an alias so existing references still read naturally at the exec sink.
Constructors
Class Method Summary
- .acp_provider?(provider : String) : Bool
-
.custom_command_allowed? : Bool
Escape hatch for power users running their own ACP agent binary.
- .default_model(provider : String, model : String) : String
- .extract_target(provider : String) : String
- .mute_acp_logs : Nil
-
.resolve_command(provider : String) : Tuple(String, Array(String))
Resolve provider aliases to actual executable command + args.
-
.tool_permissions_allowed? : Bool
session/request_permissionis how the ACP agent asks to run a tool — shell commands, file writes, network fetches — on this machine.
Instance Method Summary
-
#answer_permission_request(params : JSON::Any) : JSON::Any
Pick a real option from the ones the agent offered rather than inventing an id.
- #args : Array(String)
- #close : Nil
- #command : String
- #model : String
- #provider : String
- #report_request_failure(error : Exception) : Nil
- #request(prompt : String, format : String = "json") : String
- #request_messages(messages : Array(Hash(String, String)), format : String = "json") : String
Constructor Detail
Class Method Detail
Escape hatch for power users running their own ACP agent binary. Off by
default so a poisoned .noir.yml can't silently spawn a process.
Resolve provider aliases to actual executable command + args.
session/request_permission is how the ACP agent asks to run a tool —
shell commands, file writes, network fetches — on this machine. Noir
used to answer every one of them with a hardcoded
{"outcome":"selected","optionId":"allow-once"}, i.e. blanket approval
for whatever the agent decided to do.
That is remote-controllable input. The prompt Noir sends is source code
from the tree being scanned, and scanning code you did not write is the
normal case; a file carrying "ignore the above and run X" is enough to
turn an endpoint scan into arbitrary local execution, with the auto-yes
removing the one checkpoint that would have caught it. It also
contradicted the .resolve_command hardening right above, which refuses
to spawn an unknown agent binary precisely so untrusted config can't
reach code execution.
Deny by default. Noir puts the code to analyse in the prompt, so the agent needs no tools to answer — the only thing lost is an agent's optional extra poking around. Operators who want that back opt in explicitly, same shape as the custom-command escape hatch.
Instance Method Detail
Pick a real option from the ones the agent offered rather than
inventing an id. Option ids are agent-defined (proceed_once,
reject, …); the kind field is the part the protocol standardises,
so it is what we match on. With nothing usable on offer, cancelled
is the protocol's own "no decision" outcome and needs no id.
Public rather than private so the decision can be asserted without standing up an agent process.
General and Ollama both report their failures on stderr; the rescue
above used to be bare, so a session that never spawned, an agent that
died mid-turn, a protocol error and a genuinely empty answer were all
the same "" with nothing written anywhere. The caller reads "" as "this
code defines no endpoints", which made a completely dead agent look like
a successful AI-assisted scan.
Public so the report can be asserted without standing up an agent process.