fix(tools): coerce stringified array params in tool calls#49
Merged
Conversation
Native tool calls deliver batch params (read_file/list_files `paths`, execute_command `commands`, get_function/get_file_skeleton/search_files/ diagnostics_scan/rename_symbol/find_symbol_references `paths`/`symbols`) as real arrays. Intermittently an array argument arrives as its JSON-stringified form, e.g. the string '["README.md"]'. Handlers then fell into the "not an array -> wrap as [value]" branch and treated the literal bracketed text as a single path/command, causing ENOENT on a bracketed path and no-op or failing shell commands, which spun the agent into a retry loop. Add coerceToStringArray() / coerceFirstStringArray() and route every string-array batch param through them: JSON-stringified arrays are parsed back into real arrays, plain strings are wrapped, nullish/empty values yield []. coerceFirstStringArray preserves the singular fallback (path/symbol) where handlers support it. Object-array params (replace_symbol `replacements`, edit_file `files`) are intentionally left untouched. Covers 9 handlers. 12 unit tests for the helpers; verified end-to-end via isaac (list_files + read_file now receive real arrays, no retry loop).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
ISAAC's native function-calling uses batch/plural array params —
read_file/list_filestakepaths,execute_commandtakescommands, and the symbol/skeleton/search/diagnostics tools takepaths/symbols. Intermittently the worker/relay delivers an array argument as its JSON-stringified form (the literal string'["README.md"]'instead of the array["README.md"]).The handlers' old logic
Array.isArray(x) ? x : (x ? [x] : [])then wrapped the whole bracketed string as a single path/command:→ ENOENT on a bracketed path, no-op/failing shell commands, and an agent retry loop that never advances.
Fix
New helper
src/core/task/tools/utils/coerceArray.ts:coerceToStringArray(value)— real arrays pass through (membersString()-ified); a string that trims to[...]isJSON.parsed back into an array (falling back to single-value wrap on parse failure / non-array); other non-empty strings wrap to[value]; nullish/empty →[].coerceFirstStringArray(...candidates)— returns the first non-empty coercion, preserving the singularpath/symbolfallback where handlers support it.Routed 9 handlers through the helpers:
ReadFile,ListFiles,ExecuteCommand,GetFunction,GetFileSkeleton,DiagnosticsScan,RenameSymbol,FindSymbolReferences,SearchFiles. TheExecuteCommandarray-handling was simplified to a uniformfor…of(rawCommands is now alwaysstring[]).Object-array params (
replace_symbolreplacements,edit_filefiles) are intentionally left untouched — they are not string arrays.Verification
check-types✅,biome lint✅ (1563 files)mochafull suite ✅ 1366 passing / 0 failing (12 new helper tests)isaac:list_files→paths:["."]andread_file→paths:["README.md"]now receive real arrays, task completes with no retry loop.🤖 Generated with Claude Code