From 7a1b77892a2a572c5f2bc4300ceb10a31b30ba3e Mon Sep 17 00:00:00 2001 From: Alex Clarke Date: Mon, 27 Jul 2026 20:02:13 -0600 Subject: [PATCH] docs: added documentation for the new headless mode and acp-server --- Environment-Variables.md | 2 +- Unattended-Mode.md | 184 +++++++++++++++++++++++++++++++++++++++ _Sidebar.md | 3 + 3 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 Unattended-Mode.md diff --git a/Environment-Variables.md b/Environment-Variables.md index e3ef5c2..5de5c45 100644 --- a/Environment-Variables.md +++ b/Environment-Variables.md @@ -120,5 +120,5 @@ can also pass the `--disable-log-colors` flag as well. # Miscellaneous Variables | Environment Variable | Description | Default Value | |----------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------| -| `AUTO_CONFIRM` | Bypass all `guard_*` checks in the bash prompt helpers; useful for agent composition and routing. Can also be enabled for a single invocation by passing `--dangerously-skip-permissions` (Coyote sets `AUTO_CONFIRM=true` for the process and all spawned tools/agents). | | +| `AUTO_CONFIRM` | Bypass all `guard_*` checks in the bash prompt helpers; useful for agent composition and routing. Set automatically when passing `--dangerously-skip-permissions`, `--headless`, or `--acp-server` (Coyote sets `AUTO_CONFIRM=true` for the process and all spawned tools/agents). See [Unattended Mode](Unattended-Mode). | | | `LLM_TOOL_DATA_FILE` | Set automatically by Coyote on Windows. Points to a temporary file containing the JSON tool call data.
Tool scripts (`run-tool.sh`, `run-agent.sh`, etc.) read from this file instead of command-line args
to avoid JSON escaping issues when data passes through `cmd.exe` → bash. **Not intended to be set by users.** | | diff --git a/Unattended-Mode.md b/Unattended-Mode.md new file mode 100644 index 0000000..647d526 --- /dev/null +++ b/Unattended-Mode.md @@ -0,0 +1,184 @@ +Coyote supports two unattended operation modes for scripting and programmatic use: + +- **`--headless`** suppresses all interactive prompts and runs as a non-interactive CLI process. +- **`--acp-server`** exposes a full agent server over stdio using JSON-RPC 2.0, suitable for host-side orchestration. + It implies `--headless`. + +--- + +# `--headless` + +The `--headless` flag runs Coyote in fully unattended mode. It is designed for use in scripts, CI pipelines, and +sandboxed environments where no user is present to respond to prompts. + +When `--headless` is active: + +- All interactive prompts (confirmations, selections, free-text inputs) return structured JSON instead of blocking. +- `AUTO_CONFIRM=true` is set for the process and all spawned tools. +- REPL mode is not supported; a prompt must be provided via `-f ` or as an inline argument. + +```shell +coyote --headless -f prompt.txt +coyote --headless "Summarize this project for me" +``` + +User-interaction tool calls (e.g. `user__confirm`, `user__select`) return a structured JSON object instead of prompting. +The response shape matches the tool's normal output, so agents handle it transparently. + +--- + +# ACP Agent Server (`--acp-server`) + +`--acp-server` starts Coyote as an ACP agent server over stdio. It is designed for host applications that need to drive +Coyote programmatically — the host sends JSON-RPC requests over stdin and reads JSON-RPC responses from stdout. + +Every byte on stdout is a valid JSON-RPC 2.0 frame. Diagnostic logs go to stderr (or to the log file if `COYOTE_LOG_FILE` +is configured). The flag implies `--headless`. + +```shell +coyote --acp-server +``` + +All normal context flags work alongside `--acp-server` and are applied before the server starts listening: + +```shell +coyote --acp-server --role diagnose +coyote --acp-server --agent sisyphus +coyote --acp-server --rag my-index --no-memory +coyote --acp-server --model gpt-4o +``` + +The server then inherits the fully-configured context — the client manages sessions via `session/new` / `session/load` +as normal. + +## Protocol + +Each request and response is a single JSON object on one line, terminated by `\n`. Send requests line-by-line; the +server processes them sequentially and writes a response (or nothing, for notifications) before reading the next line. + +**Request:** +```json +{"jsonrpc": "2.0", "id": 1, "method": "method/name", "params": {...}} +``` + +**Success response:** +```json +{"jsonrpc": "2.0", "id": 1, "result": {...}} +``` + +**Error response:** +```json +{"jsonrpc": "2.0", "id": 1, "error": {"code": -32601, "message": "Method not found: ..."}} +``` + +## Methods + +| Method | Params | Result | Notes | +|------------------|-------------------------|---------------------------------------------|-------------------------------------------------------------------------------------| +| `initialize` | none | `{name, version, protocolVersion}` | Returns server identity. Call this first. | +| `session/new` | none | `{sessionId: "default"}` | Starts a new temporary session. Only one session per process. | +| `session/load` | `{sessionId: ""}` | `{sessionId: ""}` | Loads a previously saved session by name (as shown in `coyote --list-sessions`). | +| `session/prompt` | `{text: ""}` | `{output, stopReason}` | Runs one conversation turn. Requires an active session. | +| `session/cancel` | none (notification) | `{}` if an `id` was given, else no response | Sets the abort flag for the next call. See [Known Limitations](#known-limitations). | + +## Outbound Notifications + +The server may emit notifications (messages without an `id`) between a request and its response. + +| Notification | Params | When | +|------------------------------|-------------------------------|-------------------------------------------------------------------------------------------------------------------------------------| +| `session/request_permission` | `{action, question, options}` | Emitted when a tool triggers a user-interaction call (e.g. `user__confirm`, `user__select`). Sent before the turn's final response. | + +## Error Codes + +| Code | Meaning | +|----------|--------------------------------------------------------------------| +| `-32700` | Parse error — request line was not valid JSON | +| `-32601` | Method not found | +| `-32602` | Invalid params — a required field is missing | +| `-32600` | Invalid request — e.g. `session/prompt` before `session/new` | +| `-32000` | Application error — e.g. no active session, session already active | + +## Smoke Testing + +Build the binary first (`cargo build`), then use a heredoc to send multiple requests: + +**Structural test (no LLM required):** + +```shell +./target/debug/coyote --acp-server << 'EOF' +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}} +{"jsonrpc":"2.0","id":2,"method":"unknown/method","params":{}} +EOF +``` + +Expected: initialize succeeds, then `-32601` method-not-found. + +**Parse error recovery:** + +```shell +./target/debug/coyote --acp-server << 'EOF' +not valid json +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}} +EOF +``` + +Expected: `-32700` for the bad line, then a valid initialize response. The server continues processing after a parse +error. + +**Full session (requires a configured model):** + +```shell +coyote --acp-server << 'EOF' +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}} +{"jsonrpc":"2.0","id":2,"method":"session/new","params":{}} +{"jsonrpc":"2.0","id":3,"method":"session/prompt","params":{"text":"Reply with the single word: pong"}} +EOF +``` + +**Multi-turn context:** + +```shell +coyote --acp-server << 'EOF' +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}} +{"jsonrpc":"2.0","id":2,"method":"session/new","params":{}} +{"jsonrpc":"2.0","id":3,"method":"session/prompt","params":{"text":"Count to 3 and stop."}} +{"jsonrpc":"2.0","id":4,"method":"session/prompt","params":{"text":"What was the last number you said?"}} +EOF +``` + +id=4 should answer "3", confirming session history threads across turns. + +**Load a saved session:** + +```shell +coyote --acp-server << 'EOF' +{"jsonrpc":"2.0","id":1,"method":"initialize","params":{}} +{"jsonrpc":"2.0","id":2,"method":"session/load","params":{"sessionId":"my-saved-session"}} +{"jsonrpc":"2.0","id":3,"method":"session/prompt","params":{"text":"Summarize what we've discussed."}} +EOF +``` + +## Known Limitations + +**`session/cancel` cannot interrupt a running `session/prompt`.** The server processes requests sequentially. While a +`session/prompt` turn is executing, the server is not reading stdin. `session/cancel` sets an abort flag that takes +effect on the *next* invocation. + +**`session/request_permission` notifications are post-turn.** They are emitted immediately before the turn's final +response, not mid-stream. A real-time permission bridge would require a concurrent server design. + +--- + +# Sbx-Kit Headless Profile + +The embedded sbx kit ships a `headless` profile for running Coyote in unattended mode inside a sandbox. Set +`COYOTE_PROMPT_FILE` to the path of a prompt file before launching: + +```shell +export COYOTE_PROMPT_FILE=/path/to/prompt.txt +sbx run coyote --profile headless +``` + +The headless profile passes `--headless -f "${COYOTE_PROMPT_FILE}"` to Coyote and sets `COYOTE_LOG_LEVEL=WARN` to +reduce noise on stderr. For ACP use inside a sandbox, override the entrypoint directly via `sbx run --entrypoint`. diff --git a/_Sidebar.md b/_Sidebar.md index 5d10053..e52d82b 100644 --- a/_Sidebar.md +++ b/_Sidebar.md @@ -11,6 +11,9 @@ - [Shell Integrations](Shell-Integrations) - [Bash Prompt Helpers](Bash-Prompt-Helpers) - [Sessions](Sessions) +- [Unattended Mode](Unattended-Mode) + - [--headless](Unattended-Mode#--headless) + - [ACP Agent Server](Unattended-Mode#acp-agent-server---acp-server) ## Model Providers - [Clients](Clients)