diff --git a/src/agent/Agent.zig b/src/agent/Agent.zig index 4abe75e0..8bb5ba80 100644 --- a/src/agent/Agent.zig +++ b/src/agent/Agent.zig @@ -4,15 +4,16 @@ const lp = @import("lightpanda"); const log = lp.log; const Config = lp.Config; -const App = @import("../App.zig"); -const ToolExecutor = @import("ToolExecutor.zig"); -const Terminal = @import("Terminal.zig"); +const script = lp.script; const Command = lp.script.Command; const Recorder = lp.script.Recorder; const Verifier = lp.script.Verifier; + +const App = @import("../App.zig"); +const ToolExecutor = @import("ToolExecutor.zig"); +const Terminal = @import("Terminal.zig"); const CommandExecutor = @import("CommandExecutor.zig"); const SlashCommand = @import("SlashCommand.zig"); -const script = lp.script; const Agent = @This(); diff --git a/src/browser/tools.zig b/src/browser/tools.zig index 1769ce24..7f031a5f 100644 --- a/src/browser/tools.zig +++ b/src/browser/tools.zig @@ -369,30 +369,17 @@ const ActionTarget = union(enum) { const NodeAndPage = struct { node: *DOMNode, page: *lp.Frame, target: ActionTarget }; -pub const Action = enum { - goto, - search, - markdown, - links, - nodeDetails, - interactiveElements, - structuredData, - detectForms, - eval, - tree, - click, - fill, - scroll, - waitForSelector, - hover, - press, - selectOption, - setChecked, - findElement, - getEnv, - consoleLogs, - getUrl, - getCookies, +/// Derived from `tool_defs` so the enum and the tool table can't drift. +/// Tag order follows declaration order in `tool_defs`. +pub const Action = blk: { + var fields: [tool_defs.len]std.builtin.Type.EnumField = undefined; + for (tool_defs, 0..) |td, i| fields[i] = .{ .name = td.name[0..td.name.len :0], .value = i }; + break :blk @Type(.{ .@"enum" = .{ + .tag_type = u8, + .fields = &fields, + .decls = &.{}, + .is_exhaustive = true, + } }); }; pub fn call( diff --git a/src/script/Verifier.zig b/src/script/Verifier.zig index bdf4cf30..aa14e376 100644 --- a/src/script/Verifier.zig +++ b/src/script/Verifier.zig @@ -46,24 +46,30 @@ fn verifyFill(self: *Self, arena: std.mem.Allocator, selector: []const u8, expec }; return .{ .result = .passed }; } - return self.verifyElementValue(arena, selector, "value", expected_value, "value"); + return self.verifyElementValue(arena, selector, .{ .js_property = "value", .expected = expected_value, .label = "value" }); } fn verifyCheck(self: *Self, arena: std.mem.Allocator, selector: []const u8, expected: bool) VerifyResult { const expected_str: []const u8 = if (expected) "true" else "false"; - return self.verifyElementValue(arena, selector, "String(el.checked)", expected_str, "checked state"); + return self.verifyElementValue(arena, selector, .{ .js_property = "String(el.checked)", .expected = expected_str, .label = "checked state" }); } fn verifySelect(self: *Self, arena: std.mem.Allocator, selector: []const u8, expected_value: []const u8) VerifyResult { - return self.verifyElementValue(arena, selector, "value", expected_value, "selected value"); + return self.verifyElementValue(arena, selector, .{ .js_property = "value", .expected = expected_value, .label = "selected value" }); } -fn verifyElementValue(self: *Self, arena: std.mem.Allocator, selector: []const u8, js_property: []const u8, expected: []const u8, label: []const u8) VerifyResult { - const actual = self.queryElementProperty(arena, selector, js_property) orelse return .{ .result = .inconclusive }; - if (!std.mem.eql(u8, actual, expected)) +const Check = struct { + js_property: []const u8, + expected: []const u8, + label: []const u8, +}; + +fn verifyElementValue(self: *Self, arena: std.mem.Allocator, selector: []const u8, check: Check) VerifyResult { + const actual = self.queryElementProperty(arena, selector, check.js_property) orelse return .{ .result = .inconclusive }; + if (!std.mem.eql(u8, actual, check.expected)) return .{ .result = .failed, - .reason = std.fmt.allocPrint(arena, "element {s} is \"{s}\" (expected \"{s}\")", .{ label, actual, expected }) catch null, + .reason = std.fmt.allocPrint(arena, "element {s} is \"{s}\" (expected \"{s}\")", .{ check.label, actual, check.expected }) catch null, }; return .{ .result = .passed }; }