acpx streams agent activity in three output modes plus two modifiers. Pick the one that matches your consumer: a human terminal, an automation pipeline, or a script that only wants the final answer.
Explicit top-level output flags also apply when configuration fails to load: JSON modes emit one JSON-RPC error on stdout, quiet mode emits one stderr diagnostic, and text mode reports the error without a stack trace. If no output flag is supplied and configuration cannot load, errors use text mode.
#text (default)
Human-readable stream:
- assistant text as it arrives
[thinking]blocks for reasoning chunks[tool] <title> (<status>)blocks with output, diff previews, and plan updates[done] <stopReason>at the end
acpx codex 'review the auth module'
[thinking] Reading src/auth and looking for token validation
[tool] Read src/auth/index.ts (completed)
[tool] Run grep -n 'verifyToken' src/auth (completed)
output:
src/auth/jwt.ts:42:export function verifyToken
The auth module is structured as …
[done] end_turn
text is best for interactive use. It is not stable for parsing — error messages, prompts, and progress updates can change between releases.
Malformed tool locations or content produce a diagnostic and skip that tool update. The text stream continues to receive subsequent updates, assistant text, and prompt completion.
#json
NDJSON stream of raw ACP JSON-RPC messages on stdout:
acpx --format json codex exec 'review changed files' \
| jq -r 'select(.method=="session/update")'
{"jsonrpc":"2.0","id":"req-1","method":"session/prompt","params":{"sessionId":"019c…","prompt":[{"type":"text","text":"hi"}]}}
{"jsonrpc":"2.0","method":"session/update","params":{"sessionId":"019c…","update":{"sessionUpdate":"agent_message_chunk","content":{"type":"text","text":"Hello"}}}}
{"jsonrpc":"2.0","id":"req-1","result":{"stopReason":"end_turn"}}
Hard rules for json:
- No acpx-specific event envelope wrapping ACP messages.
- No synthetic
type/stream/eventVersionkeys injected onto raw ACP traffic. - No payload key renaming.
What you read on stdout is the same wire-level JSON that would have crossed the ACP transport, in submission order.
compare is the exception because it runs multiple one-shot sessions and emits a summarized CompareRow[] payload under --format json instead of interleaving raw ACP streams.
stderr can still contain prompts, progress, or warnings. If your script reads only stdout, that is fine. If you pipe both, see --json-strict below.
#--format json --json-strict
Strict JSON suppresses non-JSON output that would otherwise land on stderr:
acpx --format json --json-strict codex exec 'list TODO comments' > events.ndjson
--json-strict requires --format json. It guarantees:
- stdout is one ACP JSON-RPC message per line
- stderr stays quiet for non-error informational output
This is the right combination for "fully machine-consumed pipelines that should fail visibly on real errors."
#quiet
Final assistant text only — no tool blocks, no thinking, no [done]:
SUMMARY=$(acpx --format quiet codex exec 'one-line summary of this branch')
echo "$SUMMARY"
When the adapter includes final token usage and cost metadata in the prompt result, acpx emits that to stderr in quiet mode. stdout stays as the assistant text only.
If a quiet prompt fails, acpx exits non-zero and emits exactly one single-line diagnostic to stderr:
[acpx] error: <CODE> [<DETAIL_CODE>] <message>
DETAIL_CODE is omitted when unavailable. Embedded line endings in the message are replaced with spaces. The diagnostic never goes to stdout; quiet stdout remains reserved for any assistant text the adapter produced. This stderr contract applies to direct and queued prompts; it does not change the json or --json-strict streams.
Text already received is preserved even when a failed turn has no final ACP response. Quiet session-watch replay also emits buffered text when a failed or cancelled turn settles, without duplicating output from completed turns.
quiet is unaffected by --suppress-reads because it does not print tool call output to begin with.
#--suppress-reads
Replaces raw read-file payloads with a placeholder so logs stay readable when an agent reads a large file:
| Mode | Effect of --suppress-reads |
|---|---|
text | Read-like tool outputs render as [read output suppressed]. |
json | ACP fs/read_text_file responses and read-like tool-call outputs replace raw contents. |
quiet | No effect (quiet mode prints assistant text only). |
acpx --suppress-reads codex exec 'inspect repo and report tool usage'
The replacement preserves the surrounding ACP message shape so json consumers can still parse the stream — only the content payload is masked.
Live JSON read suppression keeps file responses separate from overlapping control responses, even when the client and agent use the same request ID. This requires an updated client and queue owner; older owners and saved raw session journals retain their existing behavior when transport direction is unavailable.
Title-based read detection uses complete leading words such as Read, cat, Open, and View; a filename or substring in another action's title does not suppress that action's output.
#Session-control command output
Session controls and queries emit command-specific JSON documents under --format json:
| Command | text | json | quiet |
|---|---|---|---|
Agent-side sessions list | TSV: sessionId title cwd updatedAt meta, plus next cursor when supplied | { source: "agent", sessions, ... }; optional _meta, cursor, cwd, nextCursor | one adapter session id per line |
Local sessions list --local or local fallback | TSV: local id, name, cwd, last-used time | array of full local session records | one local record id per line |
sessions show | key/value metadata | full local session record | local record id |
sessions history | session summary and TSV history entries | { id, sessionId, limit, count, entries } | one text preview per line |
sessions prune | summary plus pruned ids and time | { action, dryRun, count, bytesFreed, pruned } | one pruned id per line |
sessions new/ensure | local record id and creation/replacement summary | session_ensured result with created and local session identity | local record id |
status | key/value status summary | status_snapshot result | state token |
Closed records are marked [closed] in local-list text and quiet output.
#Identity fields in JSON
JSON field names depend on the command. Local lifecycle/control summaries and a status result with a matching record use these identity fields:
| Field | Meaning |
|---|---|
acpxRecordId | Local acpx record id |
acpxSessionId | ACP session id stored by that record |
agentSessionId | Optional provider-native id, when exposed by the adapter |
Full local records, returned by sessions show and local listing, name their ACP session field acpSessionId. Agent-side listing uses each adapter entry's sessionId and does not add local record identity. History, export/import, prune, and no-session status results have their own documented summary shapes; do not require the three fields above on every JSON result.
Do not assume a local record id can be passed to a native provider CLI. Use agentSessionId when the adapter supplies one.
#Picking a mode
| Use case | Pick |
|---|---|
| Interactive use, you are the reader | text (default) |
| Save full transcript for later replay | json (or --format json --json-strict) |
Pipe into jq and parse events | --format json or --json-strict |
| Capture only the final answer in a script | --format quiet |
| Long agent runs that read large files | add --suppress-reads |
| Anywhere stdout must be 100% JSON | --format json --json-strict |
#See also
- Sessions — what session-control commands return.
- Permissions — how denials surface in each format.
- CLI reference — full per-mode behavior table.
#Prompt response metadata
ACP prompt response _meta is passed through to compare summaries and embedded runtime completion results, including queued prompts. An absent field stays absent, and an explicit null stays null. Nested values are opaque, adapter-defined data; ACPX does not authenticate them or treat them as proof of model identity or configuration. Raw JSON output retains the original ACP response.
#Embedded host logging
Embedded ACP prompts leave the application's console.error function unchanged, including while prompts overlap or the host replaces its logger. Existing output modes and acpx warning and retry suppression controls keep their behavior.
#Embedded runtime plans
The acpx/runtime turn stream exposes ACP plan notifications as status events with tag: "plan". The existing text summary remains available. The optional entries field contains normalized AcpRuntimePlanEntry objects with content, status (pending, in_progress, or completed), and a valid advertised priority (high, medium, or low) when present.
Each entries array replaces the previous plan; entries: [] clears it. An omitted field supplies no structured snapshot. Entries with blank content or invalid status are skipped, and invalid priorities are omitted. Legacy updates without valid statuses can still produce a text summary. CLI JSON output continues to expose the original ACP payload.
#Embedded runtime tool calls
The acpx/runtime turn stream exposes both ACP tool_call and tool_call_update notifications as tool_call events, retaining the original tag. The optional kind preserves known ACP classifications, including switch_mode. Missing or unknown kinds are omitted without dropping the tool event.