# Stream resume

> Reconnect to a detached durable stream and replay it from the last received cursor.

Source: https://maincode.com/docs/python-agent-sdk-stream-resume
Section: Agent SDK · Matilda documentation

---

Durable streaming lets a client disconnect mid-stream and resume from where it left off. The server buffers events, keyed by a `stream_id` advertised at stream start.

## `resume_agent_stream(stream_id, last_event_id=None, *, on_event=None, ...)`

Resumes a previously detached stream by replaying buffered events from `last_event_id`. Returns the accumulated `AgentRunResult`.

```python title="resume.py"
import asyncio

from matilda_agent_sdk import resume_agent_stream


async def main():
    result = await resume_agent_stream(
        saved_stream_id,
        saved_last_event_id,
        on_event=lambda event: (
            print(event.delta, end="", flush=True)
            if event.type == "response.output_text.delta"
            else None
        ),
    )
    print("Resumed output:", result.final_output)
```

## Parameters

| Field | Type | Description |
| - | - | - |
| `stream_id` | `str` | The stream ID from the stream.started event (or result.stream\_id). |
| `last_event_id` | `str \| None` | The last cursor received (from the response.cursor event or result.last\_event\_id). Omit to replay from the start. |
| `on_event` | `Callable[[AgentEvent], None] \| None` | Event handler callback. |
| `client` | `MatildaClient \| None` | Explicit client. Defaults to the shared default. |
| `access_token` | `str \| None` | Override access token. |
| `fingerprint` | `str \| None` | Device fingerprint. |
| `stall_timeout_ms` | `int \| None` | SSE stall watchdog timeout. Defaults to 45\_000. |

Returns `AgentRunResult`.

## 401 auto-refresh

If the resume request returns 401 and the client has a `get_token` provider, the SDK automatically refreshes the token and retries once.

## Full resume example

```python title="full_resume.py"
import asyncio

from matilda_agent_sdk import resume_agent_stream, stream


async def main():
    stream_id = None
    last_event_id = None
    received_text = ""

    # Start streaming — capture IDs for potential resume
    async for event in stream({"name": "resumable-agent"}, "Tell me a fact."):
        if event.type == "stream.started":
            stream_id = event.stream_id
        if event.type == "response.cursor":
            last_event_id = event.last_event_id
        if event.type == "response.output_text.delta":
            received_text += event.delta
            print(event.delta, end="", flush=True)

    print(f"\n[Stream completed — stream_id: {stream_id}, cursor: {last_event_id}]")

    # Later — resume from the last cursor if the stream was interrupted
    if stream_id:
        result = await resume_agent_stream(
            stream_id,
            last_event_id,
            on_event=lambda event: (
                print(event.delta, end="", flush=True)
                if event.type == "response.output_text.delta"
                else None
            ),
        )
        print(f"\n[Resumed — output: {result.final_output[:60]}...]")


asyncio.run(main())
```
