From b3cd77b4b444c2452b357b60e60710e2eabe2d95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Tue, 5 May 2026 09:44:56 +0200 Subject: [PATCH] agent: unify LLM turn execution into runTurn --- src/agent/Agent.zig | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/agent/Agent.zig b/src/agent/Agent.zig index 88d8d419..ef9bc39a 100644 --- a/src/agent/Agent.zig +++ b/src/agent/Agent.zig @@ -264,7 +264,7 @@ pub fn deinit(self: *Self) void { /// Returns true on success. pub fn run(self: *Self) bool { - if (self.one_shot_task) |task| return self.runOneShot(task); + if (self.one_shot_task) |task| return self.runTurn(task, null, "Request"); if (self.script_file) |path| { const script_ok = self.runScript(path); if (!self.interactive) return script_ok; @@ -273,16 +273,14 @@ pub fn run(self: *Self) bool { return true; } -/// Final answer goes to stdout; tool calls and errors go to stderr, -/// so the caller can pipe stdout to capture a clean answer. -fn runOneShot(self: *Self, task: []const u8) bool { - const text = self.processUserMessage(task, null) catch |err| switch (err) { - error.UnsupportedAttachment, error.AttachmentReadFailed => { - // Already logged in buildUserMessageParts with detail. - return false; - }, +/// Final answer goes to stdout; errors go to stderr, so a caller can +/// pipe stdout to capture a clean answer. +fn runTurn(self: *Self, prompt: []const u8, record_comment: ?[]const u8, label: []const u8) bool { + const text = self.processUserMessage(prompt, record_comment) catch |err| switch (err) { + // buildUserMessageParts has already logged the detail. + error.UnsupportedAttachment, error.AttachmentReadFailed => return false, else => { - self.terminal.printErrorFmt("Request failed: {s}", .{@errorName(err)}); + self.terminal.printErrorFmt("{s} failed: {s}", .{ label, @errorName(err) }); return false; }, }; @@ -320,9 +318,9 @@ fn runRepl(self: *Self) void { switch (cmd) { .comment => continue :repl, - .login => self.runLlmTurnPrint(login_prompt, line, "LOGIN"), - .accept_cookies => self.runLlmTurnPrint(accept_cookies_prompt, line, "ACCEPT_COOKIES"), - .natural_language => self.runLlmTurnPrint(line, line, "Request"), + .login => _ = self.runTurn(login_prompt, line, "LOGIN"), + .accept_cookies => _ = self.runTurn(accept_cookies_prompt, line, "ACCEPT_COOKIES"), + .natural_language => _ = self.runTurn(line, line, "Request"), else => { self.cmd_executor.execute(cmd); self.recorder.record(cmd); @@ -853,17 +851,6 @@ pub fn runOneTask( return self.processUserMessage(task, null); } -/// REPL helper: run an LLM turn and route the answer to the terminal, -/// reporting failures with `label` ("LOGIN", "Request", ...). Errors are -/// swallowed — the REPL must not die from a single failed turn. -fn runLlmTurnPrint(self: *Self, prompt: []const u8, record_comment: ?[]const u8, label: []const u8) void { - const text = self.processUserMessage(prompt, record_comment) catch |err| { - self.terminal.printErrorFmt("{s} failed: {s}", .{ label, @errorName(err) }); - return; - }; - if (text) |t| self.terminal.printAssistant(t) else self.terminal.printInfo("(no response from model)"); -} - /// Returned text lives in `message_arena`, so it's only valid until the /// next prune. `null` means the model emitted nothing even after the /// synthesis turn.