> Canonical guide: https://developers.foxlight.ai/build/sdk/streams/
> Contract snapshots: Skulk 2.0.0 (b0af39c79b6b7102b2478062904f1d7cc8619975); SDK 0.4.0 (31bb090b8f64689f87514e48385e22b6ad94a6c2). Check the installed runtime when versions differ.

# Managed _streaming_

SDK 0.4.0 supports managed providers in all four I/O modes. Streaming uses a
fresh authenticated media connection per invocation, independent of health.

| Mode             | Caller input                                  | Provider output              |
| ---------------- | --------------------------------------------- | ---------------------------- |
| Unary            | One JSON request                              | One result                   |
| Server streaming | Initial JSON request                          | Chunks, then terminal        |
| Client streaming | Initial request, input chunks, input terminal | One final output terminal    |
| Bidirectional    | Initial request, input chunks, input terminal | Chunks, then output terminal |

## Implement the child handler

When the primary IPC loop receives `StreamInvoke`, start an owned task calling
`execute_stream(startup, message, handler)`. Continue answering `Health`; cancel
and await your owned task on shutdown or primary-channel loss.

```python
from collections.abc import AsyncGenerator, AsyncIterator
from skulk_capability_sdk.contracts import StreamInvoke
from skulk_capability_sdk.streams import StreamFrame

async def echo_stream(
    call: StreamInvoke,
    inputs: AsyncIterator[StreamFrame],
) -> AsyncGenerator[StreamFrame]:
    sequence = 1
    async for frame in inputs:
        if frame.kind == "chunk":
            yield StreamFrame(
                call_id=call.call_id,
                direction="provider_to_caller",
                sequence=sequence,
                kind="chunk",
                payload=frame.payload,
                media=frame.media,
            )
            sequence += 1
        if frame.is_terminal:
            break
    yield StreamFrame(
        call_id=call.call_id,
        direction="provider_to_caller",
        sequence=sequence,
        kind="completed",
    )
```

This bidirectional example requires matching input/output chunk schemas. Skulk
owns output `started` at sequence zero. Your handler starts at one, emits exactly
one terminal, and returns. Cleanup finishes before the helper publishes the
terminal and private acknowledgment. Extra frames after a terminal are rejected.

## Half-close, cancellation and bounds

Caller input `completed` carries no payload or media and half-closes input while output remains open. Caller
cancellation or disconnect cancels the invocation. Use `try/finally` for provider
resources and keep blocking work off the event loop. Provider output completion
stops input forwarding before cleanup closes the media connection.

- One active invocation per child, shared by all modes.
- One stream deadline, capped at 300 seconds by `Bounds.stream_seconds`.
- Each packet has a four-byte network-order header length, at most 64 KiB of
  finite JSON, then at most 1 MiB of raw inline media. Bytes never appear in JSON.
- Eight-frame ingress queues and awaited socket writes apply backpressure.
- The helper uses only the owner-provisioned `Startup.stream_socket`, process
  token, installed identity, manifest digest and fresh local call identity.
- Wrong identity, sequence, schema or framing fails the call. Failed processes
  follow normal supervision and restart budgets; work is never replayed.
- Media stays out of replicated State and event logs. Blob attachments remain
  immutable references; the bridge does not open caller-supplied files.

Deploy the compatible Skulk reader before enabling protocol-4 streaming bundles.
Rebuild immutable runtimes with the compatible SDK and qualify signed installation
separately from source tests. Protocol 3 unary children remain compatible for
one release cycle; protocol 2 requires rebuilding.

The managed host callback channel is a fixed unary management interface.
General Fabric clients use the [public caller contract](https://developers.foxlight.ai/build/calling-and-streaming/).
This SDK's streaming interface serves those calls from managed children.

[Streams API reference](https://developers.foxlight.ai/build/sdk/reference/streams/)
