From df5fbf4bc39d9a68eda67896c32df5b6a36deddc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Arrufat?= Date: Mon, 25 May 2026 17:33:08 +0200 Subject: [PATCH] command: require locator for null args in isRecorded Tool calls like click or hover with null arguments are unreplayable and should not be considered recorded, even if they have zero required fields. --- src/script/command.zig | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/script/command.zig b/src/script/command.zig index b25a5c85..6c86d252 100644 --- a/src/script/command.zig +++ b/src/script/command.zig @@ -58,7 +58,7 @@ pub const Command = union(enum) { fn isRecorded(self: ToolCall) bool { if (!self.tool.isRecorded()) return false; const s = self.schema(); - const args = self.args orelse return s.required.len == 0; + const args = self.args orelse return s.required.len == 0 and !self.tool.needsLocator(); if (args != .object) return !self.tool.needsLocator(); const has_selector = args.object.contains("selector"); @@ -358,10 +358,11 @@ test "isRecorded: args shape and locator semantics" { defer arena.deinit(); const aa = arena.allocator(); - // Null args: recorded iff the tool has zero required fields. A provider - // that hands back `arguments: null` for `/click` would otherwise produce - // a bare `/click` line that can't be replayed. - try testing.expect(Command.fromToolCall(.click, null).isRecorded()); + // Null args: recorded iff the tool has zero required fields AND doesn't + // need a locator. `/click` with null args is unreplayable — no selector, + // no backendNodeId — even though click's schema has zero required fields. + try testing.expect(!Command.fromToolCall(.click, null).isRecorded()); + try testing.expect(!Command.fromToolCall(.hover, null).isRecorded()); try testing.expect(!Command.fromToolCall(.goto, null).isRecorded()); try testing.expect(!Command.fromToolCall(.fill, null).isRecorded());