Skip to content

Output formats

Machine output is available for one-shot runs and the bounded session operations below.

The current one-shot CLI default is schema v8. A client can pin schema v4, v5, v6, or v8 with --output-schema-version, together with --output-format json or stream-json. On one-shot task, continue, and resume operations, each version projects every stdout record onto its published shape. Unsupported selectors fail before a rollout is opened.

PlantCore resident runs select schema v7 through their admitted bootstrap. Schema v7 is emitted on the resident App Server event stream and is not a generic one-shot CLI projection.

iteron --machine-contract is a provider-free capability query. Its JSON reports cli_stream_versions, default_cli_stream_version, and resident_protocol_version as distinct values; a launcher must not infer the CLI stream version from the resident SQ/EQ protocol.

Session operations

Schema-selected session operations use direct argv and emit one typed object:

  • --sessions [--session-limit N] [--session-cursor TOKEN] [--agent-definition-tag TAG] emits session_list_page { sessions, next_cursor };
  • --transcript RUN_ID [--transcript-cursor TOKEN] emits session_transcript_page { run_id, events, older_cursor };
  • --fork RUN_ID emits session_fork_result { parent_run_id, child_run_id, fork_point, status: "created" }.

Cursors are opaque and bounded. List cursors are tied to their tenant and tag filter; transcript cursors are tied to their run. A transcript is read newest-page first while events within each page remain chronological. A stale, modified, or cross-query cursor is rejected rather than returning a page with silent gaps or duplicates.

--agent-definition-tag is immutable grouping metadata: at most 128 UTF-8 bytes, non-blank, control-free, and rejected when it resembles a credential. A fresh run records it, resume may only repeat the recorded value, forks inherit it, and legacy untagged sessions remain readable.

Final result

Both json and stream-json end with an authoritative result object:

{
  "schema_version": 8,
  "type": "result",
  "outcome": "done",
  "reason": null,
  "success": true,
  "assistant_text": "...",
  "run_id": "...",
  "cost_usd": null,
  "cost_status": "...",
  "cost_reason": null,
  "turns": 1,
  "kernel_tax": {
    "admission_latency_us": 0,
    "broker_latency_us": 0,
    "record_fsync_latency_us": 0,
    "estimated_tokens": 0,
    "failed_runs": 0
  },
  "exit_code": 0,
  "error": null
}

The example shows shape, not guaranteed values. cost_usd can be null or unknown when no authoritative price evidence exists. Schemas v5, v6, and v8 require the typed kernel_tax object; its latency values are measured in microseconds and all five fields are non-negative integers.

Schema v4 introduced the terminal outcome value drained, which later schemas retain: it means the runtime stopped cleanly after quiescing admitted work and durably checkpointing the workspace. It has success: true and exit code 0, but remains distinct from ordinary done.

Stream event vocabulary

stream-json may emit:

  • assistant_text and thinking deltas;
  • input_attachment metadata before a multimodal SQ submission;
  • phase and turn_end lifecycle events;
  • tool_start, tool_end, and approval_request;
  • notice, steer_applied, and run_done;
  • in v8, approval_resolved, steer_submission_applied, control_submission_applied, and submission_rejected with exact submission IDs;
  • workflow_start, workflow_plan, workflow_phase;
  • workflow_agent_start, workflow_agent_activity, workflow_agent_end;
  • workflow_end;
  • the terminal result.

Every event carries schema_version. Consumers should ignore unknown event types they do not need and use the final result as the authoritative terminal outcome. input_attachment carries only ordinal, media_type, and encoded_bytes; it never carries image bytes, a filename, or a path. Historical v4 event fixtures and their terminal result remain unchanged; consumers should skip this v5+ tag when they do not need attachment metadata.

The four exact submission lifecycle records are declared only for v8. When an operator selects v4, v5, or v6, each projects to a bounded, scrubbed notice without an ID or an Applied claim; clients needing authoritative receipts should use v8 or the Product V1 App Server contract. An approval request is not itself an approval resolution, and a queued submission is not proof of application.

Schema v6 adds turn_end.context.components, a non-overlapping vector of stable_prefix_tokens, instruction_tokens, task_context_tokens, memory_tokens, transcript_tokens, attachment_tokens, tool_schema_tokens, tool_result_tokens, and lsp_result_tokens. These are content-free estimates from the same admission boundary as the aggregate context estimate; evaluation uses them to attribute efficiency to general runtime modules rather than to provider or model names.

Run-declared artifacts

A run can declare a product it made: a report, a table, a diff. Before this existed the only way to learn that a run had produced something was to parse the assistant's prose, so every consumer invented its own convention and none of them could be verified.

The declaration is a handle, never inline content, because the evolution registry has to be able to verify what it holds. The handle carries everything a consumer needs to store, list and reopen the product later:

what where it comes from
the ref and its content address artifact.hash
its kind artifact.schema
its producing turn the enclosing event's turn
the tool or effect that made it artifact.producer, artifact.provenance.effect_id
where it lives artifact.locator

Two rules the producer path enforces rather than documents.

The content lands before the event does. Admission takes a durability witness and refuses without one, so a handle never names content that is not yet readable. An event that preceded its write would hand a consumer a handle to nothing.

Exceeding the per-run ceiling is counted, never silently dropped. A run may declare at most 256 artifacts; beyond that a declaration is refused and tallied, because a product stream that quietly stops is indistinguishable from a run that stopped producing. A duplicate content address is refused the same way, so a product gets exactly one event.

On the durable record this is artifact_produced, appended under abi.md §4.3(b)2: every byte already on disk decodes unchanged, so PROTOCOL_VERSION does not bump for it. The content address is preserved through redaction — masking it would make the product unfindable — while the locator, being a path, is scrubbed.

Stdout and stderr

For machine formats, stdout remains JSON or JSONL; diagnostics are written to stderr. Do not merge the two streams before parsing.

Model and tool text passes through output-boundary redaction. Streaming deltas use a stateful scrubber so a credential-shaped token split across transport chunks is not exposed merely because each chunk looked incomplete. This is defense in depth, not permission to send secrets to a model or tool.