sendMessage, readFile, askPermission,
execute, and the rest); the raw, full request/response form of each call below lives one step down,
on ctx.client().
File access
NegotiatedCapabilities.supportsReadTextFile()/supportsWriteTextFile() before
calling; calling without the capability throws AcpCapabilityException locally, before anything is
sent.
Terminal execution
A four-step lifecycle, because the client (not the agent) controls what actually runs and may want to stream output as it happens. Each step is a call onctx.client():
client().killTerminal(...) ends a still-running terminal early, outside the normal
wait-then-release flow.
All five terminal methods check NegotiatedCapabilities.supportsTerminal() before calling, not only
createTerminal: calling any of them without the capability throws AcpCapabilityException locally,
before anything is sent. Before 0.80.0, only createTerminal was checked, so a terminal id from
a connection where the client did advertise terminal could reach waitForTerminalExit,
getTerminalOutput, releaseTerminal, or killTerminal on a different connection that never
advertised it.
ctx.execute(Command) is a convenience that runs all four steps for the common case: spawn, wait,
read, release, in one call, returning a CommandResult (output(), exitCode(): Integer, nullable
for a process a signal terminated, signal(), truncated() (whether the output was cut to the
client’s output limit), and a success() convenience for exitCode() == 0; no more timedOut()):
new CommandResult(output, exitCode, signal, truncated); the older
3-argument form (no truncated) still exists as a convenience for complete output.
See Module 18: Terminal Operations for the full
four-step version and why you’d use it instead of execute(...) (streaming output while the command
is still running, for one).
execute releases the terminal when the prompt is cancelled, not only when the command ends. ACP
requires an agent to release every terminal it creates; execute now sends terminal/release exactly
once on every path, including a cancellation that interrupts it while it’s waiting in
waitForTerminalExit. See Cancellation for the triggers that count.Permission
PermissionOptionKind (allow-once, allow-always, reject-once, reject-always, and others) is an open
value, the same discipline as StopReason: compare with .equals(...), and keep a default case for a
kind a newer agent might send. See
Forward Compatibility for why.
askPermission/askChoice announce a real tool call. Both send a tool_call session update
(status pending) before asking, so a client that looks the tool call up by ID finds it, and settle
it afterward with a tool_call_update (completed once answered, failed if the client cancelled
the request): a session update handler sees two more updates per call than it used to.
askPermission(action) alone uses kind other; pass a ToolKind explicitly (as above) for anything
else, including ToolKind.EDIT if you relied on the old always-edit behavior. askChoice fails
with AcpProtocolException (-32603) naming the option if the client answers with one it was never
offered, instead of the old unclear NumberFormatException/ArrayIndexOutOfBoundsException.Related
Concepts
Capabilities and NegotiatedCapabilities, which every call on this page is gated by
Testing
Exercising these calls without a real client or agent process