REQ-O-004: --output jsonl / --stream Flag
Tier: Opt-In | Priority: P2
Source: §5 Pagination & Large Output
Addresses: Severity: High / Token Spend: High / Time: High / Context: Critical
Description
For commands that process or return large datasets, the framework MUST support --stream (equivalent to --output jsonl) which emits one JSON object per line as results are produced, rather than buffering all results before emitting. Commands that support streaming MUST declare supports_streaming: true. In streaming mode, pagination metadata MUST be emitted as a final summary line.
Commands that stream by default (see §76) MUST additionally declare streaming_default: true. A streaming-default command MUST accept --no-stream (equivalent to --output json) to return a buffered ResponseEnvelope for compatibility with envelope-only consumers. The streaming_default field MUST be advertised in the manifest and in --help text.
Acceptance Criteria
--streamcauses output to begin appearing before the command completes- Each line of streaming output is a valid, self-contained JSON object
- The final line of streaming output is a summary object containing
paginationmetadata - A command that does not declare
supports_streaming: trueemits a warning when--streamis passed - A command that declares
streaming_default: trueemits JSONL without any flags - Passing
--no-streamto a streaming-default command returns a validResponseEnvelope - The manifest exposes
streaming_default: truefor commands that declare it
Schema
No dedicated schema type — this requirement governs streaming output format without adding new wire-format fields. Each line is a self-contained JSON object using the command's declared item type. The final summary line reuses ResponseMeta field names.
Wire Format
$ tool list-deployments --stream
{"id": "d1", "status": "complete", "target": "prod"}
{"id": "d2", "status": "running", "target": "staging"}
{"id": "d3", "status": "failed", "target": "dev"}
{"_summary": true, "total": 3, "duration_ms": 280}
With an unsupported command:
$ tool deploy --target staging --stream
{
"ok": false,
"data": null,
"error": { "code": "STREAMING_NOT_SUPPORTED", "message": "deploy does not support --stream" },
"warnings": [],
"meta": { "duration_ms": 3 }
}
Streaming-default command — JSONL without flags; envelope via --no-stream:
$ tool list-events
{"id": "e1", "type": "deploy", "ts": 1700000001}
{"id": "e2", "type": "rollback", "ts": 1700000042}
{"_summary": true, "total": 2, "duration_ms": 18}
$ tool list-events --no-stream
{"ok": true, "data": [{"id": "e1", ...}, {"id": "e2", ...}], "error": null, "warnings": [], "meta": {"total": 2, "duration_ms": 18}}
Example
Commands opt in by declaring supports_streaming: true at registration time.
app = Framework("tool")
register command "list-deployments":
supports_streaming: true
# items emitted via framework stream() call as they arrive
# tool list-deployments --stream → JSONL lines as items arrive
# tool list-deployments --output jsonl → identical behavior
Related
| Requirement / Source | Tier | Relationship |
|---|---|---|
| REQ-O-001 | O | Specializes: --stream is equivalent to --output jsonl with incremental emission |
| REQ-O-003 | O | Composes: pagination summary emitted as the final stream line |
| REQ-F-053 | F | Provides: stdout unbuffering required for streaming to work |
| §76 | — | Provides: failure mode when streaming_default is undeclared |
| Guide: Streaming vs Envelope | — | Provides: decision criteria for choosing streaming-default vs envelope-default |