summaryrefslogtreecommitdiff
path: root/libpanto/src/session_store.zig
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-07-07 11:29:23 -0600
committert <t@tjp.lol>2026-07-07 12:15:28 -0600
commit621d7fee0ace729f8d44126032d2c6e13f72ee7f (patch)
treea2938b0a9e5c0930e8644721abbe94875df9ff08 /libpanto/src/session_store.zig
parent0fa6d4209ff9b4a95e7d1955887aa4c73ee3423c (diff)
Move libpanto projects to libpantograph dependencyHEADmain
Remove the in-repo libpanto sources and binding projects from pantograph. Consume libpantograph through the Zig package URL at code.tjp.lol/libpantograph.git, including the Lua module artifact used by CLI extensions.
Diffstat (limited to 'libpanto/src/session_store.zig')
-rw-r--r--libpanto/src/session_store.zig188
1 files changed, 0 insertions, 188 deletions
diff --git a/libpanto/src/session_store.zig b/libpanto/src/session_store.zig
deleted file mode 100644
index de25e21..0000000
--- a/libpanto/src/session_store.zig
+++ /dev/null
@@ -1,188 +0,0 @@
-//! `SessionStore`: the neutral persistence seam for the `Agent`.
-//!
-//! The interface is **asymmetric**: rich on write (audit/provenance-capable),
-//! minimal on read (resume-oriented). The store decides how much write-side
-//! richness it durably keeps.
-//!
-//! ## Write side (maximalist)
-//!
-//! `appendMessages` takes `[]PersistentMessage` — the rich, audit-oriented
-//! write record. Each carries the in-memory `Message` being appended, its
-//! `usage`, the **wire-format** provider identity (`api_style`, `base_url`,
-//! `model`, `reasoning` — never CLI config aliases, and never any `api_key`
-//! material, not even a hash), and full provenance context (the entire
-//! current conversation and the tool set offered for this turn). The library
-//! *offers* all of it on every append; a store keeps what it wants. The
-//! built-in `FileSystemJSONLStore` deliberately ignores the `conversation`
-//! and `tools_available` provenance fields.
-//!
-//! ## Read side (minimal)
-//!
-//! `load` reconstructs one linear `Conversation`; `list`/`resolve`/`latest`
-//! traffic in `SessionInfo` (display/selection metadata) and `Session`
-//! (an `info` + a store to proxy to). The read path never reproduces a
-//! `PersistentMessage` — a store may not have kept the provenance.
-//!
-//! ## Store construction
-//!
-//! Stores own their own (unprescribed) `init`: a Postgres store takes a DSN,
-//! the FS store takes a directory. Nothing in the vtable carries an
-//! allocator or io — the store captured whatever it needs at its own init.
-
-const std = @import("std");
-const Allocator = std.mem.Allocator;
-
-const session_mod = @import("session.zig");
-const conversation_mod = @import("conversation.zig");
-const config_mod = @import("config.zig");
-const tool_source_mod = @import("tool_source.zig");
-
-pub const Conversation = conversation_mod.Conversation;
-pub const Message = conversation_mod.Message;
-pub const Usage = conversation_mod.Usage;
-pub const APIStyle = config_mod.APIStyle;
-pub const ReasoningEffort = config_mod.ReasoningEffort;
-pub const ToolDecl = tool_source_mod.ToolDecl;
-
-/// The default filesystem-JSONL backend, re-exported under its
-/// interface-facing name. Its concrete constructor (`init`/`open`) and
-/// catalog helpers are backend-specific and stay on that module.
-pub const FileSystemJSONLStore = @import("file_system_jsonl_store.zig").FileSystemJSONLStore;
-
-/// Wire-format provider identity. This is the **ground truth** of which
-/// endpoint a turn was sent to — never a CLI config alias (aliases get
-/// renamed; two keys for one endpoint are indistinguishable on the wire).
-/// `reasoning` disambiguates otherwise-identical endpoints. No `api_key`
-/// material ever appears here. Aliased from `config` to avoid a module
-/// cycle (config must not import session_store).
-pub const WireIdentity = config_mod.WireIdentity;
-
-/// The rich, audit-oriented write record. The library offers all of this on
-/// every append; the store keeps what it wants.
-pub const PersistentMessage = struct {
- /// The in-memory message being appended (carries its own `metadata`).
- message: Message,
- /// Provider usage for this message (assistant turns), or null.
- usage: ?Usage = null,
- /// Wire-format provider identity for the turn this message belongs to.
- identity: WireIdentity,
- /// Full provenance: the entire current conversation at write time. The
- /// FS store ignores this; an audit store may content-address it.
- conversation: []const Message = &.{},
- /// Full provenance: the tool set offered for this turn. The FS store
- /// ignores this; an audit store may content-address it.
- tools_available: []const ToolDecl = &.{},
-};
-
-/// Display/selection metadata for one session — pure data, aliased. Used by
-/// `panto sessions` and resume pre-selection. The last-used wire identity is
-/// updated on append (for resume), never a CLI config alias.
-pub const SessionInfo = struct {
- id: []const u8,
- created: []const u8,
- modified: []const u8,
- message_count: usize,
- /// May be truncated.
- last_user_message: []const u8,
- /// Last-used wire identity, updated on append.
- api_style: APIStyle,
- base_url: []const u8,
- model: []const u8,
- reasoning: ReasoningEffort,
-
- pub fn deinit(self: SessionInfo, alloc: Allocator) void {
- alloc.free(self.id);
- alloc.free(self.created);
- alloc.free(self.modified);
- alloc.free(self.last_user_message);
- alloc.free(self.base_url);
- alloc.free(self.model);
- }
-};
-
-/// A session handle: pure data (a `SessionInfo`) plus a store to proxy to.
-pub const Session = struct {
- info: SessionInfo,
- store: SessionStore,
-
- /// Reconstruct the conversation. The id came from `resolve`/`latest`, so
- /// the conversation must exist; a `null` from the store is promoted to
- /// an error.
- pub fn load(self: Session) !Conversation {
- return (try self.store.load(self.info.id)) orelse error.SessionNotFound;
- }
-
- /// Append a batch of messages, proxying to the store. Takes `*Session`
- /// for API symmetry and to allow future in-place `info` updates; today
- /// it only updates the non-owning `api_style`/`reasoning` last-used
- /// fields (the `base_url`/`model` strings stay the owned originals to
- /// avoid aliasing borrowed config memory — resume picks the default
- /// model rather than matching the stored wire identity, so the stale
- /// display strings are harmless).
- pub fn append(self: *Session, messages: []PersistentMessage) !void {
- try self.store.appendMessages(self.info.id, messages);
- if (messages.len > 0) {
- const id = messages[messages.len - 1].identity;
- self.info.api_style = id.api_style;
- self.info.reasoning = id.reasoning;
- }
- }
-};
-
-/// A pluggable session-persistence backend.
-pub const SessionStore = struct {
- ptr: *anyopaque,
- vtable: *const VTable,
-
- pub const VTable = struct {
- /// Mint an in-memory session handle. Cannot fail: nothing hits the
- /// backend until the first `appendMessages` (create-on-demand), so
- /// no record exists before the first assistant message.
- create: *const fn (ctx: *anyopaque) Session,
-
- /// List known sessions, newest first. Caller frees via
- /// `freeSessionInfos`.
- list: *const fn (ctx: *anyopaque) anyerror![]SessionInfo,
-
- /// Free a slice returned by `list`.
- freeSessionInfos: *const fn (ctx: *anyopaque, infos: []SessionInfo) void,
-
- /// Resolve a (possibly abbreviated) id to a session, or null if no
- /// match.
- resolve: *const fn (ctx: *anyopaque, id: []const u8) anyerror!?Session,
-
- /// The most recent session, or null if none exist.
- latest: *const fn (ctx: *anyopaque) anyerror!?Session,
-
- /// Reconstruct one linear `Conversation` for `id`, or null if absent.
- /// The returned `Conversation` self-describes its allocator.
- load: *const fn (ctx: *anyopaque, id: []const u8) anyerror!?Conversation,
-
- /// Append a batch atomically. A single append is a length-1 batch.
- /// The store reads what it wants off each `PersistentMessage` and
- /// is responsible for any de-duplication of provenance.
- appendMessages: *const fn (ctx: *anyopaque, session_id: []const u8, messages: []PersistentMessage) anyerror!void,
- };
-
- pub fn create(self: SessionStore) Session {
- return self.vtable.create(self.ptr);
- }
- pub fn list(self: SessionStore) ![]SessionInfo {
- return self.vtable.list(self.ptr);
- }
- pub fn freeSessionInfos(self: SessionStore, infos: []SessionInfo) void {
- self.vtable.freeSessionInfos(self.ptr, infos);
- }
- pub fn resolve(self: SessionStore, id: []const u8) !?Session {
- return self.vtable.resolve(self.ptr, id);
- }
- pub fn latest(self: SessionStore) !?Session {
- return self.vtable.latest(self.ptr);
- }
- pub fn load(self: SessionStore, id: []const u8) !?Conversation {
- return self.vtable.load(self.ptr, id);
- }
- pub fn appendMessages(self: SessionStore, session_id: []const u8, messages: []PersistentMessage) !void {
- return self.vtable.appendMessages(self.ptr, session_id, messages);
- }
-};