pub struct JsonlSessionStore { /* private fields */ }Expand description
JSONL file-based session store.
Each session is a single .jsonl file in the configured directory.
Line 1 is SessionMeta (JSON), lines 2+ are one LlmMessage each.
Concurrent writes to the same session may corrupt the file. Callers are expected to enforce single-writer access.
With the search feature enabled, a tantivy index is maintained in
<sessions_dir>/.search_index/ and used by
SessionStore::search(). Without the feature, search falls back to a
linear scan of all JSONL files.
Implementations§
Source§impl JsonlSessionStore
impl JsonlSessionStore
Sourcepub fn new(sessions_dir: PathBuf) -> Result<Self>
pub fn new(sessions_dir: PathBuf) -> Result<Self>
Create a new store rooted at the given directory.
Creates the directory (and parents) if it does not exist.
Sourcepub fn open_search_index(&self) -> Result<()>
pub fn open_search_index(&self) -> Result<()>
Open (or create) the tantivy search index eagerly.
Normally the index is opened lazily on the first search() call.
Call this method if you want to detect index-creation errors at startup
rather than at search time.
Only available with the search feature.
Sourcepub fn rebuild_search_index(&self) -> Result<()>
pub fn rebuild_search_index(&self) -> Result<()>
Build (or rebuild) the tantivy index from all current JSONL files.
Existing index data is replaced. Use this after bulk-importing sessions outside of the store API.
Only available with the search feature.
Sourcepub fn with_migrators(self, migrators: Vec<Box<dyn SessionMigrator>>) -> Self
pub fn with_migrators(self, migrators: Vec<Box<dyn SessionMigrator>>) -> Self
Register session migrators for automatic schema upgrades on load.
Sourcepub fn default_dir() -> Option<PathBuf>
pub fn default_dir() -> Option<PathBuf>
Default sessions directory: <config_dir>/swink-agent/sessions.
Returns None if the platform config directory cannot be determined.
Sourcepub fn new_session_id() -> String
pub fn new_session_id() -> String
Generate a new unique session ID using a UTC timestamp plus UUID suffix.
Source§impl JsonlSessionStore
impl JsonlSessionStore
Sourcepub fn save_entries(
&self,
id: &str,
meta: &SessionMeta,
entries: &[SessionEntry],
) -> Result<()>
pub fn save_entries( &self, id: &str, meta: &SessionMeta, entries: &[SessionEntry], ) -> Result<()>
Save a session with rich entry types.
Lines 2+ are SessionEntry values serialized with an entry_type tag.
When the search feature is enabled, the tantivy index is updated
after the file is written (best-effort — index errors are logged but
do not fail the save).
Sourcepub fn append_entries(
&self,
id: &str,
meta: &SessionMeta,
entries: &[SessionEntry],
) -> Result<SessionMeta>
pub fn append_entries( &self, id: &str, meta: &SessionMeta, entries: &[SessionEntry], ) -> Result<SessionMeta>
Append entries to an existing session without rewriting the whole file.
This is the append-only counterpart to Self::save_entries. Instead of
rewriting every record on each turn, it appends only the new entries
and patches the metadata line in place (reusing the same on-disk
machinery and locking guarantees as the internal append path).
The caller’s meta.sequence must match the on-disk sequence
(optimistic concurrency, identical to Self::save_entries); a mismatch
returns an error. Every other meta field is written as provided —
only id and sequence are set by the store. The returned
SessionMeta carries the incremented sequence so callers stay in
sync without re-reading the file.
If the metadata line can no longer be patched in place — its serialized form would outgrow the slot reserved on disk — this transparently falls back to a full rewrite that preserves all existing lines.
Errors if the session file does not exist; use Self::save_entries to
create a new session.
When the search feature is enabled, only the newly appended
entries are added to the tantivy index afterwards — the session’s
existing documents are untouched (best-effort — index errors are
logged but do not fail the append).
Sourcepub fn load_entries(&self, id: &str) -> Result<(SessionMeta, Vec<SessionEntry>)>
pub fn load_entries(&self, id: &str) -> Result<(SessionMeta, Vec<SessionEntry>)>
Load a session with rich entry types.
Parses each line after metadata as a SessionEntry. Old-format lines
(raw LlmMessage without entry_type) are interpreted as
SessionEntry::Message for backward compatibility.
Trait Implementations§
Source§impl SessionStore for JsonlSessionStore
impl SessionStore for JsonlSessionStore
Source§fn save(
&self,
id: &str,
meta: &SessionMeta,
messages: &[AgentMessage],
) -> Result<()>
fn save( &self, id: &str, meta: &SessionMeta, messages: &[AgentMessage], ) -> Result<()>
Source§fn save_full(
&self,
id: &str,
meta: &SessionMeta,
messages: &[AgentMessage],
state: &Value,
) -> Result<SessionMeta>
fn save_full( &self, id: &str, meta: &SessionMeta, messages: &[AgentMessage], state: &Value, ) -> Result<SessionMeta>
Source§fn append(&self, id: &str, messages: &[AgentMessage]) -> Result<()>
fn append(&self, id: &str, messages: &[AgentMessage]) -> Result<()>
Source§fn load(
&self,
id: &str,
registry: Option<&CustomMessageRegistry>,
) -> Result<(SessionMeta, Vec<AgentMessage>)>
fn load( &self, id: &str, registry: Option<&CustomMessageRegistry>, ) -> Result<(SessionMeta, Vec<AgentMessage>)>
Source§fn load_full(
&self,
id: &str,
registry: Option<&CustomMessageRegistry>,
) -> Result<(SessionMeta, Vec<AgentMessage>, Option<Value>)>
fn load_full( &self, id: &str, registry: Option<&CustomMessageRegistry>, ) -> Result<(SessionMeta, Vec<AgentMessage>, Option<Value>)>
Source§fn list(&self) -> Result<Vec<SessionMeta>>
fn list(&self) -> Result<Vec<SessionMeta>>
Source§fn save_state(&self, id: &str, state: &Value) -> Result<()>
fn save_state(&self, id: &str, state: &Value) -> Result<()>
Source§fn load_state(&self, id: &str) -> Result<Option<Value>>
fn load_state(&self, id: &str) -> Result<Option<Value>>
Source§fn save_interrupt(&self, id: &str, state: &InterruptState) -> Result<()>
fn save_interrupt(&self, id: &str, state: &InterruptState) -> Result<()>
Source§fn load_interrupt(&self, id: &str) -> Result<Option<InterruptState>>
fn load_interrupt(&self, id: &str) -> Result<Option<InterruptState>>
Source§fn clear_interrupt(&self, id: &str) -> Result<()>
fn clear_interrupt(&self, id: &str) -> Result<()>
Source§fn load_with_options(
&self,
id: &str,
options: &LoadOptions,
) -> Result<(SessionMeta, Vec<SessionEntry>)>
fn load_with_options( &self, id: &str, options: &LoadOptions, ) -> Result<(SessionMeta, Vec<SessionEntry>)>
Source§fn search(
&self,
query: &str,
options: &SessionSearchOptions,
) -> Result<Vec<SessionHit>>
fn search( &self, query: &str, options: &SessionSearchOptions, ) -> Result<Vec<SessionHit>>
Auto Trait Implementations§
impl !Freeze for JsonlSessionStore
impl !RefUnwindSafe for JsonlSessionStore
impl !UnwindSafe for JsonlSessionStore
impl Send for JsonlSessionStore
impl Sync for JsonlSessionStore
impl Unpin for JsonlSessionStore
impl UnsafeUnpin for JsonlSessionStore
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>, which can then be
downcast into Box<dyn ConcreteType> where ConcreteType implements Trait.Source§fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
Rc<Trait> (where Trait: Downcast) to Rc<Any>, which can then be further
downcast into Rc<ConcreteType> where ConcreteType implements Trait.Source§fn as_any(&self) -> &(dyn Any + 'static)
fn as_any(&self) -> &(dyn Any + 'static)
&Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &Any’s vtable from &Trait’s.Source§fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot
generate &mut Any’s vtable from &mut Trait’s.Source§impl<T> DowncastSend for T
impl<T> DowncastSend for T
Source§impl<T> DowncastSync for T
impl<T> DowncastSync for T
impl<T> Fruit for T
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
fn instrument(self, span: Span) -> Instrumented<Self> ⓘ
Source§fn in_current_span(self) -> Instrumented<Self> ⓘ
fn in_current_span(self) -> Instrumented<Self> ⓘ
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more