/** * artifacts/streaming — the OPTIONAL leg of the port, and how to ask for it. * * A claim check exists so a 6 MB result never rides the conversation. It does * not follow that the 6 MB should ride the PROCESS: a store that can accept * bytes as they arrive, and hand them back the same way, lets a report be * written to a bucket without ever existing whole in memory. That is what * `putStream` / `getStream` are for. * * ── Why OPTIONAL members and not two more verbs ───────────────────────────── * Not every backend can honor the promise, and the ones that cannot must say * so by ABSENCE rather than by faking it. A `Map`-backed store "streaming" * bytes it already holds whole would be theatre: the memory was already spent, * and a consumer that chose it to bound memory would have been told a * falsehood in the shape of a capability. So: * * • `fileArtifacts` — YES. The bytes go to a sibling file as they arrive. * • object stores — YES, through the SDK's own streaming upload/download. * • `sqliteArtifacts`— NO. `node:sqlite` reads and writes a BLOB whole; * there is no incremental blob I/O to build on, so a * stream here would buffer everything and call it a * stream. * • `inMemoryArtifacts` — NO, by design. It holds payloads whole, under a * byte budget; streaming into it would defeat both. * * ── Web streams, one shape ────────────────────────────────────────────────── * `ReadableStream` — the shape that exists in every supported Node * AND in a browser, so the port stays the same object everywhere the rest of * this folder already works (the Buffer-free base64 law in `payload.ts`, for * the same reason). Node-native adapters bridge at their own edge with * `Readable.toWeb` / `Readable.fromWeb`; that bridge is an ADAPTER detail and * never leaks into the port. * * ── What streaming COSTS, named so it cannot be assumed away ──────────────── * `get` is the VERIFYING read: when the meta carries a `digest`, the store * re-hashes what it holds and throws `ArtifactIntegrityError` rather than hand * back bytes that no longer match their ticket. **`getStream` does not, and * cannot**: the digest is computed over the WHOLE canonical payload with the * one primitive every adapter shares (`crypto.subtle.digest`, one-shot by * design and identical in Node and a browser), so verifying a stream would mean * buffering the payload — the exact cost the member exists to avoid. Faking it * with a second, incremental hashing implementation would be a different * promise wearing the same field name. * * So the trade is STATED rather than silently made, in all three places a * caller meets it (the port's `getStream`, the guard below, and this header): * * • `get(scope, ref)` — integrity verified, payload held whole. * • `getStream(scope, ref)` — memory bounded, integrity NOT verified. The * `digest` still rides `meta`, so a caller who needs the guarantee on this * path can hash what it collected and compare — deliberately the caller's * move, because only the caller knows whether it can afford to hold the * bytes it just streamed. * * ── What `ctx.artifacts` does NOT get, and why (a decision, not an omission) ─ * The tool-facing capability keeps FIVE verbs this phase. A tool receives its * artifacts pre-scoped and answers a model; the model cannot hold a stream, * and a tool that genuinely moves gigabytes wants the store itself, with its * own lifecycle and its own error handling. So a tool that needs streaming * holds the store (it already constructed it) and calls it directly — while * `ctx.artifacts` stays the small, safe, scope-bound surface a model-driven * tool can be trusted with. */ import { type ArtifactStore } from './types.js'; /** An {@link ArtifactStore} that really implements `putStream`. */ export type PutStreamingArtifactStore = ArtifactStore & Required>; /** An {@link ArtifactStore} that really implements `getStream`. */ export type GetStreamingArtifactStore = ArtifactStore & Required>; /** An {@link ArtifactStore} that implements BOTH streaming members. */ export type StreamingArtifactStore = ArtifactStore & Required>; /** * Can this store take a streamed put? Narrowing type guard — the answer is * the type, so a consumer branches once and the compiler carries it. * * @example * if (canPutArtifactStream(store)) await store.putStream(scope, input, body); * else await store.put(scope, { ...input, data: await collect(body) }); */ export declare function canPutArtifactStream(store: ArtifactStore): store is PutStreamingArtifactStore; /** * Can this store hand back a stream? Narrowing type guard. * * Note what the streamed read does NOT carry: `get` re-verifies a `digest` * before returning, `getStream` cannot (see the header) — it bounds memory, * not integrity. Branching to `getStream` for a digested artifact is a * deliberate trade, and the ticket keeps the digest so a caller who needs the * guarantee can make it themselves. */ export declare function canGetArtifactStream(store: ArtifactStore): store is GetStreamingArtifactStore; /** Both halves at once — for a consumer that needs the round trip. */ export declare function canStreamArtifacts(store: ArtifactStore): store is StreamingArtifactStore; /** * Read a stream to its bytes, refusing at a ceiling instead of growing until * the process dies. * * Adapters that must hold the payload to store it (an object `put` of a * declared length) use this, and so does any consumer bridging a stream into * `put`. The ceiling is the caller's declared `bytes`: a producer that says * 10 MB and sends 11 is refused rather than trusted, because the meta it * would otherwise mint describes a payload that does not exist. */ export declare function collectStream(body: ReadableStream, ceiling: number): Promise<{ readonly bytes: Uint8Array; readonly overflowed: boolean; }>; /** One chunk of bytes as a web stream — how a store that HOLDS the payload * answers `getStream` honestly (it never claimed to be lazy; it is handing * over what it has, in the port's shape). */ export declare function bytesAsStream(bytes: Uint8Array): ReadableStream; /** The stated `bytes` on a streamed put, checked before anything is stored. */ export declare function assertStreamBytes(adapter: string, bytes: unknown): number;