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/turn_persist.zig | 133 ------------------------------------------ 1 file changed, 133 deletions(-) delete mode 100644 libpanto/src/turn_persist.zig (limited to 'libpanto/src/turn_persist.zig') diff --git a/libpanto/src/turn_persist.zig b/libpanto/src/turn_persist.zig deleted file mode 100644 index 6d260c8..0000000 --- a/libpanto/src/turn_persist.zig +++ /dev/null @@ -1,133 +0,0 @@ -//! Turn → session-log persistence: map in-memory `Conversation` messages -//! to rich `PersistentMessage` write records and append them through a -//! `Session` handle. -//! -//! This logic lives in `libpanto` and is called by the `Agent` itself, so -//! every embedder gets persistence for free. The functions here are -//! stateless helpers over a `Session`; the agent owns the store and the -//! conversation. -//! -//! The write record is **maximalist** (full wire identity + usage + the -//! entire current conversation + the offered tool set). The library offers -//! it all on every append; each store keeps what it wants. `PersistentMessage` -//! borrows the in-memory `Message` directly — no disk conversion happens -//! here; the store does its own serialization. -//! -//! Per-message usage is read directly off `Message.usage`. - -const std = @import("std"); -const Allocator = std.mem.Allocator; - -const conversation = @import("conversation.zig"); -const session_store = @import("session_store.zig"); - -const PersistentMessage = session_store.PersistentMessage; -const WireIdentity = session_store.WireIdentity; -const ToolDecl = session_store.ToolDecl; -const Session = session_store.Session; - -/// Persist every conversation message at index `>= start_index` through -/// `session` as a single atomic batch. -/// -/// Each `PersistentMessage` borrows the in-memory `Message`, the full -/// current conversation, and `tools` (the offered tool set) — all owned by -/// the caller and valid for the duration of the call. The wire `identity` -/// is stamped on every message; the store decides per-role what to keep -/// (the FS store drops the stamp on system entries). -/// -/// An assistant message carrying a ToolUse with no following matching -/// ToolResult is skipped (a dangling tool call from an interrupted turn); -/// persisting it would make the log un-replayable. -pub fn persistTurn( - alloc: Allocator, - session: *Session, - conv: *conversation.Conversation, - start_index: usize, - identity: WireIdentity, - tools: []const ToolDecl, -) !void { - return persistRange(alloc, session, conv, start_index, conv.messages.items.len, identity, tools); -} - -/// Persist conversation messages in `[start_index, end_index)`. Like -/// `persistTurn` but with an explicit upper bound, so an incremental flush can -/// exclude a trailing not-yet-coherent message (a dangling tool call awaiting -/// its results) while still committing everything before it. -pub fn persistRange( - alloc: Allocator, - session: *Session, - conv: *conversation.Conversation, - start_index: usize, - end_index: usize, - identity: WireIdentity, - tools: []const ToolDecl, -) !void { - var batch: std.ArrayList(PersistentMessage) = .empty; - defer batch.deinit(alloc); - - const all_messages = conv.messages.items; - var i = start_index; - while (i < end_index) : (i += 1) { - const msg = &all_messages[i]; - if (msg.role == .assistant and hasToolUseWithoutFollowingResults(conv, i)) { - continue; - } - // Stamp the producing identity once, on first persist. A message that - // already carries one (e.g. a kept-verbatim turn carried through - // compaction, or one rebuilt from disk) keeps it — only the messages - // freshly produced under `identity` are stamped now. This is what - // makes the stamp survive a later compaction onto a different model. - if (msg.identity == null) { - msg.identity = try conversation.dupeWireIdentity(alloc, identity); - } - try batch.append(alloc, .{ - .message = msg.*, - .usage = msg.usage, - .identity = msg.identity.?, - .conversation = all_messages, - .tools_available = tools, - }); - } - - if (batch.items.len == 0) return; - try session.append(batch.items); -} - -/// Persist a compaction result. The agent rewrote the conversation to -/// `[system..., summary, kept-suffix...]`; persist everything from the -/// latest compaction summary onward as fresh entries. -pub fn persistCompaction( - alloc: Allocator, - session: *Session, - conv: *conversation.Conversation, - identity: WireIdentity, - tools: []const ToolDecl, -) !void { - const start = conversation.latestCompactionIndex(conv.messages.items) orelse return; - try persistTurn(alloc, session, conv, start, identity, tools); -} - -/// True when the assistant message at `index` contains a ToolUse block -/// with no matching ToolResult in the immediately following user message -/// — i.e. a dangling tool call (e.g. a turn cut short by an error). -pub fn hasToolUseWithoutFollowingResults( - conv: *const conversation.Conversation, - index: usize, -) bool { - const msg = conv.messages.items[index]; - for (msg.content.items) |block| { - if (block != .ToolUse) continue; - if (index + 1 >= conv.messages.items.len) return true; - const next = conv.messages.items[index + 1]; - if (next.role != .user) return true; - var found = false; - for (next.content.items) |next_block| { - if (next_block == .ToolResult and std.mem.eql(u8, next_block.ToolResult.tool_use_id, block.ToolUse.id)) { - found = true; - break; - } - } - if (!found) return true; - } - return false; -} -- cgit v1.3