> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rkat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Capability matrix

> Build profiles, feature gates, error codes, and capability behavior across Meerkat configurations.

Meerkat is modular. The `meerkat` facade defaults to the three provider
features (`anthropic`, `openai`, `gemini`) plus the shared `copilot` backend;
realm persistence, compaction,
comms, MCP, skills, semantic memory, ATIF facade export, and live-channel
orchestration are compile-time options. Schedule, WorkGraph, and durable-job
substrates are always linked by the facade. Their runtime services and tools
still require host composition and policy activation; the empty `schedule`
and `workgraph` Cargo features are compatibility aliases, not selectors.
These matrices show what works in common profiles.

This page contains several intentionally separate matrices. Build profiles
describe facade compile-time composition. The session-operation matrix covers
only session lifecycle behavior. Generic `CapabilityId` entries describe
tool-family policy, while `runtime/capabilities` describes one host process.
None of those projections is a complete list of every API method or runtime
authority.

## Build profiles

| Profile                      | Cargo Features                     | Use Case                                                                                                                                                          |
| ---------------------------- | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Minimal**                  | `--no-default-features`            | Ephemeral agent core for a host that supplies its own provider path; no persistence or compaction                                                                 |
| **Provider default**         | default features                   | Anthropic + OpenAI + Gemini providers with the shared Copilot backend; no session persistence, compaction, live transport, comms, MCP, skills, or semantic memory |
| **Persistent SQLite**        | `session-store`                    | Persistent session service with the built-in SQLite backend; the feature also compiles the explicitly ephemeral `memory` backend                                  |
| **Persistent JSONL**         | `session-store,jsonl-store`        | Persistent session service with the separately enabled JSONL backend                                                                                              |
| **Compacting**               | `session-compaction`               | Auto-compact long conversations                                                                                                                                   |
| **Persistent + compacting**  | `session-store,session-compaction` | Persistent session state plus automatic compaction                                                                                                                |
| **Live host core**           | `openai-realtime,live`             | Live orchestration for realtime-capable sessions; the host must still configure a transport                                                                       |
| **WebRTC-capable live host** | `openai-realtime,live-webrtc`      | Compiles WebRTC support; `rkat-rpc` must also start with `--live-webrtc`                                                                                          |

Durable jobs do not have a facade feature switch of their own. The job types
and services are compiled into the facade, but durable detached execution needs
a persistent realm job store, persistent blob store, runtime delivery inbox,
and realm-bound delivery projector. Memory-only and standalone builds fail
detached shell submission closed instead of falling back to a volatile process.

## Capability behavior

This table is scoped to session lifecycle operations. It does not describe
jobs, approvals, mobs, live transports, or every host surface.

| Operation                 | Minimal                                         | Persistent                                  | Compacting                                      | Persistent + compacting                     |
| ------------------------- | ----------------------------------------------- | ------------------------------------------- | ----------------------------------------------- | ------------------------------------------- |
| `create_session`          | Works                                           | Works                                       | Works                                           | Works                                       |
| `start_turn`              | Works                                           | Works + exact physical successor committed  | Works                                           | Works + exact physical successor committed  |
| `interrupt`               | Works                                           | Works                                       | Works                                           | Works                                       |
| `read` (running session)  | Works                                           | Works                                       | Works                                           | Works                                       |
| `read` (archived)         | Final in-memory view                            | `SESSION_NOT_FOUND`                         | Final in-memory view                            | `SESSION_NOT_FOUND`                         |
| `read_history` (archived) | `SESSION_PERSISTENCE_DISABLED`                  | Works (committed transcript)                | `SESSION_PERSISTENCE_DISABLED`                  | Works (committed transcript)                |
| `list`                    | Live sessions only (archived hidden)            | Live + stored sessions (archived hidden)    | Live sessions only (archived hidden)            | Live + stored sessions (archived hidden)    |
| `archive`                 | Retires live handle, keeps final in-memory view | Durable archive commit, then runtime retire | Retires live handle, keeps final in-memory view | Durable archive commit, then runtime retire |
| Auto-compaction           | No-op                                           | No-op                                       | Triggers at threshold                           | Triggers at threshold                       |

