Skip to content

fix: repair malformed JSON in model-written tool arguments - #62

Merged
elkaix merged 4 commits into
mainfrom
feat/tool-args-repair
Aug 12, 2026
Merged

fix: repair malformed JSON in model-written tool arguments#62
elkaix merged 4 commits into
mainfrom
feat/tool-args-repair

Conversation

@elkaix

@elkaix elkaix commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

Model-written tool arguments sometimes fail strict JSON.parse and kill the tool call at preflight. Two failure classes were observed in real Dynamic Workflow launches:

  • Invalid escape sequences — markdown-style escapes such as \* inside JSON string values (Bad escaped character at position 3458).
  • Unescaped inner quotes — a literal " inside a string value that terminates it early (Expected ',' or ']' after array element in JSON at position 2832).

Both now repair instead of failing the call. The repair runs only as a fallback after JSON.parse rejects the input, and the original parse error is still reported when the repaired text also fails to parse.

Approach

A single scanner pass rewrites invalid escapes to a literal backslash plus character, and rewrites an in-string " to \" unless the next non-whitespace character is structural (, : } ]) or end of input. A content quote immediately followed by a structural character stays ambiguous and still closes the string; that limit is documented in the function comment.

Test plan

  • packages/agent-core/test/loop/tool-call.e2e.test.ts — 45 tests, including a reproduction of the observed items-array failure, mixed-class repair, whitespace before the terminator, and preservation of the original error on still-broken input.
  • Full suite green locally.

Summary by CodeRabbit

  • Bug Fixes

    • Improved handling of malformed tool arguments, including invalid escape sequences and unescaped quotation marks.
    • Preserves valid escapes while repairing recoverable formatting issues.
    • Provides clearer errors when arguments remain structurally invalid.
  • Tests

    • Added coverage for malformed escapes, embedded quotes, combined errors, valid escapes, and invalid JSON structures.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: adaddcf9-74da-46f7-8762-a81708575d16

📥 Commits

Reviewing files that changed from the base of the PR and between 83663b6 and 8ef6d30.

📒 Files selected for processing (1)
  • packages/agent-core/test/loop/tool-call.e2e.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/agent-core/test/loop/tool-call.e2e.test.ts

📝 Walkthrough

Walkthrough

parseToolCallArguments now repairs selected malformed JSON string escapes and quotes before retrying parsing. Tests cover repaired arguments, preserved valid escapes, and structurally invalid input.

Changes

Tool argument repair

Layer / File(s) Summary
Parser repair implementation
packages/agent-core/src/loop/tool-call.ts, .changeset/tool-args-escape-repair.md
The parser repairs invalid string escapes and ambiguous interior quotes, then retries JSON parsing. The changeset documents the patch.
Parser repair validation
packages/agent-core/test/loop/tool-call.e2e.test.ts
Tests cover malformed escapes, unescaped quotes, valid escapes, and invalid syntax that remains unrepaired.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title uses the fix: conventional-commit prefix, imperative mood, and 58 characters, which is within the 72-character limit.
Description check ✅ Passed The description clearly explains the problem, implementation approach, limitations, and test plan, although it does not use the repository template headings.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@pkg-pr-new

pkg-pr-new Bot commented Aug 12, 2026

Copy link
Copy Markdown
pnpm dlx https://pkg.pr.new/@pythoughts/pythinker-code@8ef6d30
npx https://pkg.pr.new/@pythoughts/pythinker-code@8ef6d30

commit: 8ef6d30

