From df42d18d6d60f77adf48adaa323d14a2a1a49215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Fri, 22 May 2026 09:09:19 +0200 Subject: [PATCH] command: check required fields for null-arg tool calls Tool calls with null arguments are now only considered recorded if the tool schema has no required fields. This prevents generating invalid, unreplayable commands for tools like `goto` or `fill`. --- src/script/command.zig | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/script/command.zig b/src/script/command.zig index 64063f2e..c9776973 100644 --- a/src/script/command.zig +++ b/src/script/command.zig @@ -56,9 +56,9 @@ pub const Command = union(enum) { .tool_call => |tc| blk: { const s = schemaOf(tc); if (!s.recorded) break :blk false; + const args = tc.args orelse break :blk s.required.len == 0; // backendNodeId is invalidated by any DOM mutation, so calls // using it aren't replayable. - const args = tc.args orelse break :blk true; if (args == .object and args.object.contains("backendNodeId")) break :blk false; break :blk true; }, @@ -500,6 +500,17 @@ test "isRecorded / canHeal / producesData via tool flags" { try testing.expect(!login.canHeal()); } +test "isRecorded: null args on a required-fields tool are not recorded" { + // A provider that hands back `arguments: null` for `/click` would + // otherwise produce a bare `/click` line that can't be replayed. + const click_null = Command.fromToolCall(.click, null); + try testing.expect(click_null.isRecorded()); // click has zero required fields + const goto_null = Command.fromToolCall(.goto, null); + try testing.expect(!goto_null.isRecorded()); // goto requires url + const fill_null = Command.fromToolCall(.fill, null); + try testing.expect(!fill_null.isRecorded()); // fill requires value +} + test "ScriptIterator: basic slash commands" { const content = "/goto https://example.com\n" ++