# Durable Session Channels (/docs/ai/session-channels)



<StoryFrame label="Session invariant" title="One identity. One ordered journal. Any number of reconnecting views." accent="worker">
  <StoryFact label="Authority">
    Live owner/member ACL on every operation
  </StoryFact>

  <StoryFact label="Continuity">
    Strict inclusive cursor over committed sequences
  </StoryFact>

  <StoryFact label="Recovery">
    Verified snapshots accelerate replay; never replace it
  </StoryFact>
</StoryFrame>

A durable agent session owns ordered input, output, controls, membership, retention,
and runtime requirements across individual turns. A conversation view is a
projection of that journal, not a second source of truth.

## Identity and access [#identity-and-access]

Session identity is scoped by tenant, pinned agent definition, and external id; the
API returns an opaque server id. Repeated creation and racing tabs converge on one
session. A bounded idempotency key makes each input or control append exactly once.

Knowing a same-tenant session id grants nothing. Every create, list, get, append,
subscribe, action, lifecycle command, token operation, and watch operation checks
the live owner/member ACL.

| Session role        | Capabilities                                                          |
| ------------------- | --------------------------------------------------------------------- |
| `owner` / `manager` | Read, append, controls, membership, lifecycle, scoped tokens, watches |
| `contributor`       | Read, subscribe, append, answer input, invoke declared actions        |
| `viewer`            | Read and subscribe only                                               |

Browser session tokens last at most 15 minutes and bind tenant, actor, environment,
session, token id, operations, and expiry. They cannot delegate token minting,
membership, lifecycle, legal-hold, or watch-management authority.

## One cursor contract [#one-cursor-contract]

Responses identify the last committed inclusive sequence. A subscriber requests
records strictly after that sequence with `Last-Event-ID` or `?after=`. Reconnect
backfills the durable journal before joining live delivery and removes boundary
duplicates. A future cursor conflicts; pruned retained history returns typed HTTP
410 `resync_required` metadata.

Closing a network stream never cancels accepted work. Use the explicit turn cancel
operation when cancellation is intended.

<ProcessRail label="Reconnect path" title="The journal closes every delivery gap">
  <ProcessStep label="01" title="Commit" tone="commit">
    Persist the event and assign its inclusive sequence.
  </ProcessStep>

  <ProcessStep label="02" title="Resume">
    Request records strictly after the last seen sequence.
  </ProcessStep>

  <ProcessStep label="03" title="Backfill" tone="gold">
    Read durable history before joining live delivery.
  </ProcessStep>

  <ProcessStep label="04" title="Follow" tone="worker">
    Remove boundary duplicates and continue from the live stream.
  </ProcessStep>
</ProcessRail>

Interrupted inference and tools recover differently:

* model work preserves committed partial deltas and follows the pinned `discard`,
  `continue`, or `repair` policy;
* an ambiguous in-flight tool call parks for reconciliation and is never blindly
  repeated;
* `text_reset` stream controls tell clients whether interrupted partial output was
  cleared, retained, or replaced.

## Ordered controls [#ordered-controls]

`POST /api/v1/chat/sessions/{session_id}/controls` appends one typed control:

* `queued_message` becomes the next normal turn;
* `steering_message` is considered only between completed model/tool steps;
* a declared `action` creates a new immutable history revision;
* `context_candidate` is internal-only and requires a declared processor or
  specialist source.

Controls never interrupt an in-flight side effect. Undo or regenerate changes only
the model-visible branch; it cannot claim to reverse an external action. Compensation
must be a separately authorized tool call with its own target and receipt.

## Verified fold snapshots [#verified-fold-snapshots]

Postgres remains authoritative. A background worker may store a bounded snapshot
with the session incarnation, event sequence, journal-prefix hash, fold/codec
versions, definition/policy/tool lineage, and state hash. Reads verify every binding,
then resume from `sequence + 1`.

Missing, stale, corrupt, oversized, wrong-lineage, or unsupported snapshots are
quarantined with content-free evidence and fall back to full journal replay. Redis
is not required for correctness.

## Runtime compatibility and upgrades [#runtime-compatibility-and-upgrades]

Every request sends `Ductor-Agent-Protocol-Version: 1`. Missing or unsupported
versions return HTTP 426 with supported versions. Active turns pin build,
deployment, compatibility set, journal-fold version, and client-protocol version;
an incompatible worker cannot claim them.

An owner or manager may upgrade a safe, unleased session:

```http
POST /api/v1/chat/sessions/{session_id}/upgrade
Ductor-Agent-Protocol-Version: 1
Content-Type: application/json

{"expected_sequence":42,"reason":"rolling deploy"}
```

The server selects the target runtime. Partial model output, running tools, pending
input, and reconciliation states must resolve first. The successful upgrade appends
an immutable `session.runtime_upgraded` fact; it never rewrites history.

## Retention and residency [#retention-and-residency]

Default content retention is 30 days and the content-free tombstone default is 90
days, bounded by tenant policy. Only closed terminal sessions without a live lease
or legal hold are swept. The deletion transaction writes a receipt and authorized
tombstone before removing content.

Admission separately pins home storage, allowed replicas, provider destinations,
artifact/log destinations, and failover policy. Moving compute never silently moves
journal data or provider egress.

## Related [#related]

<Cards>
  <Card title="Durable Chat Agent" href="/docs/ai/chat-agent">
    Canonical HTTP and SSE operations.
  </Card>

  <Card title="Agent Watches" href="/docs/ai/agent-watches">
    Explicitly approved monitoring attached to a session.
  </Card>
</Cards>
