summaryrefslogtreecommitdiff
path: root/libpanto/src/agent.zig
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-06-07 11:35:49 -0600
committert <t@tjp.lol>2026-06-07 11:35:49 -0600
commitb14859b9726185ab873356390068e887b7f486d3 (patch)
tree6530d8ee62e7639b50c99a67d23f89e25ef572dc /libpanto/src/agent.zig
parentd36a51358efbc48de3d5b732727455efc60acae1 (diff)
R2: redesign session store with wire-format identity
Replace the single-session SessionManager seam with a directory-backed catalog. session_manager.zig -> file_system_jsonl_store.zig; the old machinery becomes internal SessionFile, and a new FileSystemJSONLStore implements the redesigned SessionStore vtable (create/list/resolve/latest/ load/appendMessages) minting Session/SessionInfo handles. Session logs now record wire-format provider identity ({api_style, base_url, model, reasoning}) instead of a single provider string or CLI aliases; no api_key material is ever stored. Disk* content types renamed Stored*; the rich audit-oriented write record is PersistentMessage (in-memory Message + WireIdentity + provenance). Agent.init now takes a Session; persist_provider/persist_model display strings deleted (banner stays alias-based, resume picks default model). Message.metadata round-trips. Dangling-prompt recovery dropped. CLI migrated to the catalog store for run/resume/list. Clean break, no version bump (old logs wiped).
Diffstat (limited to 'libpanto/src/agent.zig')
-rw-r--r--libpanto/src/agent.zig240
1 files changed, 120 insertions, 120 deletions
diff --git a/libpanto/src/agent.zig b/libpanto/src/agent.zig
index e8be325..604ee55 100644
--- a/libpanto/src/agent.zig
+++ b/libpanto/src/agent.zig
@@ -197,20 +197,12 @@ pub const Agent = struct {
/// at `init`); torn down in `deinit`. Turn-driving methods operate on
/// this directly rather than taking a `*Conversation` parameter.
conversation: conversation.Conversation,
- /// Pluggable persistence backend. Every message that enters
- /// `conversation` is persisted here as it is added. Defaults to a no-op
- /// `NullStore` so embedders/tests that don't care about persistence
- /// keep working. Borrowed: the embedder owns the concrete store and
- /// must outlive the agent.
- session_store: session_store_mod.SessionStore,
- /// Display provider/model names used to stamp persisted entries. These
- /// are the embedder's user-facing identity strings (e.g. the
- /// `"provider:model"` ref's parts), which can't be derived from the
- /// `ProviderConfig` snapshot (it only carries the `APIStyle` tag and
- /// the raw model string). Borrowed; lifetime owned by the embedder. The
- /// model defaults to the config snapshot's model when left empty.
- persist_provider: []const u8 = "",
- persist_model: []const u8 = "",
+ /// The session this agent appends to. Minted from the store at `init`
+ /// (fresh: `store.create()`) or supplied by the embedder on resume
+ /// (`resolve`/`latest`). `Session.append` proxies to the store and
+ /// updates the session's last-used wire identity. The embedder owns the
+ /// underlying store, which must outlive the agent.
+ session: session_store_mod.Session,
/// Injectable streaming seam. Defaults to the real provider dispatch
/// (`provider_mod.openStream`); tests override it with a stub.
open_stream_fn: provider_mod.OpenStreamFn = provider_mod.openStream,
@@ -239,7 +231,7 @@ pub const Agent = struct {
allocator: Allocator,
io: Io,
config: *const Config,
- store: session_store_mod.SessionStore,
+ session: session_store_mod.Session,
maybe_conversation: ?conversation.Conversation,
) Agent {
return .{
@@ -248,16 +240,18 @@ pub const Agent = struct {
.config = config,
.registry = ToolRegistry.init(allocator),
.conversation = maybe_conversation orelse conversation.Conversation.init(allocator),
- .session_store = store,
+ .session = session,
};
}
pub fn deinit(self: *Agent) void {
- // The agent owns the conversation and the tool registry; it borrows
- // the config snapshot and the session store, which the embedder
- // tears down.
+ // The agent owns the conversation, the tool registry, and the
+ // session handle's `info` (minted by `store.create()` or resolved
+ // by the embedder and handed in). It borrows the config snapshot
+ // and the underlying store, which the embedder tears down.
self.registry.deinit();
self.conversation.deinit();
+ self.session.info.deinit(self.allocator);
}
/// Add a single tool to this agent's tool set. Visible at the next turn.
@@ -271,22 +265,11 @@ pub const Agent = struct {
try self.registry.registerSource(src);
}
- /// The provider/model identity used to stamp persisted entries. Uses
- /// the embedder-supplied display names when set, falling back to the
- /// active config snapshot's model string (and the `APIStyle` tag name
- /// for the provider) when not.
- fn providerModel(self: *const Agent) struct { provider: []const u8, model: []const u8 } {
- const cfg_model = switch (self.config.provider) {
- inline else => |c| c.model,
- };
- const provider = if (self.persist_provider.len > 0)
- self.persist_provider
- else switch (self.config.provider) {
- .openai_chat => "openai_chat",
- .anthropic_messages => "anthropic_messages",
- };
- const model = if (self.persist_model.len > 0) self.persist_model else cfg_model;
- return .{ .provider = provider, .model = model };
+ /// The wire-format provider identity stamped on persisted entries,
+ /// derived from the active config snapshot. Ground truth: never a CLI
+ /// alias, never any `api_key` material.
+ fn wireIdentity(self: *const Agent) session_store_mod.WireIdentity {
+ return self.config.provider.wireIdentity();
}
/// Swap the active configuration snapshot. Takes effect at the start of
@@ -312,14 +295,13 @@ pub const Agent = struct {
.append => try self.conversation.addSystemMessage(text),
.replace => try self.conversation.replaceSystemMessage(text),
}
- const pm = self.providerModel();
try turn_persist.persistTurn(
self.allocator,
- self.session_store,
+ &self.session,
&self.conversation,
start,
- pm.provider,
- pm.model,
+ self.wireIdentity(),
+ &.{},
);
}
@@ -355,14 +337,13 @@ pub const Agent = struct {
// recovery guarantee).
const user_start = self.conversation.messages.items.len;
try self.conversation.addUserMessage(message.text);
- const pm = self.providerModel();
try turn_persist.persistTurn(
self.allocator,
- self.session_store,
+ &self.session,
&self.conversation,
user_start,
- pm.provider,
- pm.model,
+ self.wireIdentity(),
+ &.{},
);
const s = try self.allocator.create(Stream);
@@ -375,23 +356,23 @@ pub const Agent = struct {
/// `[system..., summary, kept-suffix...]`), so persist the whole
/// post-compaction window instead of `[start..]`.
fn persistTurnTail(self: *Agent, start: usize) !void {
- const pm = self.providerModel();
+ const id = self.wireIdentity();
if (self.auto_compacted) {
try turn_persist.persistCompaction(
self.allocator,
- self.session_store,
+ &self.session,
&self.conversation,
- pm.provider,
- pm.model,
+ id,
+ &.{},
);
} else {
try turn_persist.persistTurn(
self.allocator,
- self.session_store,
+ &self.session,
&self.conversation,
start,
- pm.provider,
- pm.model,
+ id,
+ &.{},
);
}
}
@@ -621,13 +602,12 @@ pub const Agent = struct {
) !CompactionResult {
const res = try self.compact(system_prompt, extra_instructions);
if (res.compacted) {
- const pm = self.providerModel();
try turn_persist.persistCompaction(
self.allocator,
- self.session_store,
+ &self.session,
&self.conversation,
- pm.provider,
- pm.model,
+ self.wireIdentity(),
+ &.{},
);
}
return res;
@@ -1798,59 +1778,81 @@ const HardFailTool = struct {
};
/// An in-memory `SessionStore` test double: records every appended
-/// `DiskMessage` (role + provider/model stamp) so tests can assert the
+/// `StoredMessage` (role + provider/model stamp) so tests can assert the
/// agent persisted the right turn without touching disk. Honors the store
/// ownership contract by freeing each consumed message after recording its
/// salient fields.
const CapturingStore = struct {
allocator: Allocator,
- roles: std.ArrayList(session_store_mod.DiskMessageRole) = .empty,
- providers: std.ArrayList(?[]const u8) = .empty,
+ roles: std.ArrayList(conversation.MessageRole) = .empty,
+ base_urls: std.ArrayList([]const u8) = .empty,
fn init(allocator: Allocator) CapturingStore {
return .{ .allocator = allocator };
}
fn deinit(self: *CapturingStore) void {
- for (self.providers.items) |p| if (p) |s| self.allocator.free(s);
- self.providers.deinit(self.allocator);
+ for (self.base_urls.items) |s| self.allocator.free(s);
+ self.base_urls.deinit(self.allocator);
self.roles.deinit(self.allocator);
}
- fn appendMessagesVT(
- ctx: *anyopaque,
- messages: []session_store_mod.DiskMessage,
- providers: []const ?[]const u8,
- _: []const ?[]const u8,
- ) anyerror!void {
+ fn createVT(ctx: *anyopaque) session_store_mod.Session {
const self: *CapturingStore = @ptrCast(@alignCast(ctx));
- for (messages, 0..) |m, i| {
- try self.roles.append(self.allocator, m.role);
- // Prefer the assistant message's own provider stamp, else the
- // entry-level provider stamp (user messages).
- const prov = m.provider orelse providers[i];
- const dup: ?[]const u8 = if (prov) |p| try self.allocator.dupe(u8, p) else null;
- try self.providers.append(self.allocator, dup);
- }
- // Contract: appends consume their messages.
- for (messages) |m| m.deinit(self.allocator);
+ const a = self.allocator;
+ const info: session_store_mod.SessionInfo = .{
+ .id = a.dupe(u8, "cap") catch "cap",
+ .created = a.dupe(u8, "") catch "",
+ .modified = a.dupe(u8, "") catch "",
+ .message_count = 0,
+ .last_user_message = a.dupe(u8, "") catch "",
+ .api_style = .openai_chat,
+ .base_url = a.dupe(u8, "") catch "",
+ .model = a.dupe(u8, "") catch "",
+ .reasoning = .default,
+ };
+ return .{ .info = info, .store = self.store() };
}
- fn loadConversationVT(_: *anyopaque, alloc: Allocator) anyerror!session_store_mod.LoadedSession {
- return .{ .conversation = conversation.Conversation.init(alloc), .dangling_user = null };
+ fn listVT(ctx: *anyopaque) anyerror![]session_store_mod.SessionInfo {
+ const self: *CapturingStore = @ptrCast(@alignCast(ctx));
+ return self.allocator.alloc(session_store_mod.SessionInfo, 0);
+ }
+ fn freeSessionInfosVT(ctx: *anyopaque, infos: []session_store_mod.SessionInfo) void {
+ const self: *CapturingStore = @ptrCast(@alignCast(ctx));
+ for (infos) |i| i.deinit(self.allocator);
+ self.allocator.free(infos);
}
- fn sessionIdVT(_: *anyopaque) []const u8 {
- return "cap";
+ fn resolveVT(_: *anyopaque, _: []const u8) anyerror!?session_store_mod.Session {
+ return null;
+ }
+ fn latestVT(_: *anyopaque) anyerror!?session_store_mod.Session {
+ return null;
}
- fn activeModelVT(_: *anyopaque) ?session_store_mod.ActiveModel {
+ fn loadVT(_: *anyopaque, _: []const u8) anyerror!?conversation.Conversation {
return null;
}
+ fn appendMessagesVT(
+ ctx: *anyopaque,
+ _: []const u8,
+ messages: []session_store_mod.PersistentMessage,
+ ) anyerror!void {
+ const self: *CapturingStore = @ptrCast(@alignCast(ctx));
+ for (messages) |m| {
+ try self.roles.append(self.allocator, m.message.role);
+ try self.base_urls.append(self.allocator, try self.allocator.dupe(u8, m.identity.base_url));
+ }
+ }
+
const vtable: session_store_mod.SessionStore.VTable = .{
+ .create = createVT,
+ .list = listVT,
+ .freeSessionInfos = freeSessionInfosVT,
+ .resolve = resolveVT,
+ .latest = latestVT,
+ .load = loadVT,
.appendMessages = appendMessagesVT,
- .loadConversation = loadConversationVT,
- .sessionId = sessionIdVT,
- .activeModel = activeModelVT,
};
fn store(self: *CapturingStore) session_store_mod.SessionStore {
@@ -1880,27 +1882,25 @@ test "agent persists user, assistant, and tool-result messages of a turn" {
var cap = CapturingStore.init(allocator);
defer cap.deinit();
- var agent = Agent.init(allocator, io, &h.config, cap.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, cap.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
- agent.persist_provider = "openai";
- agent.persist_model = "gpt-4o";
try drainTurn(&agent, "call a tool");
// Persisted, in order: user prompt, assistant(ToolUse), user(ToolResult),
// assistant(text).
try testing.expectEqual(@as(usize, 4), cap.roles.items.len);
- try testing.expectEqual(session_store_mod.DiskMessageRole.user, cap.roles.items[0]);
- try testing.expectEqual(session_store_mod.DiskMessageRole.assistant, cap.roles.items[1]);
- try testing.expectEqual(session_store_mod.DiskMessageRole.user, cap.roles.items[2]);
- try testing.expectEqual(session_store_mod.DiskMessageRole.assistant, cap.roles.items[3]);
+ try testing.expectEqual(conversation.MessageRole.user, cap.roles.items[0]);
+ try testing.expectEqual(conversation.MessageRole.assistant, cap.roles.items[1]);
+ try testing.expectEqual(conversation.MessageRole.user, cap.roles.items[2]);
+ try testing.expectEqual(conversation.MessageRole.assistant, cap.roles.items[3]);
- // The display provider stamp rode through on every entry.
- for (cap.providers.items) |p| {
- try testing.expect(p != null);
- try testing.expectEqualStrings("openai", p.?);
+ // The wire identity (base_url from the active config) rode through on
+ // every entry.
+ for (cap.base_urls.items) |b| {
+ try testing.expectEqualStrings("u", b);
}
}
@@ -1919,7 +1919,7 @@ test "agent runs a turn against NullStore without persisting or erroring" {
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2209,7 +2209,7 @@ test "runStep dispatches a tool call and loops to a final text turn" {
try h.registry.register(try EchoTool.create(allocator, "echo", "ECHO:"));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2260,7 +2260,7 @@ test "runStep dispatches multiple tool calls in parallel" {
try h.registry.register(try BarrierTool.create(allocator, "barrierC", &barrier));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2300,7 +2300,7 @@ test "runStep: native tool handler error becomes an error result and the model g
try h.registry.register(try FailingTool.create(allocator, "boom"));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2334,7 +2334,7 @@ test "runStep: unknown tool becomes an error tool result and the loop continues"
defer h.deinit();
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2367,7 +2367,7 @@ test "runStep with no tool calls returns after one provider step" {
defer h.deinit();
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2394,7 +2394,7 @@ test "runStep surfaces EmptyAssistantResponse when provider commits an empty mes
defer h.deinit();
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2425,7 +2425,7 @@ test "runStep delivers all source-backed calls in one batch on one thread" {
try h.registry.registerSource(try TestSource.create(allocator, "panto-lua", &.{ "lua_x", "lua_y" }));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2477,7 +2477,7 @@ test "runStep: distinct sources run on distinct threads in parallel" {
try h.registry.registerSource(try TestSource.create(allocator, "src_b", &.{"src_b_t"}));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2515,7 +2515,7 @@ test "runStep: source whole-batch error becomes per-call error results and conti
try h.registry.registerSource(try FailingSource.create(allocator, "fs", &.{ "fa", "fb" }));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2559,7 +2559,7 @@ test "runStep: mixed single Tools and source-backed tools coexist in one turn" {
try h.registry.registerSource(try TestSource.create(allocator, "src", &.{ "src_t1", "src_t2" }));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2599,7 +2599,7 @@ test "setConfig swaps provider between turns; agent tool set persists" {
};
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &cfg_a, ns.store(), null);
+ var agent = Agent.init(allocator, io, &cfg_a, ns.store().create(), null);
defer agent.deinit();
try agent.registerTool(try EchoTool.create(allocator, "late", "B:"));
agent.open_stream_fn = stub.install();
@@ -2638,7 +2638,7 @@ test "compact: summarizes prefix, keeps suffix, system survives" {
// adding the longer first turn exceeds it.
h.config.compaction = .{ .keep_verbatim = 10 };
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2701,7 +2701,7 @@ test "compact: restated suffix usage reconstructs a fresh cumulative chain" {
// summarizes the prefix.
h.config.compaction = .{ .keep_verbatim = 20 };
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2775,7 +2775,7 @@ test "compact: no-op when conversation already fits the budget" {
h.activate();
h.config.compaction = .{ .keep_verbatim = 1_000_000 };
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2813,7 +2813,7 @@ test "compact: extra instructions are appended to the system prompt" {
h.activate();
h.config.compaction = .{ .keep_verbatim = 1 };
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2854,7 +2854,7 @@ test "runStep: auto-compacts on context overflow and retries once" {
h.activate();
h.config.compaction = .{ .keep_verbatim = 10 };
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2899,7 +2899,7 @@ test "runStep: context overflow without compaction prompt propagates" {
defer h.deinit();
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -2971,7 +2971,7 @@ test "runStep: provider 429 retries then succeeds without duplicate messages" {
defer h.deinit();
fastRetryHarness(&h);
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -3015,7 +3015,7 @@ test "runStep: provider 500 retries with backoff notification" {
defer h.deinit();
fastRetryHarness(&h);
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -3053,7 +3053,7 @@ test "runStep: provider auth failure does not retry" {
defer h.deinit();
fastRetryHarness(&h);
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -3092,7 +3092,7 @@ test "runStep: retries exhaust and hard-fail after max_attempts" {
defer h.deinit();
fastRetryHarness(&h);
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -3130,7 +3130,7 @@ test "runStep: Retry-After is honored and reported" {
// Cap below the Retry-After to verify the policy cap applies.
h.config.retry = .{ .initial_delay_ms = 0, .max_delay_ms = 1, .jitter = false };
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -3165,7 +3165,7 @@ test "runStep: cancellation from a tool still hard-fails" {
try h.registry.register(try HardFailTool.create(allocator, "hard"));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -3196,7 +3196,7 @@ test "runStep: source per-call error produces a per-call error result and contin
try h.registry.registerSource(try PartialSource.create(allocator, "ps", &.{ "pa", "pb" }));
h.activate();
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();
@@ -3235,7 +3235,7 @@ test "runStep: context-overflow compaction fires a compaction retry notification
h.activate();
h.config.compaction = .{ .keep_verbatim = 10 };
var ns = null_store_mod.NullStore.init(allocator);
- var agent = Agent.init(allocator, io, &h.config, ns.store(), null);
+ var agent = Agent.init(allocator, io, &h.config, ns.store().create(), null);
defer agent.deinit();
h.seedInto(&agent);
agent.open_stream_fn = stub.install();