This page is the canonical settled-state contract for session and runtime behavior.
Ownership model
Meerkat has one canonical semantic path:
- the runtime control plane (
MeerkatMachine in meerkat-runtime) owns keep_alive, Queue/Steer routing, comms drain lifecycle, external-event admission, and request/turn commit semantics; runtime-backed surfaces reach those semantics only by lowering into it
SessionService is the substrate lifecycle seam used by those surfaces
EphemeralSessionService / build_ephemeral_service remain valid for testing, embedded use, and WASM internals, but they are not the primary product path and do not own runtime semantics
The runtime-backed path is:
Surface -> MeerkatMachine -> SessionService -> AgentFactory::build_agent()
The runtime-backed build contract has an explicit binding seam:
- runtime-backed surfaces should call
MeerkatMachine::prepare_bindings(session_id)
- those bindings flow into
SessionBuildOptions.runtime_build_mode = RuntimeBuildMode::SessionOwned(bindings)
- standalone / embedded / test-only builds should opt into
RuntimeBuildMode::StandaloneEphemeral explicitly instead of relying on silent fallback
SessionRuntimeBindings is the epoch-local runtime handle for a session. It carries:
session_id
epoch_id
- the canonical
OpsLifecycleRegistry
- shared consumer cursor state used for recovery-safe completion visibility
This keeps one owner for runtime semantics:
PersistentRuntimeDriver::recover() owns input/runtime/control recovery
MeerkatMachine owns session entry recovery (ops_lifecycle, epoch_id, cursor state)
Direct substrate usage is intentionally narrower:
- Queue-only turns
- no runtime-owned
keep_alive
- no Steer/render-metadata semantics
- no runtime ingress/admission ownership
Agent construction contract
Agent construction is centralized in AgentFactory::build_agent().
- Surfaces pass per-request build data in-band via
CreateSessionRequest.build (SessionBuildOptions).
- No out-of-band staging lock is used.
FactoryAgentBuilder maps SessionBuildOptions to AgentBuildConfig.
- For runtime-backed builds,
SessionBuildOptions.runtime_build_mode should carry RuntimeBuildMode::SessionOwned(bindings) from prepare_bindings().
- For standalone/testing/embedded builds, prefer
RuntimeBuildMode::StandaloneEphemeral explicitly.
- Session metadata persists realm context and durable session identity:
realm_id
instance_id
backend
config_generation
- durable LLM identity
keep_alive
- visible comms identity metadata such as
comms_name and peer_meta
Session lifecycle and turn semantics
create_session
create_session(req) builds the agent and optionally runs the first turn.
- Returns
RunResult with session_id.
InitialTurnPolicy::RunImmediately executes the first turn inline (default).
InitialTurnPolicy::Defer registers the session without running a turn.
Create has a commit boundary:
- pre-commit failure: side-effect free; no committed session identity
- post-commit first-turn failure: return session identity and keep the session resumable
Committed create failure must not be silently rewritten to “cancelled” or cleaned up as unpublished work.
start_turn
start_turn(id, req) executes a new turn on an existing session.
- At most one in-flight turn per session.
- Concurrent attempts return
SESSION_BUSY.
- Committed success must not be rewritten to cancellation.
- Runtime-backed surfaces may hot-swap supported live settings on an existing session where the surface contract says that is allowed.
Deferred first-turn system prompt override
Rust supports system_prompt on StartTurnRequest only for a deferred session’s first turn.
- deferred + first turn: allowed
- existing history/messages: rejected
This keeps Rust aligned with the current runtime contract instead of implying arbitrary mid-session prompt replacement.
interrupt
interrupt(id) cancels an in-flight turn.
- If no turn is running:
SESSION_NOT_RUNNING.
read and list
read(id) and list(query) are non-blocking with respect to in-flight turns.
- Persistent services can include durable sessions from the realm backend.
- Presence in
list() is not the same as “live session exists”; runtime-owned seams must answer liveness.
read_history
read_history(id, query) returns the last committed transcript snapshot for a session.
- Messages are returned oldest-to-newest.
offset and limit apply from the start of the full transcript.
- Active sessions do not expose in-flight partial output through history reads.
- Archived sessions remain readable on persistent backends; the ephemeral service rejects archived history reads with
SESSION_PERSISTENCE_DISABLED.
archive
- The canonical
SessionDocumentMachine owns the session_lifecycle_terminal fact for ALL profiles; shells realize its verdict, they never decide it.
- Realization order is fail-closed: durable document commit first, runtime retire second.
RuntimeState::Retired is the runtime realization of the same verdict, so the resurrection window is unrepresentable.
- Archived sessions are excluded from
list() and rejected for further reads/turns (SESSION_NOT_FOUND on persistent services; the ephemeral service serves a final in-memory view).
- Committed transcript history remains readable via
read_history on persistent backends.
Keep-alive contract
keep_alive is a runtime/session concept, not an old “host mode” execution path.
Rules:
- create / run omitted => default
false
- continue / resume omitted => inherit persisted session intent
- explicit
keep_alive override is a session/runtime mutation once validated
- invalid
keep_alive + comms configuration is rejected before any stateful work
- once validated, an explicit
keep_alive mutation may commit independently of turn success
keep_alive=true requires usable comms identity/config on the surfaces that expose it.
External events
External events are queue-only runtime-backed inputs.
- they are admitted into runtime ingress
- they do not invent a second direct execution path
- “turn-boundary inbox draining” is not the primary mental model for the current runtime-backed design
Explicit override semantics
Where the surface supports omission vs explicit override, these are distinct facts:
- omit / inherit
- disable / false
- set / concrete value
When all three meanings matter, the wire/API must preserve that distinction. Typed optional fields and override masks are preferred over default-value folklore. Concretely: SystemPromptOverride (Inherit / Set / Disable) is the canonical wire+persisted carrier for the per-request system-prompt fact, and per-turn provider parameters travel as the typed ProviderParamsOverride, not a raw JSON bag.
Realm contract
Sessions are realm-scoped.
- Same
realm_id: shared visibility and config context.
- Different
realm_id: strict isolation.
- Backend is pinned per realm via
realm_manifest.json.
Compaction contract
Compaction is optional and non-fatal.
- Triggered by token thresholds and turn guards.
- On failure, emits
CompactionFailed and continues with uncompacted history.
- Compaction usage counts toward run budgets.
Durability
Ephemeral mode
No durability across process restart.
Persistent mode
Durability follows the active persistent realm backend (sqlite or jsonl).
The memory backend is explicit ephemeral storage and does not survive process
restart.
- Completed turns are persisted.
- Crash during in-flight turn may lose only that turn.
- SQLite-backed realms are the default persistent mode and support normal same-realm multi-process workflows.
Persistent session metadata is the source of truth for resumed durable session behavior unless an interactive surface supplies an explicit typed override.
Config concurrency contract
Config runtime uses generation CAS.
config/get returns current generation.
config/set and config/patch can specify expected_generation.
- Mismatches fail deterministically with generation conflict.
Data governance
No automatic sensitive-data redaction is applied to session content or tool payloads.
Encrypt storage at rest externally if required by your environment.
See also