Archive lifecycle truth is owned by the canonical `SessionDocumentMachine` for all
profiles: the durable archived verdict commits first and the runtime handle is
retired second, so a half-archived state is unrepresentable.

## Error codes

Two layers matter here:

* **Session transport mapping**: how `SessionError` is projected by session-facing transports
* **Canonical wire `ErrorCode` mapping**: the higher-level `meerkat-contracts` envelope model used across public protocol surfaces

The table below is the current **session transport** mapping (`SessionError::code()` is the stable string code; JSON-RPC numbers come from `session_error_to_rpc`):

| `SessionError`               | Code                                     | JSON-RPC             | MCP        | CLI                                  |
| ---------------------------- | ---------------------------------------- | -------------------- | ---------- | ------------------------------------ |
| `NotFound`                   | `SESSION_NOT_FOUND`                      | -32001               | tool error | exit 1                               |
| `Busy`                       | `SESSION_BUSY`                           | -32002               | tool error | exit 1                               |
| `PersistenceDisabled`        | `SESSION_PERSISTENCE_DISABLED`           | -32603               | tool error | exit 1                               |
| `CompactionDisabled`         | `SESSION_COMPACTION_DISABLED`            | -32603               | tool error | exit 1                               |
| `NotRunning`                 | `SESSION_NOT_RUNNING`                    | -32603               | tool error | exit 1                               |
| `DurableTailHeldForRecovery` | `SESSION_DURABLE_TAIL_HELD_FOR_RECOVERY` | -32003               | tool error | exit 1                               |
| `DurableTailRecoveryRefused` | `SESSION_DURABLE_TAIL_RECOVERY_REFUSED`  | -32003               | tool error | exit 1                               |
| `DurableEvidenceQuarantined` | `SESSION_DURABLE_EVIDENCE_QUARANTINED`   | -32003               | tool error | exit 1                               |
| `Store`                      | `SESSION_STORE_ERROR`                    | -32603               | tool error | exit 1                               |
| `Unsupported`                | `SESSION_UNSUPPORTED`                    | -32603               | tool error | exit 1                               |
| `Agent`                      | `AGENT_ERROR`                            | by cause (see below) | tool error | exit 1 (exit 2 on budget exhaustion) |
| `FailedWithData`             | `SESSION_ERROR`                          | -32603               | tool error | exit 1                               |

`Agent` errors map per typed cause to the canonical contracts codes: provider
failures -32010, budget exhaustion -32011, hook denial -32012, cancellation
-32005, config errors -32602, everything else -32603.

Temporary councils are agent-internal mob orchestration, not a public wire
family. The `council` agent tool returns structured results through the ordinary
tool-result channel. Cleanup debt is deliberately not a tool failure: a sealed
result whose cleanup retained debt carries `cleanup.status = "debt"` or
`"pending"`. Seating failures, wiring gaps, exhausted budgets, and elapsed
deadlines remain typed `exit_reason` values on that result.

The durable resume holds are not service faults: the session exists, its
content is retained intact, and no runnable authority is handed out until
machine-authorized recovery or reconciliation clears it. They classify as
"durably present, not runnable" — the canonical `SessionNotRunning` wire code
(JSON-RPC -32003, HTTP 409) — and carry a typed `durable_resume_hold`
structured payload (`tail_held_for_recovery` / `recovery_refused` /
`evidence_quarantined`, plus `session_error_code`, `session_id`, and
`content_retained: true`) so callers distinguish the hold class without
parsing messages.

### Transport error mapping summary

* **JSON-RPC**: `NotFound`/`Busy` keep dedicated codes (-32001/-32002), durable resume holds use `SessionNotRunning` (-32003), and typed agent causes use the canonical `meerkat_contracts::ErrorCode::jsonrpc_code()` values. Remaining session variants collapse to -32603 with the message preserved.
* **REST**: Endpoint-typed `ApiError` mapping (`NotFound` -> 404; `Busy` -> 400 on create/turn, 409 on interrupt conflicts; archived/unavailable history -> 400; agent/internal -> 500). Where canonical wire errors are emitted, `ErrorCode::http_status()` decides the status. Error body includes `{ "code": "...", "message": "..." }`.
* **MCP Server**: Tool calls return `is_error: true` with the error message and code in the content.
* **CLI**: General commands use 0 for success, 1 for error, and 2 for graceful budget exhaustion. Multi-host mob commands additionally preserve the typed operator failures: 45 scope denied, 46 host unavailable, 47 stale cursor, and 48 stale fence. Error details are printed to stderr.

