Struct AcpSessionStore
pub struct AcpSessionStore { /* private fields */ }Implementations§
Source§impl AcpSessionStore
impl AcpSessionStore
pub fn new(workspace_dir: &Path) -> Result<Self>
Sourcepub fn create_session(
&self,
session_uuid: &str,
agent_alias: &str,
workspace_dir: &str,
principal_id: Option<&str>,
) -> Result<i64>
pub fn create_session( &self, session_uuid: &str, agent_alias: &str, workspace_dir: &str, principal_id: Option<&str>, ) -> Result<i64>
Record a new session stamped with its owning principal (RFC 7141
session isolation). principal_id is the caller’s scope, or None for
an unscoped/admin connection (NULL owner => visible only to unscoped
connections). Returns the integer id assigned by SQLite.
Sourcepub fn create_session_with_interaction_surface(
&self,
session_uuid: &str,
agent_alias: &str,
workspace_dir: &str,
interaction_surface: Option<&str>,
principal_id: Option<&str>,
) -> Result<i64>
pub fn create_session_with_interaction_surface( &self, session_uuid: &str, agent_alias: &str, workspace_dir: &str, interaction_surface: Option<&str>, principal_id: Option<&str>, ) -> Result<i64>
Record a session with an optional host-validated interaction surface, stamped with its owning principal (RFC 7141).
Sourcepub fn delete_session_owned(
&self,
session_uuid: &str,
owner_principal_id: &str,
) -> Result<bool>
pub fn delete_session_owned( &self, session_uuid: &str, owner_principal_id: &str, ) -> Result<bool>
Delete a session ONLY if owner_principal_id matches the stored
owner, in one predicated statement (RFC 7141 atomic ownership).
Child-row cleanup follows the same cascade as Self::delete_session.
Sourcepub fn session_principal(
&self,
session_uuid: &str,
) -> Result<Option<Option<String>>>
pub fn session_principal( &self, session_uuid: &str, ) -> Result<Option<Option<String>>>
The owning principal of a session, for authorization on mutation paths
(RFC 7141 F2) without hydrating its full message history. Returns
Ok(None) when the session does not exist, Ok(Some(None)) for a
NULL-owner (unscoped/legacy) row, and Ok(Some(Some(id))) when owned.
Sourcepub fn session_owner_and_surface(
&self,
session_uuid: &str,
) -> Result<Option<(Option<String>, Option<String>)>>
pub fn session_owner_and_surface( &self, session_uuid: &str, ) -> Result<Option<(Option<String>, Option<String>)>>
Read the owner and interaction surface needed before checkpoint recovery without hydrating a potentially large transcript.
Sourcepub fn bind_interaction_surface_if_unset(
&self,
session_uuid: &str,
interaction_surface: &str,
) -> Result<String>
pub fn bind_interaction_surface_if_unset( &self, session_uuid: &str, interaction_surface: &str, ) -> Result<String>
Bind an unlabelled legacy session to a validated surface exactly once. Returns the durable value after the update so the caller can reject a concurrent or pre-existing mismatch.
Sourcepub fn load_session(&self, session_uuid: &str) -> Result<Option<AcpSessionData>>
pub fn load_session(&self, session_uuid: &str) -> Result<Option<AcpSessionData>>
Load session metadata and full message history for restore.
Returns None if the session_uuid is not found.
Sourcepub fn load_message_page(
&self,
session_uuid: &str,
limit: usize,
cursor: Option<&str>,
) -> Result<AcpSessionPage>
pub fn load_message_page( &self, session_uuid: &str, limit: usize, cursor: Option<&str>, ) -> Result<AcpSessionPage>
Load one bounded page of the projected ACP transcript. Cursor reads hydrate only the durable groups needed for this page; the cursor’s message bound makes later appends invisible to an established walk.
Sourcepub fn load_session_for_agent(
&self,
session_uuid: &str,
agent_alias: &str,
) -> Result<Option<AcpSessionData>>
pub fn load_session_for_agent( &self, session_uuid: &str, agent_alias: &str, ) -> Result<Option<AcpSessionData>>
Load a durable ACP transcript only when both its UUID and owning agent match. Keeping the alias predicate in SQL makes an unknown UUID and a UUID owned by another agent indistinguishable to callers. Killed sessions remain readable as history; killing prevents runtime rehydration, not access to retained transcripts by their owner.
Sourcepub fn classify_session_for_agent(
&self,
session_uuid: &str,
agent_alias: &str,
) -> Result<AcpSessionAccess>
pub fn classify_session_for_agent( &self, session_uuid: &str, agent_alias: &str, ) -> Result<AcpSessionAccess>
Classify an exact ACP session key without exposing the foreign owner.
Callers use Foreign and Missing to make the same fail-closed response
while still avoiding an unsafe fallback to another session backend.
Sourcepub fn is_live_session_for_agent(
&self,
session_uuid: &str,
agent_alias: &str,
) -> Result<bool>
pub fn is_live_session_for_agent( &self, session_uuid: &str, agent_alias: &str, ) -> Result<bool>
Whether session_uuid is a live ACP session owned by agent_alias.
Sourcepub fn list_session_ids(&self) -> Result<Vec<String>>
pub fn list_session_ids(&self) -> Result<Vec<String>>
Every durable ACP session key, used only to fail closed when a Chat key collides with the separate ACP namespace.
Sourcepub fn load_session_for_restore(
&self,
session_uuid: &str,
) -> Result<AcpSessionRestore>
pub fn load_session_for_restore( &self, session_uuid: &str, ) -> Result<AcpSessionRestore>
Load only durable ACP rows that are allowed to become live sessions. Killed rows keep their transcript for history/export but are terminal for runtime restore paths.
Sourcepub fn list_sessions(&self) -> Result<Vec<AcpSessionSummary>>
pub fn list_sessions(&self) -> Result<Vec<AcpSessionSummary>>
List restorable sessions as lightweight summaries, ordered by most recent
activity first. This is the picker-facing read: it avoids the full
message-history hydration that load_session performs. Killed rows keep
history/export data but are terminal and must not be offered for restore.
Stale projected counts are rescored from the rows first, so the listing
never reports a cache another writer left behind.
Sourcepub fn list_live_sessions_by_agent(
&self,
agent_alias: &str,
) -> Result<Vec<AcpSessionSummary>>
pub fn list_live_sessions_by_agent( &self, agent_alias: &str, ) -> Result<Vec<AcpSessionSummary>>
List live ACP sessions owned by agent_alias, ordered by most recent
activity. Killed rows are omitted from live discovery, but their retained
transcripts remain readable through load_session_for_agent and they
remain available to export through list_sessions_by_agent.
pub fn contains_session(&self, session_uuid: &str) -> Result<bool>
Sourcepub fn append_turn(
&self,
session_uuid: &str,
messages: &[ConversationMessage],
) -> Result<()>
pub fn append_turn( &self, session_uuid: &str, messages: &[ConversationMessage], ) -> Result<()>
Append all ConversationMessages from one completed turn in one transaction.
Sourcepub fn replace_messages(
&self,
session_uuid: &str,
messages: &[ConversationMessage],
) -> Result<()>
pub fn replace_messages( &self, session_uuid: &str, messages: &[ConversationMessage], ) -> Result<()>
Replace a session’s visible transcript while keeping message identity local to the transcript. Native retained provider context uses a separate projection and never calls this method for a trim.
pub fn begin_turn_checkpoint( &self, session_uuid: &str, turn_id: &str, messages: &[ConversationMessage], ) -> Result<()>
pub fn append_turn_checkpoint( &self, session_uuid: &str, turn_id: &str, messages: &[ConversationMessage], ) -> Result<i64>
Sourcepub fn persist_retained_context_at_frontier(
&self,
session_uuid: &str,
turn_id: &str,
retained_messages: &[ConversationMessage],
breadcrumb: bool,
frontier: i64,
) -> Result<()>
pub fn persist_retained_context_at_frontier( &self, session_uuid: &str, turn_id: &str, retained_messages: &[ConversationMessage], breadcrumb: bool, frontier: i64, ) -> Result<()>
Persist an owner-observed checkpoint frontier. The frontier is supplied by the serial event consumer after its append transaction; it is never inferred by content comparison or by a delayed global MAX in this path.
Sourcepub fn persist_retained_context_seed(
&self,
session_uuid: &str,
retained_messages: &[ConversationMessage],
breadcrumb: bool,
) -> Result<()>
pub fn persist_retained_context_seed( &self, session_uuid: &str, retained_messages: &[ConversationMessage], breadcrumb: bool, ) -> Result<()>
Persist a seed-time trim projection. No turn checkpoint exists during restore, so this path verifies only the session identity and commits the owner-produced snapshot before its notification is forwarded.
pub fn checkpoint_frontier( &self, session_uuid: &str, turn_id: &str, ) -> Result<i64>
Sourcepub fn finalize_turn_checkpoint_with_context(
&self,
session_uuid: &str,
turn_id: &str,
terminal_messages: &[ConversationMessage],
retained_messages: &[ConversationMessage],
breadcrumb: bool,
) -> Result<()>
pub fn finalize_turn_checkpoint_with_context( &self, session_uuid: &str, turn_id: &str, terminal_messages: &[ConversationMessage], retained_messages: &[ConversationMessage], breadcrumb: bool, ) -> Result<()>
Finalize a turn while atomically preserving visible journal fragments, the final provider projection, breadcrumb provenance and checkpoint deletion. Existing transcript rows are never replaced or re-identified.
Sourcepub fn finalize_turn_checkpoint_with_context_for_owner(
&self,
session_uuid: &str,
turn_id: &str,
terminal_messages: &[ConversationMessage],
retained_messages: &[ConversationMessage],
breadcrumb: bool,
owner_principal_id: Option<&str>,
) -> Result<()>
pub fn finalize_turn_checkpoint_with_context_for_owner( &self, session_uuid: &str, turn_id: &str, terminal_messages: &[ConversationMessage], retained_messages: &[ConversationMessage], breadcrumb: bool, owner_principal_id: Option<&str>, ) -> Result<()>
Finalize only the durable row owned by the expected principal. The owner test and transcript, retained-context, and checkpoint changes occur in the same immediate transaction.
pub fn discard_turn_checkpoint( &self, session_uuid: &str, turn_id: &str, ) -> Result<()>
pub fn finalize_turn_checkpoint( &self, session_uuid: &str, turn_id: &str, messages: &[ConversationMessage], ) -> Result<()>
pub fn recover_turn_checkpoint( &self, session_uuid: &str, interruption_marker: &str, ) -> Result<bool>
Sourcepub fn recover_turn_checkpoint_for_owner(
&self,
session_uuid: &str,
interruption_marker: &str,
owner_principal_id: Option<&str>,
) -> Result<bool>
pub fn recover_turn_checkpoint_for_owner( &self, session_uuid: &str, interruption_marker: &str, owner_principal_id: Option<&str>, ) -> Result<bool>
Recover an interrupted turn only while the durable row still belongs
to owner_principal_id. The owner lookup and all checkpoint changes
share one immediate transaction, so a same-ID replacement cannot be
recovered under a stale principal authorization.
Sourcepub fn bounded_transcript_messages(
messages: &[ConversationMessage],
) -> Vec<ConversationMessage>
pub fn bounded_transcript_messages( messages: &[ConversationMessage], ) -> Vec<ConversationMessage>
Apply the transcript’s existing display/storage bound to native tool results before they enter a checkpoint or canonical ACP history.
pub fn bounded_tool_output(output: &str) -> String
Sourcepub fn provider_safe_history(
messages: &[ConversationMessage],
) -> Vec<ConversationMessage>
pub fn provider_safe_history( messages: &[ConversationMessage], ) -> Vec<ConversationMessage>
Produce provider-safe ACP history while preserving client-visible text. Only an immediately adjacent tool-call/result pair is retained, and one result is kept for each unambiguous call id. Duplicate call or result IDs within a batch are rejected; recovery markers stay transcript-only.
pub fn set_token_count( &self, session_uuid: &str, token_count: u64, ) -> Result<()>
Sourcepub fn clear_token_count(&self, session_uuid: &str) -> Result<()>
pub fn clear_token_count(&self, session_uuid: &str) -> Result<()>
Clear the durable token snapshot back to the schema’s unknown representation (0). Used when an accepted turn attempt serves a route without token usage: the prior snapshot must not survive, mirroring the client-side meter which clears on accepted usage-less events.
Sourcepub fn persist_usage_snapshot(
&self,
session_uuid: &str,
input_tokens: Option<u64>,
accepted: bool,
) -> Result<()>
pub fn persist_usage_snapshot( &self, session_uuid: &str, input_tokens: Option<u64>, accepted: bool, ) -> Result<()>
Apply a TurnEvent::Usage to the durable token snapshot. Accepted
events with usage overwrite it; accepted usage-less events clear it
back to unknown (0) so a resumed session never replays a stale
route’s count; rejected billing telemetry never touches the store.
Both durable consumers (ACP server, RPC dispatch) route through here
so the accepted-gate cannot drift between paths.
Record whether this session’s persisted transcript currently starts
with the synthetic trim breadcrumb, as one canonical fact alongside
the transcript. Silently no-ops for an unknown session (matching
append_turn’s tolerance for a session removed mid-turn) rather than
erroring like set_token_count, since this is best-effort bookkeeping
that must never fail a turn.
Sourcepub fn set_plan(&self, session_uuid: &str, entries: &[PlanEntry]) -> Result<()>
pub fn set_plan(&self, session_uuid: &str, entries: &[PlanEntry]) -> Result<()>
Persist the session’s latest TodoWrite plan as a JSON array of
PlanEntry (whole-list replace). An empty slice stores an empty
array (a cleared plan), distinct from SQL NULL (never had one).
Sourcepub fn get_plan(&self, session_uuid: &str) -> Result<Vec<PlanEntry>>
pub fn get_plan(&self, session_uuid: &str) -> Result<Vec<PlanEntry>>
Load the session’s stored plan. Returns an empty vec when the session has no plan (NULL or absent). Malformed JSON is treated as an empty plan rather than a hard error, so a corrupt plan column never blocks session restore.
Sourcepub fn append_event(
&self,
session_uuid: &str,
action: Action,
outcome: EventOutcome,
payload: Option<&str>,
) -> Result<()>
pub fn append_event( &self, session_uuid: &str, action: Action, outcome: EventOutcome, payload: Option<&str>, ) -> Result<()>
Record a session-lifecycle event. Caller passes typed enums; the SQLite
layer is the only place strings appear. Same Action / EventOutcome
values are used at the matching zeroclaw_log::record! call site.
Sourcepub fn delete_session(&self, session_uuid: &str) -> Result<bool>
pub fn delete_session(&self, session_uuid: &str) -> Result<bool>
Delete a session and all its child rows (messages, tool calls, events
cascade via FK). Returns true if the session existed.
Sourcepub fn count_live_sessions_by_agent(&self, agent_alias: &str) -> Result<usize>
pub fn count_live_sessions_by_agent(&self, agent_alias: &str) -> Result<usize>
Count live ACP sessions for agent_alias — rows not yet killed
(killed_at IS NULL). A non-zero count is a HARD blocker for deleting the
agent: the operator must end the sessions first.
Sourcepub fn list_sessions_by_agent(
&self,
agent_alias: &str,
) -> Result<Vec<AcpSessionSummary>>
pub fn list_sessions_by_agent( &self, agent_alias: &str, ) -> Result<Vec<AcpSessionSummary>>
Summaries of every ACP session (live or killed) attributed to
agent_alias, for the export-then-delete archive.
Sourcepub fn projected_message_count(
&self,
session_uuid: &str,
) -> Result<Option<usize>>
pub fn projected_message_count( &self, session_uuid: &str, ) -> Result<Option<usize>>
Persisted projected conversation-entry count for the session, the
same number the session picker lists and turn_end reports. Reading
it avoids hydrating the full message history that load_session
performs just to re-derive the count; a cache another writer left
stale is rescored from the session’s rows here first. None when the
session UUID is unknown.
Sourcepub fn delete_sessions_by_agent(&self, agent_alias: &str) -> Result<usize>
pub fn delete_sessions_by_agent(&self, agent_alias: &str) -> Result<usize>
Delete every ACP session (live or killed) for agent_alias, returning the
row count. Child tables (acp_messages/acp_tool_calls/acp_session_events)
cascade via their ON DELETE CASCADE FKs (foreign_keys = ON).
Sourcepub fn rename_sessions_by_agent(&self, from: &str, to: &str) -> Result<usize>
pub fn rename_sessions_by_agent(&self, from: &str, to: &str) -> Result<usize>
Re-point every ACP session (live or killed) from from to to,
returning the row count. The agent-rename cascadekeeps the
session and its transcript; only the owning alias moves. Unlike delete,
a live session (killed_at IS NULL) is no obstacle to rename.
Sourcepub fn mark_session_killed_atomic(
&self,
session_uuid: &str,
) -> Result<AcpSessionKillTransition>
pub fn mark_session_killed_atomic( &self, session_uuid: &str, ) -> Result<AcpSessionKillTransition>
Atomically persist that an admin intentionally killed this ACP session. The transcript and any checkpoint stay durable, but runtime rehydration must not revive it.
Sourcepub fn mark_session_killed_atomic_for_owner(
&self,
session_uuid: &str,
owner_principal_id: Option<&str>,
) -> Result<AcpSessionKillTransition>
pub fn mark_session_killed_atomic_for_owner( &self, session_uuid: &str, owner_principal_id: Option<&str>, ) -> Result<AcpSessionKillTransition>
Tombstone only the durable row still owned by this principal. The update predicate and transition inspection share one transaction.
Sourcepub fn mark_session_killed(&self, session_uuid: &str) -> Result<bool>
pub fn mark_session_killed(&self, session_uuid: &str) -> Result<bool>
Persist that an admin intentionally killed this ACP session. The
transcript stays durable, but runtime rehydration must not revive it.
This compatibility wrapper retains the original boolean contract for
existing callers; use mark_session_killed_atomic when the distinction
between marked, already-killed, and missing matters.
Sourcepub fn is_session_killed(&self, session_uuid: &str) -> Result<bool>
pub fn is_session_killed(&self, session_uuid: &str) -> Result<bool>
Return whether this durable ACP session has been intentionally killed. Missing rows are not killed; callers can then use normal load handling to distinguish SESSION_NOT_FOUND from a terminal killed session.
Sourcepub fn touch_session(&self, session_uuid: &str) -> Result<()>
pub fn touch_session(&self, session_uuid: &str) -> Result<()>
Update last_activity without appending messages.