@elkaix
elkaix force-pushed the feat/tui-signature-design branch from 5d80ea7 to 525b3db Compare August 12, 2026 04:39
@elkaix
elkaix force-pushed the feat/tool-args-repair branch from e498ea7 to 4bde753 Compare August 12, 2026 04:39
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from 525b3db to de67ad5 Compare August 12, 2026 04:45
@elkaix
elkaix force-pushed the feat/tool-args-repair branch from 4bde753 to 1a632b2 Compare August 12, 2026 05:15
@elkaix
elkaix force-pushed the feat/tui-signature-design branch from de67ad5 to eade981 Compare August 12, 2026 05:15
@elkaix
elkaix force-pushed the feat/tool-args-repair branch from 1a632b2 to ea8b00e Compare August 12, 2026 05:36
@elkaix
elkaix force-pushed the feat/tui-signature-design branch 2 times, most recently from 4b47377 to 98a175d Compare August 12, 2026 05:59
@elkaix
elkaix force-pushed the feat/tool-args-repair branch from ea8b00e to 41902da Compare August 12, 2026 05:59
@elkaix
elkaix force-pushed the feat/tui-signature-design branch 2 times, most recently from bf66a7c to ebfb674 Compare August 12, 2026 06:30
Base automatically changed from feat/tui-signature-design to main August 12, 2026 07:13
@elkaix
elkaix force-pushed the feat/tool-args-repair branch from 41902da to 1807f63 Compare August 12, 2026 07:16
The helper throws when its input parses cleanly, so the two error
assertions cannot silently compare against an empty string.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/agent-core/test/loop/tool-call.e2e.test.ts`:
- Around line 179-183: Update the test case for parseToolCallArguments to use
input containing both an invalid in-string escape and a separate structural
error, such as {"a":"bad \\*","b":[}, so repairInvalidStringEscapes is attempted
but reparsing still fails. Keep the assertion focused on returning the original
parseErrorMessage for the unmodified input.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 2871e31a-253c-4d0b-abd1-d831ae342719

📥 Commits

Reviewing files that changed from the base of the PR and between 065bf2e and 83663b6.

📒 Files selected for processing (3)
  • .changeset/tool-args-escape-repair.md
  • packages/agent-core/src/loop/tool-call.ts
  • packages/agent-core/test/loop/tool-call.e2e.test.ts

Comment thread packages/agent-core/test/loop/tool-call.e2e.test.ts
The previous input never triggered a repair, so the fallback that
reports the original error after a failed reparse was untested.
@elkaix
elkaix merged commit 7fc36fd into main Aug 12, 2026
12 checks passed
@elkaix
elkaix deleted the feat/tool-args-repair branch August 12, 2026 07:54
elkaix pushed a commit that referenced this pull request Aug 12, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @pythoughts/pythinker-code@0.16.0

### Minor Changes

- [#59](#59)
[`6999b68`](6999b68)
- Add an opt-in advisor: a second model reviews the conversation after a
completed user turn unless another review is already running, and its
notes appear as an `<advisory>` block in the agent's next turn; enable
with `[advisor] enabled = true` plus an advisor model (the `advisor`
model role or `[advisor] model`), and it runs only when the advisor
shares the session model's provider.

- [#56](#56)
[`b71f094`](b71f094)
- Add model roles: lock a model alias to the small, implementer, or
advisor slot with `/model <role>`, list assignments with `/model roles`,
and reference roles as `@small`, `@implementer`, or `@advisor` wherever
a subagent model can be set; an assigned implementer role becomes the
default model for subagents.

- [#57](#57)
[`99c427c`](99c427c)
- Show what the agent is doing in the working indicator: eligible tool
calls whose input schema accepts the injected field now carry a short
model-written intent, streamed live into the spinner label (for example
"check failing test…") instead of a rotating placeholder; disable with
`PYTHINKER_CODE_EXPERIMENTAL_TOOL_INTENT=0`.

- [#58](#58)
[`065bf2e`](065bf2e)
- Redesign core TUI surfaces: tool cards get state-tinted backgrounds
with three new theme tokens, a status bar with a per-session accent
color appears between the input box and footer, and the prompt box uses
a neutral border while permission mode appears in the status bar. The
working-label shimmer uses a calmer constant-velocity sweep with
alternating mission-control highlights.

### Patch Changes

- [#62](#62)
[`7fc36fd`](7fc36fd)
- Repair invalid escape sequences and unescaped quotes in model-written
tool arguments instead of failing the tool call.

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant