# `SuperPi.ACP.Server`
[🔗](https://github.com/oleksandr/super-pi-beam/blob/v0.1.0/lib/super_pi/acp/server.ex#L1)

Agent Client Protocol agent over stdio: `bin/super-pi acp`.

Editors (Zed, JetBrains, GitHub Copilot CLI, the neovim plugins) start the agent as a subprocess and
speak JSON-RPC 2.0, one message per line. ACP is bidirectional: the agent also sends requests
(`session/request_permission`, `fs/read_text_file`) and waits for the answer. So this is not
`SuperPi.RPC.Server`: that module only replies and notifies and has no table of outstanding
requests. Here a `pending` table maps each outgoing request id to what waits for it.

## Methods

| ACP | Super-Pi |
|---|---|
| `initialize` | Agent capabilities, `authMethods` from `Auth.required?/0`, harness capabilities in `_meta.superPi.harnesses` |
| `authenticate` | The token store of `auth.login` (token in `_meta.token` or `SUPER_PI_GATEWAY_TOKEN`) |
| `session/new` | A conversation with `workspacePaths: [cwd]`. `_meta.harness` picks the harness. `mcpServers` are applied to the MCP manager |
| `session/load` | Resume (not fork): replays history as `session/update` notifications, then answers |
| `_superpi/session/fork` | Fork at the latest entry into a new session (ACP's `_` convention for custom methods) |
| `session/prompt` | Submits in a supervised task, so `session/cancel` can arrive during the prompt |
| `session/cancel` | `UI.Broker.cancel_pending/1`, then `Machine.cancel/1`, in that order |
| `session/set_mode` | Sets `executionMode` within the remote ceiling and the harness ceiling |

`stopReason`: `end_turn` for a finished turn, `cancelled` for a cancel, `refusal` for an error (the
error text goes to the client as a message chunk first).

## Terminals and files

`terminal/*` is never delegated: `SandboxRunner` gives process-group kill, output spill and the
sandbox, and an editor terminal gives none of that and bypasses `permissions.deny`. A running command
is a `tool_call` of kind `execute`.

`config :super_pi, :acp, fs_delegation: :unsaved_only | :never | :always` (default
`:unsaved_only`). `fs/read_text_file` returns the unsaved editor buffer, the only reason to delegate.
`SuperPi.Env.ClientFS` asks the client only when the client advertised the method, the machine runs
on this node (the editor's files do not exist on another node) and the path is inside the
workspace. Writes always stay on the server.

## Transport

`run_stdio/1` starts the server and reads standard input. Output is one JSON object per line on
standard output, so logs must go to standard error (`SUPER_PI_LOG_STDERR=1`, set by `bin/super-pi`).
For tests, `start_link(send: fun)` and `send(server, {:acp_line, line})`.

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `read_text_file`

```elixir
@spec read_text_file(pid(), integer(), String.t(), keyword()) ::
  {:ok, String.t()} | {:error, term()}
```

Asks the client for the text of a file, including unsaved changes. Used by `SuperPi.Env.ClientFS`.

# `run_stdio`

Starts the server on standard input and output and returns when the input ends.

# `start_link`

---

*Consult [api-reference.md](api-reference.md) for complete listing*