## Mob control scopes

Plane-(b) operator access to a mob is scope-gated (multi-host mobs §8) with an
owner-implicit-full, default-deny posture:

| Principal                                                                              | Effective scopes                                    |
| -------------------------------------------------------------------------------------- | --------------------------------------------------- |
| Owner (v1 local RPC/REST/stdio/public-MCP consoles, the CLI, the owner bridge session) | Implicit full — every scope, no grant record needed |
| Granted principal                                                                      | Exactly its granted, unexpired `ControlScope` set   |
| Unknown / unresolvable principal                                                       | None (default-deny)                                 |

Grants are recorded per principal with an optional raw `expires_at_ms`;
expiry is evaluated once per decision at the enforcement seam (`expired ⟺
expires_at_ms <= now_ms`, never cached, never a machine fact). Grant
administration requires `AdminGrants`; `AdminHost` covers host bind/revoke
only — the two are distinct. Denials are typed end to end:
`MobError::ScopeDenied` carries `{ required, presented }`, projected as
`ErrorCode::ScopeDenied` (JSON-RPC `-32025`, HTTP 403, CLI exit 45) with a
`WireScopeDeniedDetail` details payload; `presented` is always the caller's
own effective set. Member upcalls and member-context tool surfaces ride the
separate agent authority lane (`MobToolAuthorityContext`): grants never gate
upcalls, and upcalls never satisfy scope checks. Revocation and expiry take
effect at admission time — already-open event streams are not torn down in
v1 (recorded v2 hardening).

## Pick only what you need

```toml theme={null}
# Minimal: just the agent loop
[dependencies]
meerkat = { version = "=0.8.32", default-features = false, features = ["anthropic"] }

# Add persistence
meerkat = { version = "=0.8.32", default-features = false, features = ["anthropic", "session-store"] }

# Add compaction
meerkat = { version = "=0.8.32", default-features = false, features = ["anthropic", "session-store", "session-compaction"] }

# Add facade-local predicate-schedule helper re-exports. The schedule service
# and WorkGraph substrate are already linked.
meerkat = { version = "=0.8.32", default-features = false, features = ["anthropic", "session-store", "schedule"] }

# Broad native facade with WebSocket live support
meerkat = { version = "=0.8.32", features = [
  "all-providers", "openai-realtime", "session-store", "session-compaction",
  "memory-store-session", "comms", "mcp", "skills", "schedule", "live",
  "atif", # 0.8.23+
] }

# WebRTC is separate and still needs `rkat-rpc --live-webrtc` at runtime
meerkat = { version = "=0.8.32", features = ["openai-realtime", "live-webrtc"] }
```

## Runtime capabilities

Meerkat exposes two related capability projections:

* `GET /capabilities` and JSON-RPC `capabilities/get` return the registered
  `CapabilityStatus` inventory. A status distinguishes available,
  policy-disabled, and not-compiled behavior.
* `GET /runtime/capabilities` and JSON-RPC `runtime/capabilities` return
  `RuntimeHostCapabilities`, including explicit boolean facts about the
  current host and its registered endpoints.

Do not infer one layer from the other. A generic capability token describes a
feature or policy family; a host feature flag answers whether this concrete
runtime surface can serve the corresponding host protocol.

### Capability inventory

The complete `CapabilityId` vocabulary is:

