diff --git a/src/agent.zig b/src/agent.zig index d9f555ca..69d646c4 100644 --- a/src/agent.zig +++ b/src/agent.zig @@ -6,8 +6,6 @@ pub const CommandExecutor = @import("agent/CommandExecutor.zig"); pub const Recorder = @import("agent/Recorder.zig"); pub const Verifier = @import("agent/Verifier.zig"); pub const SlashCommand = @import("agent/SlashCommand.zig"); -pub const autoDetectProvider = Agent.autoDetectProvider; -pub const listModels = Agent.listModels; test { _ = Agent; diff --git a/src/agent/Agent.zig b/src/agent/Agent.zig index fc304b85..35bec6c6 100644 --- a/src/agent/Agent.zig +++ b/src/agent/Agent.zig @@ -98,11 +98,7 @@ verifier: Verifier, recorder: Recorder, messages: std.ArrayList(zenai.provider.Message), message_arena: std.heap.ArenaAllocator, -model: []const u8, -/// When non-null, `model` aliases this heap buffer (allocated by --pick-model) -/// and `deinit` must free it. When null, `model` aliases an arg-parser slice -/// or a comptime literal (`zenai.provider.defaultModel`) and we don't own it. -model_owned: ?[]u8, +model: []u8, system_prompt: []const u8, script_file: ?[]const u8, self_heal: bool, @@ -145,18 +141,15 @@ pub fn init(allocator: std.mem.Allocator, app: *App, opts: Config.Agent) !*Self // Resolve model BEFORE the heavy init so --pick-model's prompt fires // before tool_executor / ai_client setup. // Precedence: --model > --pick-model > defaultModel. - var model_owned: ?[]u8 = null; - errdefer if (model_owned) |buf| allocator.free(buf); - const model: []const u8 = if (opts.model) |m| - m - else if (opts.pick_model and effective_provider != null and api_key != null) blk: { - const picked = try pickModel(allocator, effective_provider.?, api_key.?, opts.base_url); - model_owned = picked; - break :blk picked; - } else if (effective_provider) |p| - zenai.provider.defaultModel(p) + const model: []u8 = if (opts.pick_model and effective_provider != null and api_key != null) + try pickModel(allocator, effective_provider.?, api_key.?, opts.base_url) + else if (opts.model) |m| + try allocator.dupe(u8, m) + else if (effective_provider) |p| + try allocator.dupe(u8, zenai.provider.defaultModel(p)) else - ""; + try allocator.dupe(u8, ""); + errdefer allocator.free(model); const tool_executor: *ToolExecutor = try .init(allocator, app); errdefer tool_executor.deinit(); @@ -209,7 +202,6 @@ pub fn init(allocator: std.mem.Allocator, app: *App, opts: Config.Agent) !*Self .messages = .empty, .message_arena = .init(allocator), .model = model, - .model_owned = model_owned, .system_prompt = opts.system_prompt orelse default_system_prompt, .script_file = opts.script_file, .self_heal = opts.self_heal, @@ -240,7 +232,7 @@ pub fn deinit(self: *Self) void { }, } } - if (self.model_owned) |buf| self.allocator.free(buf); + self.allocator.free(self.model); self.allocator.destroy(self); } @@ -961,8 +953,7 @@ fn resolveApiKey(provider: ?Config.AiProvider, needs_llm: bool) !?[:0]const u8 { /// One-shot for `--list-models`: resolve the provider (explicit, then env /// auto-detect), reject `--no-llm`, fetch chat-capable model IDs from the -/// provider, and print them to stdout (sorted, one per line). Shares -/// provider-resolution logic with `init` so the two paths can't drift. +/// provider, and print them to stdout (one per line). pub fn listModels(allocator: std.mem.Allocator, opts: Config.Agent) !void { if (opts.no_llm) { log.fatal(.app, "list-models needs LLM", .{ diff --git a/src/main.zig b/src/main.zig index 92de75c8..499f2f2c 100644 --- a/src/main.zig +++ b/src/main.zig @@ -64,7 +64,7 @@ fn run(allocator: Allocator, main_arena: Allocator) !void { return std.process.cleanExit(); }, .agent => |opts| if (opts.list_models) { - try lp.agent.listModels(allocator, opts); + try lp.agent.Agent.listModels(allocator, opts); return std.process.cleanExit(); }, else => {}, diff --git a/src/mcp/Server.zig b/src/mcp/Server.zig index da69db89..b955401b 100644 --- a/src/mcp/Server.zig +++ b/src/mcp/Server.zig @@ -28,8 +28,7 @@ transport: Transport, /// Optional PandaScript recorder. Activated by the `record_start` tool; /// cleared by `record_stop`. State-mutating browser tool calls are -/// serialized into the active recorder via `Command.fromToolCall`. The -/// Recorder owns its path string and line counter — see `agent/Recorder.zig`. +/// serialized into the active recorder via `Command.fromToolCall`. recorder: ?Recorder = null, pub fn init(allocator: std.mem.Allocator, app: *App, writer: *std.io.Writer) !*Self {