SessionService lifecycle
All surfaces (CLI, REST, JSON-RPC, MCP Server) route through SessionService for the full
session lifecycle. The contract is identical regardless of transport.
CLI ----------+
REST ---------+
MCP Server ---+---> SessionService ---> AgentFactory::build_agent()
JSON-RPC -----+
Agent construction
Agent construction is fully centralized in AgentFactory::build_agent(). Surface crates
contain zero AgentBuilder::new() calls. The FactoryAgentBuilder bridges AgentFactory into
the SessionAgentBuilder trait used by EphemeralSessionService.
Surfaces that need per-request configuration (e.g. override_builtins, override_shell,
specific tools) stage a full AgentBuildConfig into the build_config_slot before
calling create_session():
// Surface code (CLI, REST, etc.)
let build_config = AgentBuildConfig::new(model)
.with_system_prompt(system_prompt)
.with_override_builtins(override_builtins)
.with_override_shell(override_shell);
factory_builder.stage_config(build_config).await;
let result = service.create_session(req).await?;
If no config is staged, FactoryAgentBuilder falls back to building a minimal config from the
CreateSessionRequest fields.
Session creation
create_session(req) builds an agent and runs the first turn.
- Always works in every build profile.
- Returns
RunResult with the session ID for subsequent turns.
- The session is live (in-memory) until archived.
- Supports
host_mode: process the prompt then stay alive for comms messages.
Turn execution
start_turn(id, req) runs a new turn on an existing session.
- Busy check: uses atomic compare-and-swap on the turn lock. Exactly one caller wins; others get
SESSION_BUSY.
- Sequential execution: turns run one at a time per session. No queueing — callers retry on Busy.
- Event streaming: if
event_tx is provided, events are forwarded during the turn.
- Host mode: when
host_mode: true, the agent processes the prompt then stays alive waiting for comms messages (peer-to-peer inter-agent communication).
Interruption
interrupt(id) cancels an in-flight turn.
- Checks
turn_lock atomically. If no turn is running, returns SESSION_NOT_RUNNING.
- Sends cancel signal to the session task’s agent.
- The interrupted turn returns
AgentError::Cancelled.
Reading session state
read(id) returns SessionView (state + billing separated).
- For live sessions: queries the session task for a snapshot.
- For persistent sessions (when
session-store is enabled): falls back to the store.
- Non-blocking — can run concurrently with a turn in progress.
Listing sessions
list(query) returns session summaries.
- Live sessions return real-time data from
SessionSummaryCache (updated after each turn via watch channel).
- Persistent mode merges live + stored sessions (deduplicated by ID).
- Pagination via
offset and limit.
Archiving
archive(id) removes a session from the live map and shuts down its task.
- In persistent mode, the snapshot remains in the store for historical queries.
- Archiving during an in-flight turn sends shutdown after the turn completes (graceful).
Compaction
Context compaction is an optional, non-fatal session capability.
Trigger rules
last_input_tokens >= threshold OR estimated_history_tokens >= threshold
- Never on the first turn (
current_turn == 0)
- Blocked if
current_turn - last_compaction_turn < min_turns_between_compactions
Compaction flow
Emit CompactionStarted
Signal that compaction is beginning.
Clone and prompt
Clone messages, append compaction prompt as User message.
Call LLM
Call LLM with empty tools and max_summary_tokens.
Handle failure
On failure: emit CompactionFailed, continue with uncompacted history (non-fatal).
Extract summary
Extract summary text from response.
Rebuild history
Call compactor.rebuild_history() to produce new messages + discarded list.
Replace messages
Replace session messages.
Record usage
Record compaction usage in budget.
Emit CompactionCompleted
Signal that compaction finished.
Rebuild algorithm
- Preserve
Message::System verbatim (extracted from messages, single source of truth)
- Inject summary as
Message::User with prefix
- Retain recent complete turns (User -> Assistant -> ToolResults sequences) per
recent_turn_budget
- Everything else goes to
discarded (for future memory indexing)
Budget
- Compaction draws from the same
Budget as regular turns
- If budget exhausted before compaction, compaction is skipped
max_summary_tokens caps the summary LLM response
Durability guarantees
Ephemeral mode
No durability. Process death loses all session state.
Persistent mode (session-store)
- Snapshot saved AFTER a turn completes (not mid-turn)
- Crash mid-turn loses the in-flight turn but preserves up to the last completed turn
- redb provides ACID transactions with WAL
Compaction durability
- Compacted session is saved as a regular snapshot
- Pre-compaction history is NOT recoverable (unless indexed into MemoryStore)
Data governance
No sensitive data filtering is applied in compaction summaries. The event store records all
AgentEvent variants including ToolCallRequested (which contains tool arguments). No
encryption at rest (redb files are plaintext).
.rkat/sessions/ files are derived projection output (materialized by SessionProjector), NOT
canonical state. Deleting them and replaying from the event store produces identical content.
See also