SDK artifact output contracts
Trackers: pm-dilou2 and pm-mav1ak.
Exporter artifacts are data streams, not command receipts. A package declares the channel when it registers an exporter so the host never guesses whether a returned value should be appended to artifact bytes.
Agent quick context
- Declare
output.channel: "stdout"for JSON, NDJSON, CSV, or opaque bytes. The host suppresses its receipt by default, including under global--json. - Declare
output.channel: "file"when the exporter writes a file. The host renders the returned bounded receipt by default. - An exporter may still return a structured result. Suppressed receipts remain available to hooks, telemetry, and embedding hosts.
- Write optional human progress or summaries to stderr. Never mix them into a stdout artifact.
- Legacy registrations without
outputretain their previous rendering.
Registration
import type { ExtensionApi } from "@unbrained/pm-cli/sdk";
export function activate(api: ExtensionApi): void {
api.registerExporter(
"report-json",
async () => {
process.stdout.write('{"items":[]}\n');
return { exported: 0 };
},
{
description: "Export a complete JSON report to stdout.",
output: {
channel: "stdout",
media_type: "application/json",
},
},
);
}
The normalized contract is visible in the extension contribution inventory. Derived help also states that stdout artifact bytes are exclusive and host receipt rendering is suppressed when the extension does not supply a custom description.
Channel behavior
| Artifact channel | Default receipt | stdout | stderr |
|---|---|---|---|
stdout |
suppress |
Artifact bytes only | Extension-owned diagnostics or summary |
file |
render |
Bounded structured receipt | Extension-owned diagnostics |
File exporters may select receipt: "render" or receipt: "suppress" as an
explicit compatibility control. Stdout artifacts always suppress the host
receipt; declaring receipt: "render" with channel: "stdout" is rejected at
registration so redirection, jq, and binary output remain clean by
construction.
The host does not decode, re-encode, buffer, or inspect stdout artifact bytes. Consequently NUL bytes and non-UTF-8 payloads pass through unchanged. The extension owns media correctness and stream completion; the host owns only the post-handler receipt policy.
Shared NDJSON framing
SDK-built producers can use serializeNdjsonStream(rows, trailer) to append one
typed pm.stream.trailer after a bounded row batch. It supplies one stable
place for counts, continuation cursors, source identity, and constant-size
producer metadata without adding per-row overhead.