Output & Policy

Output formats

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 / eventVersion keys 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:

ModeEffect of --suppress-reads
textRead-like tool outputs render as [read output suppressed].
jsonACP fs/read_text_file responses and read-like tool-call outputs replace raw contents.
quietNo 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:

Commandtextjsonquiet
Agent-side sessions listTSV: sessionId title cwd updatedAt meta, plus next cursor when supplied{ source: "agent", sessions, ... }; optional _meta, cursor, cwd, nextCursorone adapter session id per line
Local sessions list --local or local fallbackTSV: local id, name, cwd, last-used timearray of full local session recordsone local record id per line
sessions showkey/value metadatafull local session recordlocal record id
sessions historysession summary and TSV history entries{ id, sessionId, limit, count, entries }one text preview per line
sessions prunesummary plus pruned ids and time{ action, dryRun, count, bytesFreed, pruned }one pruned id per line
sessions new/ensurelocal record id and creation/replacement summarysession_ensured result with created and local session identitylocal record id
statuskey/value status summarystatus_snapshot resultstate 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:

FieldMeaning
acpxRecordIdLocal acpx record id
acpxSessionIdACP session id stored by that record
agentSessionIdOptional 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 casePick
Interactive use, you are the readertext (default)
Save full transcript for later replayjson (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 filesadd --suppress-reads
Anywhere stdout must be 100% JSON--format json --json-strict

#See also

#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.