# Session State Management for Extension Sidecars in Reasonix: Architecture and Implementation

> Discover how Reasonix manages session state for extension sidecars. Learn about the architecture using JSON-RPC for deterministic behavior and secure state control.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: architecture
- Published: 2026-08-07

---

**The Reasonix host maintains authoritative control over session state while out-of-process extension sidecars interact through a strict JSON-RPC protocol using session IDs, runtime generations, and optional policy slots to ensure deterministic behavior without direct state mutation.**

The DeepSeek-Reasonix architecture isolates extensions as out-of-process sidecars to guarantee memory safety and deterministic execution. This design places the burden of session state management for extension sidecars squarely on the host process, which exposes limited, well-defined hooks for sidecars to read data or influence session behavior through capability-based declarations. All interactions are validated against the machine-readable schema defined in [`internal/extension/protocol/schema.generated.json`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/extension/protocol/schema.generated.json).

## Host Authority and Sidecar Isolation

In Reasonix, the host process retains exclusive ownership of the **session state** while sidecars operate in isolated processes with no direct access to internal data structures. Sidecars communicate with the host exclusively through the extension protocol defined in [`docs/EXTENSION_PROTOCOL.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/EXTENSION_PROTOCOL.md), using JSON-RPC 2.0 over NDJSON. This boundary ensures that even if a sidecar crashes or executes malicious code, the session remains intact and uncorrupted.

## Session Identification and Runtime Generation

Every UI interaction between the host and sidecar carries a **session ID** and a **runtime generation** to prevent stale updates from corrupting the current interface. When a sidecar publishes UI payloads via `host/ui/publish` or receives requests via `host/ui/request`, these identifiers allow the host to discard any messages bearing outdated generation numbers.

The following Go implementation demonstrates how a sidecar publishes a UI notification that includes the current session identifier:

```go
func publishNotification(msg string) {
    notif := map[string]any{
        "session_id": mySessionID,
        "type":       "notification",
        "payload": map[string]any{
            "title":   "Info",
            "message": msg,
        },
    }
    rpc.Send("host/ui/publish", notif) // JSON-RPC 2.0 over NDJSON
}

```

## The Session Policy Slot

Extensions that need to influence session behavior—such as defining custom expiration, persistence rules, or metadata—can declare the `session_policy` capability. This replacement-strategy slot grants ownership to exactly one sidecar at a time, with the host validating declarations during the initialization handshake. Sidecars declare this capability in their manifest and register the slot during the `extension/initialize` request.

First, declare the capability in [`reasonix.toml`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/reasonix.toml):

```toml
[plugin."my-extension"]
runtime = "my-extension-sidecar"
capabilities = ["session_policy"]

```

Then, handle the initialization request in [`internal/extension/sidecar/main.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/extension/sidecar/main.go) to register the slot and cache the session ID:

```go
func handleInitialize(req jsonrpc.Request) (jsonrpc.Response, error) {
    var params struct {
        Manifest struct {
            SessionID string `json:"session_id"`
        } `json:"manifest"`
    }
    if err := json.Unmarshal(req.Params, &params); err != nil {
        return jsonrpc.ErrorResponse(req.ID, jsonrpc.InvalidParams, err)
    }
    // Cache the session ID for later UI calls
    mySessionID = params.Manifest.SessionID
    // Declare that we own the session_policy slot
    return jsonrpc.Response{
        ID: req.ID,
        Result: map[string]any{
            "declarations": []any{
                map[string]any{
                    "slot":   "session_policy",
                    "plugin": "my-extension",
                },
            },
        },
    }, nil
}

```

## Externalized Content and Memory Safety

Large session-related payloads, such as transcript logs or checkpoint data, are never passed directly to sidecars. Instead, the host externalizes these to its content store, and sidecars retrieve data via the `host/content/read` endpoint in 256 KiB chunks. This design preserves memory safety in the isolated process and allows the host to enforce strict size limits, as documented in [`docs/SESSION_REFERENCE_ARCHITECTURE.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/SESSION_REFERENCE_ARCHITECTURE.md).

The following pattern demonstrates reading a large session transcript in chunks:

```go
func readLargeTranscript(ref string) ([]byte, error) {
    // The host returns data in 256 KiB chunks; we reassemble them.
    var full []byte
    offset := 0
    for {
        chunk, err := rpc.Call("host/content/read", map[string]any{
            "ref":    ref,
            "offset": offset,
            "size":   256 * 1024,
        })
        if err != nil {
            return nil, err
        }
        data := chunk["data"].([]byte)
        full = append(full, data...)
        if len(data) < 256*1024 {
            break // reached EOF
        }
        offset += len(data)
    }
    return full, nil
}

```

## Session Lifecycle Management

The sidecar lifecycle begins when the host spawns the extension and sends the `extension/initialize` request. During this handshake, the sidecar receives the current **session ID** via the manifest and may optionally declare its ownership of the `session_policy` slot. The host manages termination through the `extension/shutdown` method or forcefully if the sidecar process crashes. In either case, pending RPCs are cancelled and the session store remains consistent, with metadata persisted according to the active policy.

## Summary

- The host maintains authoritative session state; sidecars run out-of-process and interact via JSON-RPC protocol defined in [`docs/EXTENSION_PROTOCOL.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/EXTENSION_PROTOCOL.md).
- Every UI payload includes a **session ID** and **runtime generation** to prevent stale updates from corrupting the interface.
- The `session_policy` slot allows exactly one sidecar at a time to influence session behavior through capability declaration in the manifest.
- Large payloads are externalized to the host content store and read in 256 KiB chunks via `host/content/read` to ensure memory safety.
- Session lifecycle is managed through explicit `extension/initialize` and `extension/shutdown` handshakes, with crash recovery handled by the host.

## Frequently Asked Questions

### What happens if a sidecar crashes during an active session?

If a sidecar process crashes, the host detects the failure and cancels any pending RPCs. Because the host retains authoritative control over the session store in [`docs/SESSION_REFERENCE_ARCHITECTURE.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/SESSION_REFERENCE_ARCHITECTURE.md), the session state remains consistent and the sidecar can be restarted without data corruption. The host may optionally respawn the sidecar and resume the session using the same session ID.

### Can multiple extensions own the session_policy slot simultaneously?

No. The protocol enforces that only one sidecar can own the `session_policy` replacement-strategy slot at any given time. The host validates declarations during the initialization handshake and rejects conflicting claims. If a new sidecar attempts to claim the slot while another owns it, the host applies the replacement rules defined in the extension protocol schema.

### How does Reasonix prevent stale UI updates from sidecars?

The host utilizes **runtime generation** counters alongside session IDs in every UI payload published via `host/ui/publish`. When processing updates, the host compares the generation number in the message against the current session generation and silently discards any messages carrying stale values, ensuring that only current sidecar results affect the UI state.

### What is the maximum payload size for session data passed directly to sidecars?

Reasonix does not pass large session data directly to sidecars. Instead, payloads are externalized to the host's content store, and sidecars must retrieve data using the `host/content/read` endpoint in fixed 256 KiB chunks. This chunking mechanism effectively allows the host to enforce size limits and prevents memory exhaustion in isolated sidecar processes.