| Capability           | Registration or policy gate                    | Meaning                                                                      |
| -------------------- | ---------------------------------------------- | ---------------------------------------------------------------------------- |
| `sessions`           | Core, always registered                        | Agent loop and session lifecycle                                             |
| `streaming`          | Core, always registered                        | Agent event streaming                                                        |
| `structured_output`  | Core, always registered                        | Schema-validated output extraction                                           |
| `hooks`              | Registered when the hooks crate is linked      | Hook execution; no separate config gate                                      |
| `builtins`           | Tools crate plus `tools.builtins_enabled`      | Builtin task, file, image, and utility tools                                 |
| `shell`              | Native tools crate plus `tools.shell_enabled`  | Shell and shell-job tools; absent on WASM                                    |
| `comms`              | Compiled comms capability                      | Inter-agent messaging and keep-alive support                                 |
| `memory_store`       | Compiled `memory-store` support                | Semantic memory retrieval over compacted history                             |
| `schedule`           | Schedule crate plus `tools.schedule_enabled`   | Durable schedules and occurrence delivery                                    |
| `work_graph`         | WorkGraph crate plus `tools.workgraph_enabled` | Realm-scoped dependency-aware work                                           |
| `session_store`      | Native `session-store` feature                 | Persistent sessions and event projection                                     |
| `session_compaction` | Native `session-compaction` feature            | Automatic transcript compaction                                              |
| `skills`             | Compiled skills support plus `skills.enabled`  | Skill discovery, resolution, and injection                                   |
| `mcp_live`           | Compiled `mcp` support                         | Add, remove, or reload MCP servers on a live session                         |
| `adaptive_flow`      | Known mobpack requirement token                | Adaptive FlowMaster execution; not an automatically registered inventory row |

WorkGraph's companion skill uses the same canonical `work_graph` token as the
public inventory. Adaptive mobpacks stamp `adaptive_flow` into their
requirements so a host that does not know or satisfy the requirement fails
closed instead of silently running a downgraded flow.

Optional capability bundles are declared by the owning feature crate.
`meerkat-capabilities` supplies the typed vocabulary and collection seam;
contracts and facades may project those declarations into `CapabilityStatus`,
but they must not invent feature policy or translate aliases. For example,
`meerkat-schedule` owns Schedule policy, `meerkat-workgraph` owns WorkGraph
policy and registration, and `meerkat-skills` owns Skills policy.

### Runtime host feature flags

`RuntimeHostFeatureFlags` contains these exact boolean fields:

| Flag                      | Host fact                                                                                                              |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `runtime_backed_sessions` | Session operations lower through the runtime control plane                                                             |
| `mobs`                    | Mob runtime methods are available                                                                                      |
| `mcp_live`                | Live MCP mutation methods are available                                                                                |
| `comms`                   | Host comms operations are available                                                                                    |
| `blobs`                   | Realm blob retrieval is available                                                                                      |
| `session_events`          | Session event subscription is available                                                                                |
| `session_streams`         | Session output stream subscription is available                                                                        |
| `schedules`               | Schedule methods are available                                                                                         |
| `skills`                  | Skill inspection is available                                                                                          |
| `event_replay`            | Durable session-event replay is available                                                                              |
| `artifacts`               | Stable artifact record and download methods are available                                                              |
| `approvals`               | Persistent RPC-host approval records are available                                                                     |
| `external_members`        | External mob-member hosting is available                                                                               |
| `secure_remote_rpc`       | This host advertises the secure remote-RPC contract                                                                    |
| `multi_host_mobs`         | Host binding and remote member materialization are available                                                           |
| `durable_jobs`            | The job and monitor protocol family is wired; each detached submission still checks durable realm storage and delivery |

Older payloads that predate `multi_host_mobs` or `durable_jobs` decode those
fields as `false`. Clients must check the explicit flag instead of guessing
from `mobs`, the server version, or the presence of shared schema components.

The approval flag covers request, decision, status, and audit truth. It does
not claim an automatic in-loop tool gate. A named built-in memory realm still
has a `store_path`, so RPC persists its approval sidecar and may report
`approvals=true`; only an ad hoc bundle without a store path falls back to a
process-local service and reports the flag as false.

Likewise, `durable_jobs=true` reports host protocol wiring, not permission to
silently run a volatile detached process. Background shell needs a persistent
realm job store, blob store, runtime delivery inbox, and delivery projector or
fails closed.

### Public surface coverage

This table describes dedicated public protocol or command families. It does
not treat an agent's ability to call a model-visible tool as an operator API.

| Domain                            | CLI                                              | REST                    | JSON-RPC                                                                     | MCP server                            |
| --------------------------------- | ------------------------------------------------ | ----------------------- | ---------------------------------------------------------------------------- | ------------------------------------- |
| Session lifecycle and history     | Yes                                              | Yes                     | Yes                                                                          | Yes                                   |
| Live session events               | Stream output                                    | SSE                     | Subscriptions                                                                | Open/read/close stream tools          |
| Durable event replay              | ATIF export                                      | No                      | `events/latest_cursor`, `events/list_since`, `events/snapshot`               | No                                    |
| Blob fetch                        | `blob get`                                       | No                      | `blob/get`                                                                   | `meerkat_blob_get`                    |
| Stable artifact records/downloads | No                                               | No                      | `artifact/*`                                                                 | No                                    |
| Approval records                  | No                                               | No                      | `approval/*`                                                                 | No                                    |
| Durable jobs and monitors         | No dedicated command                             | No                      | `jobs/*`, `monitors/start`                                                   | No                                    |
| Schedules                         | Agent tools only                                 | Full CRUD and tool call | Full CRUD and tool call                                                      | Schedule tools                        |
| WorkGraph                         | Observation plus trusted goal/attention controls | Observation             | Observation                                                                  | Agent mutation tools plus observation |
| Live audio                        | Placed mob-member WebSocket controls             | No                      | Session `live/*` supports WebSocket/WebRTC; member control is WebSocket-only | No                                    |
| Multi-host mobs                   | Admin and observation                            | Observation only        | Admin and observation                                                        | Placement plus observation only       |
| Temporary councils                | No                                               | No                      | No                                                                           | No                                    |
| Provider credential management    | Yes                                              | Yes                     | Yes                                                                          | No                                    |

Shared wire types do not grant exposure. In particular, durable member role
migration is absent from every public CLI, REST, JSON-RPC, MCP, and generated
SDK spawn request even though the private remote-host bridge shares schema
types with public artifacts.

Temporary councils are exposed only as the `council` tool in
`AgentMobToolSurface`, beside `delegate` and `fork_off`. Its authority is the
calling agent's machine-minted mob-tool context: it must be allowed to create
the temporary mob and manage every source mob it names. No CLI, REST, JSON-RPC,
public MCP, or generated SDK council verb exists.

### Live transport gates

Realtime model capability, live orchestration, and transport activation are
separate facts:

| Gate               | Requirement                                                                                            |
| ------------------ | ------------------------------------------------------------------------------------------------------ |
| Realtime model     | The selected `ModelProfile.realtime` is `true`                                                         |
| Live orchestration | The host is built with `live` and the appropriate realtime provider support, such as `openai-realtime` |
| Direct WebSocket   | `rkat-rpc` starts with `--live-ws <addr>`                                                              |
| WebRTC             | `rkat-rpc` is built with `live-webrtc` and starts with `--live-webrtc`                                 |

The WebRTC build feature alone does not enable signaling. The `live/*` family
is advertised only when at least one live transport is active, and
`live/webrtc/answer` is advertised only when the WebRTC runtime gate is active.

### Event delivery and durability

Live session subscriptions use a bounded 256-entry best-effort broadcast. A
consumer that falls behind receives `stream_truncated` with
`reason.kind: "stream_lagged"` and an exact `reason.dropped` count, then
continues from the retained tail. Built-in realm-backed persistent sessions
separately feed event envelopes into a dedicated unbounded projector queue and
append them asynchronously to a durable audit/replay log. Custom persistent
hosts receive this surface only when they install the optional event projection seam. The
projector warns when its backlog reaches 1,024. A
durable append fault latches a typed halt and refuses to append later sequence
numbers past the hole, but does not undo the committed turn. A slow UI
subscriber therefore does not itself truncate audit projection input.
Reconcile live gaps through available durable replay, a snapshot, or ATIF
export rather than treating the live tail as complete evidence. SessionStore
and RuntimeStore remain session authority; `.rkat/sessions/` views and ATIF
output are derived projections.

## Model capabilities

`ModelProfile` describes per-model capabilities used for feature gating and tool visibility. Two fields are relevant to multimodal content support:

| Capability            | Description                                                                                                   | Anthropic | OpenAI                                                           | Gemini                 |
| --------------------- | ------------------------------------------------------------------------------------------------------------- | --------- | ---------------------------------------------------------------- | ---------------------- |
| `vision`              | Model can process image content in user messages                                                              | Yes       | Yes                                                              | Yes                    |
| `image_tool_results`  | Model can process image content in tool results                                                               | Yes       | Yes for current chat and Responses rows; no for `gpt-realtime-2` | Yes                    |
| `inline_video`        | Model can process video content in user messages, including inline bytes and provider-readable URI references | No        | No                                                               | Yes                    |
| `realtime`            | Model can back a caller-initiated live channel                                                                | No        | `gpt-realtime-2` only                                            | No current catalog row |
| `supports_web_search` | Model supports provider-native web search tools                                                               | Yes       | Yes for chat catalog rows; not `gpt-realtime-2` / Codex          | Yes                    |

These fields are set in `meerkat-models/src/capabilities/**` (the types live in `meerkat-core/src/model_profile/**`) and exposed via `ModelProfile`. They control:

* **`view_image` tool visibility** -- hidden via the capability-base `ToolFilter` (`capability_base_filter_for_image_tool_results`) when `image_tool_results` is `false`.
* **`ContentBlock::Image` in tool results** -- providers that do not support `image_tool_results` will receive image content blocks converted to text descriptions.
* **`ContentBlock::Image` in user messages** -- providers that do not support `vision` will not receive image content blocks in user messages.
* **Still images on live channels** -- a realtime model's `vision` capability projects to `LiveOpenResult.capabilities.image_in`; clients must check that channel capability before sending an image chunk.
* **Provider-native web search** -- injected by default when `supports_web_search` is `true` and the corresponding `provider_tools.*` config is enabled.

## System-message projection

The durable Session transcript is the authority for instructions. It retains
every `System` and `SystemNotice` message at its exact authored position; an
adapter projection never rewrites that transcript.

| Provider wire                      | Provider-side representation       | Relative position    |
| ---------------------------------- | ---------------------------------- | -------------------- |
| Standard OpenAI Responses          | Ordinary ordered input item        | Preserved            |
| OpenAI-compatible Chat Completions | Ordinary ordered message           | Preserved            |
| OpenAI Realtime                    | Ordinary ordered conversation item | Preserved            |
| Anthropic Messages                 | Distinct top-level system blocks   | Leading prefix only  |
| Gemini `generateContent`           | Distinct `systemInstruction.parts` | Leading prefix only  |
| Private ChatGPT Responses          | Singular instruction string        | One leading row only |

Provider limitations never redefine Session validity. Meerkat authors every
System row as ordinary ordered history. A limited provider request projection
returns a typed, non-retryable error when the resulting transcript is
unrepresentable; rebinding to an exact provider can execute the same Session
unchanged. Adapters never hoist, merge, or delimiter-join distinct canonical
System rows.

Realtime reconstruction applies the same ordered replay-window policy to every
role and replays each retained System row as an in-conversation System item.
`runtime_system_context` remains retired, and the provider's separate session
instructions field is not a shadow transcript authority. `SystemNotice`
remains an explicit in-place history event.

Image generation uses provider-owned image profiles rather than `ModelProfile` chat-model flags. OpenAI and Gemini provider crates advertise image targets, supported backend plans, dimensions/aspect ratios, and provider-specific `provider_params`; the runtime exposes one model-facing `generate_image` tool and commits generated outputs as blob-backed assistant image blocks.

Gemini video URI support follows the active Google backend. Vertex Gemini accepts `gs://` references directly. Gemini API sessions pass public or already-registered file URIs as `fileData`, and register `gs://` references through the Files API only when Google bearer auth is available; API-key-only sessions should use public/pre-registered file URIs or Vertex for direct GCS references.

## Provider-native web search

Web search is enabled by default for all catalog models with `supports_web_search: true`. The factory resolves provider-specific request defaults at build time as a typed, never-persisted `ProviderTag` (the legacy-named `tool_defaults` half of `ProviderParamsCarrier`). These defaults include provider-native tool bodies and model-capability-derived cache policy. Effective per-turn params are a typed field-wise merge: explicit `provider_params` win and build defaults fill only the unset provider-native slots -- the old RFC 7396 raw-JSON merge-patch is retired.

| Provider    | Tool type             | Config key                                  | Default |
| ----------- | --------------------- | ------------------------------------------- | ------- |
| Anthropic   | `web_search_20250305` | `provider_tools.anthropic.web_search`       | `true`  |
| OpenAI      | `web_search`          | `provider_tools.openai.web_search`          | `true`  |
| Gemini      | `google_search`       | `provider_tools.gemini.google_search`       | `true`  |
| Self-hosted | Not wired (v1)        | `SelfHostedModelConfig.supports_web_search` | `false` |

