From 621d7fee0ace729f8d44126032d2c6e13f72ee7f Mon Sep 17 00:00:00 2001 From: t Date: Tue, 7 Jul 2026 11:29:23 -0600 Subject: Move libpanto projects to libpantograph dependency 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. --- libpanto/src/session_store.zig | 188 ----------------------------------------- 1 file changed, 188 deletions(-) delete mode 100644 libpanto/src/session_store.zig (limited to 'libpanto/src/session_store.zig') 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); - } -}; -- cgit v1.3