> ## Documentation Index
> Fetch the complete documentation index at: https://lab.pollack.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Agent-to-Client Calls

> Files, terminals, and permission: the requests an agent makes of the client mid-turn, and how a client serves them

Most of ACP flows client-to-agent, but three families of call run the other way, mid-prompt, back
into the client: file access, terminal execution, and permission requests. The agent calls them
exactly like any other request; the client answers them with a handler it registered on its builder.

The prompt context keeps a convenience layer directly on it (`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

```java theme={null}
// Agent, from a prompt handler
String content = ctx.readFile("src/Main.java");               // convenience: throws if unreadable
Optional<String> maybe = ctx.tryReadFile("src/Main.java");     // convenience: empty if unreadable
ctx.writeFile("output.txt", "generated content");

// The full request/response form, with an offset and line limit
var response = ctx.client().readTextFile(new ReadTextFileRequest(sessionId, "large-file.txt", 100, 50));
```

```java theme={null}
// Client
AcpSyncClient client = AcpClient.sync(transport)
    .readTextFileHandler(req -> new ReadTextFileResponse(Files.readString(Path.of(req.path()))))
    .writeTextFileHandler(req -> {
        Files.writeString(Path.of(req.path()), req.content());
        return new WriteTextFileResponse();
    })
    .build();
```

<Warning>
  **acp-autoconfig's file capabilities default to `false`.** A Spring Boot client built with the
  starter advertises neither `fs.readTextFile` nor `fs.writeTextFile` until you register a handler
  with an `AcpClientCustomizer` **and** turn on the matching property
  (`spring.acp.client.capabilities.read-text-file`/`write-text-file`). See
  [the Boot HTTP page](/docs/acp-java-sdk/autoconfig#enabling-file-access-over-http-or-any-transport).
  A handler built with the plain SDK builder, as above, advertises the capability for you from
  whether a handler is registered.
</Warning>

The agent checks `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 on `ctx.client()`:

| Step | Agent calls | Client handles | Purpose |
| - | - | - | - |
| 1 | `client().createTerminal(...)` | `createTerminalHandler` | Spawn the process |
| 2 | `client().waitForTerminalExit(...)` | `waitForTerminalExitHandler` | Block until it finishes |
| 3 | `client().getTerminalOutput(...)` | `terminalOutputHandler` | Read accumulated stdout/stderr |
| 4 | `client().releaseTerminal(...)` | `releaseTerminalHandler` | Free the client's resources |

`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()`):

```java theme={null}
CommandResult result = ctx.execute("ls", "-la");
```

The canonical constructor is `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](/docs/acp-java-sdk/tutorial/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).

<Note>
  **`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](/docs/acp-java-sdk/cancellation) for the triggers that count.
</Note>

## Permission

```java theme={null}
// Agent
boolean allowed = ctx.askPermission("Delete files in /tmp?");                            // convenience, kind "other"
boolean allowedEdit = ctx.askPermission("Rewrite config.yaml", ToolKind.EDIT);            // convenience, explicit kind
Optional<String> choice = ctx.askChoice("Which format?", "JSON", "XML", "YAML");          // convenience, empty if cancelled

// Full form
var response = ctx.client().requestPermission(new RequestPermissionRequest(sessionId, toolCall, options));
```

```java theme={null}
// Client
.requestPermissionHandler(req -> new RequestPermissionResponse(req.options().getFirst().id()))
```

`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](/docs/acp-java-sdk/forward-compatibility) for why.

<Note>
  **`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`.
</Note>

## Related

<CardGroup cols={2}>
  <Card title="Concepts" icon="compass" href="/docs/acp-java-sdk/concepts">
    Capabilities and NegotiatedCapabilities, which every call on this page is gated by
  </Card>

  <Card title="Testing" icon="flask" href="/docs/acp-java-sdk/testing">
    Exercising these calls without a real client or agent process
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.