diff options
| author | t <t@tjp.lol> | 2026-06-07 11:35:49 -0600 |
|---|---|---|
| committer | t <t@tjp.lol> | 2026-06-07 11:35:49 -0600 |
| commit | b14859b9726185ab873356390068e887b7f486d3 (patch) | |
| tree | 6530d8ee62e7639b50c99a67d23f89e25ef572dc /libpanto/src/agent.zig | |
| parent | d36a51358efbc48de3d5b732727455efc60acae1 (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.zig | 240 |
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(); |