**Opt-out:**

* Config-level: set the matching config key from the table above to `false`
* CLI: `rkat run --no-web-search "..."`.
* Per-request Anthropic/OpenAI: `provider_params: {"provider_tag": {"provider": "anthropic", "web_search": false}}` (or `"provider": "open_ai"`)
* Per-request Gemini: `provider_params: {"provider_tag": {"provider": "gemini", "google_search": false}}`
* An explicit non-object value (`false` or `null`) in the typed search slot blocks the build-derived default and no search tool is emitted

**Resume behavior:** Tool defaults are re-derived on every build (including resume) from current config and model profile. Config changes take effect immediately on resumed sessions. Explicit `provider_params` overrides are persisted in `SessionMetadata`.

**Extraction turns:** Web search tools are stripped during structured output extraction to maintain the deterministic, tool-free invariant.

**Hook interaction:** Pre-LLM hooks see the merged request defaults in `HookLlmRequest.provider_params` as an observational projection. Hooks cannot rewrite effective provider params.

## MCP server loading

MCP server connections are **non-blocking across all surfaces**. Servers connect in parallel in the background after `apply_staged()`. The agent loop polls `poll_external_updates()` at each `CallingLlm` boundary and injects a `[MCP_PENDING]` system notice while servers are still connecting. Tools become visible as each server completes its handshake.

* Per-server timeout: `connect_timeout_secs` in MCP config (default: 10s)
* CLI: `--wait-for-mcp` blocks before the first turn until all servers finish connecting
* SDK: `McpRouterAdapter::wait_until_ready(timeout)` provides the same blocking behavior programmatically

## Compaction behavior

When `session-compaction` is enabled:

* **Trigger**: `last_input_tokens >= threshold` OR `estimated_history_tokens >= threshold`
  OR the measured request budget for the boundary (hydrated messages + exact
  visible tool definitions + effective output reserve) crosses the threshold on
  its input side
* **Guards**: Never on first turn. Ordinary compactions wait at least 3 turns;
  a live history-token or exact provider-byte capacity crossing bypasses that
  cost guard so an already-oversized request can recover immediately. A
  request-budget crossing does not bypass it: tool definitions and the output
  reserve survive compaction, so a crossing they cause is a cost decision, not a
  recovery one.
* **Failure**: Non-fatal. `CompactionFailed` event emitted, agent continues with uncompacted history.
* **Budget**: Compaction LLM call draws from the same token budget as regular turns.

Events emitted: `CompactionStarted`, `CompactionCompleted`, `CompactionFailed`.

## Concurrency

* At most one turn runs per session at a time.
* Second `start_turn` while one is in-flight returns `SESSION_BUSY`.
* `interrupt()` cancels the in-flight turn.
* `read()` and `list()` are non-blocking.
* No queueing: callers retry on `Busy`.

## Durability

* **Ephemeral**: Process death loses all state.
* **Persistent**: A successful turn commits an exact store-issued physical successor according to the pinned realm backend. The serialized `Session` remains domain state, not an embedded checkpoint or proof of currentness.
* Intra-turn persistence may write provisional physical successors. Each write returns an exact receipt, and the final runtime boundary promotes the latest receipt without reserializing or reapplying the turn.
* `WholeBlob` currentness binds the session ID, store revision, and blob digest. `HeadCanonical` currentness binds the session ID, store revision, boundary head, and committed head token.
* Backends are pinned per realm (`realm_manifest.json`). Surfaces share state only when both `realm_id` and the resolved physical provider/root match.
* SQLite-backed realms are the standard same-realm multi-process mode.
* Every composed store slot carries a machine-readable durability class (`durable` / `rebuildable_cache` / `scratch`). An undeclared non-persistent durable slot needs a manifest `ephemeral_domains` declaration or startup fails typed. A provider can instead return typed `DeclaredEphemeral` directly; neither path permits silent in-memory fallback.

## See also

* [Session contracts](/reference/session-contracts) -- concurrency, durability, and compaction semantics
* [Architecture](/reference/architecture) -- crate structure and agent loop details
