summaryrefslogtreecommitdiff
path: root/src/session_persist.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/session_persist.zig')
-rw-r--r--src/session_persist.zig153
1 files changed, 0 insertions, 153 deletions
diff --git a/src/session_persist.zig b/src/session_persist.zig
deleted file mode 100644
index d5349bc..0000000
--- a/src/session_persist.zig
+++ /dev/null
@@ -1,153 +0,0 @@
-//! Session-log persistence helpers shared between the REPL loop
-//! (`main.zig`) and slash-command handlers (e.g. `compaction.zig`).
-//!
-//! These were previously private to `main.zig`. They were lifted into
-//! their own module so that command handlers living outside the root
-//! source file can reuse them without depending on `main.zig` (which,
-//! being the executable root, can't be imported as a normal module).
-
-const std = @import("std");
-const panto = @import("panto");
-
-/// Append a single user-prompt message to the session log.
-pub fn appendUserPromptToSession(
- alloc: std.mem.Allocator,
- mgr: *panto.session_manager.SessionManager,
- text: []const u8,
- provider: []const u8,
- model: []const u8,
-) !void {
- const blocks = try alloc.alloc(panto.session_manager.DiskContentBlock, 1);
- blocks[0] = .{ .text = .{ .text = try alloc.dupe(u8, text) } };
- _ = try mgr.appendMessage(
- .{ .role = .user, .content = blocks },
- provider,
- model,
- );
-}
-
-/// After the agent loop has driven a turn to completion, persist every
-/// new in-memory message at index `>= start_index` to the session log.
-///
-/// Each in-memory message is mapped to disk:
-/// - assistant → assistant entry with provider/model/stop_reason metadata
-/// and Usage (from `per_message_usage`, one slot per assistant message in
-/// completion order).
-/// - user (with ToolResult blocks) → user entry stamped with provider/model.
-///
-/// `stop_reason` is recorded as `"stop"` for now; surfacing the real wire
-/// value requires plumbing it through the Receiver vtable (future work,
-/// separate from token plumbing).
-pub fn persistTurn(
- alloc: std.mem.Allocator,
- mgr: *panto.session_manager.SessionManager,
- conv: *const panto.conversation.Conversation,
- start_index: usize,
- provider: []const u8,
- model: []const u8,
- per_message_usage: []const ?panto.session_manager.Usage,
-) !void {
- var batch_messages: std.ArrayList(panto.session_manager.DiskMessage) = .empty;
- defer batch_messages.deinit(alloc);
- var batch_providers: std.ArrayList(?[]const u8) = .empty;
- defer batch_providers.deinit(alloc);
- var batch_models: std.ArrayList(?[]const u8) = .empty;
- defer batch_models.deinit(alloc);
-
- var i = start_index;
- var assistant_seen: usize = 0;
- while (i < conv.messages.items.len) : (i += 1) {
- const msg = conv.messages.items[i];
- const blocks = try alloc.alloc(panto.session_manager.DiskContentBlock, msg.content.items.len);
- var allocated: usize = 0;
- errdefer {
- for (blocks[0..allocated]) |b| b.deinit(alloc);
- alloc.free(blocks);
- }
- for (msg.content.items) |block| {
- blocks[allocated] = try panto.session.contentBlockToDisk(alloc, block);
- allocated += 1;
- }
- if (msg.role == .assistant and hasToolUseWithoutFollowingResults(conv, i)) {
- for (blocks[0..allocated]) |b| b.deinit(alloc);
- alloc.free(blocks);
- continue;
- }
- switch (msg.role) {
- .system => {
- // Mid-turn system messages aren't a thing in pantograph today.
- // Treat as harmless and persist verbatim, sans stamps.
- try batch_messages.append(alloc, .{ .role = .system, .content = blocks });
- try batch_providers.append(alloc, null);
- try batch_models.append(alloc, null);
- },
- .user => {
- try batch_messages.append(alloc, .{ .role = .user, .content = blocks });
- try batch_providers.append(alloc, provider);
- try batch_models.append(alloc, model);
- },
- .assistant => {
- // Pair this assistant message with its usage, if the
- // receiver captured one for this position. (A turn
- // ending in a stream error can have fewer usage entries
- // than assistant messages; treat as null and move on.)
- const usage: ?panto.session_manager.Usage = if (assistant_seen < per_message_usage.len)
- per_message_usage[assistant_seen]
- else
- null;
- assistant_seen += 1;
- try batch_messages.append(alloc, .{
- .role = .assistant,
- .content = blocks,
- .provider = try alloc.dupe(u8, provider),
- .model = try alloc.dupe(u8, model),
- .stop_reason = try alloc.dupe(u8, "stop"),
- .usage = usage,
- });
- try batch_providers.append(alloc, null);
- try batch_models.append(alloc, null);
- },
- }
- }
- try mgr.appendMessagesAtomic(batch_messages.items, batch_providers.items, batch_models.items);
-}
-
-/// Persist a compaction to the session log. The agent rewrote the
-/// in-memory conversation to `[system..., summary, kept-suffix...]`; the
-/// superseded prefix stays on disk untouched. We append everything from
-/// the compaction summary onward as fresh entries: the summary message
-/// (carrying the `compactionSummary` block) followed by the duplicated
-/// kept-verbatim suffix. On replay, the latest summary resets effective
-/// context, so the duplicated suffix is what survives.
-///
-/// Usage is not threaded through here: the duplicated entries are replays
-/// of already-sized turns, and the summary itself isn't an assistant turn.
-pub fn persistCompaction(
- alloc: std.mem.Allocator,
- mgr: *panto.session_manager.SessionManager,
- conv: *const panto.conversation.Conversation,
- provider: []const u8,
- model: []const u8,
-) !void {
- const start = panto.conversation.latestCompactionIndex(conv.messages.items) orelse return;
- try persistTurn(alloc, mgr, conv, start, provider, model, &.{});
-}
-
-pub fn hasToolUseWithoutFollowingResults(conv: *const panto.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;
-}