pi-tool-repair

Validate-then-repair extension for pi — fixes common LLM tool-call mistakes (null fields, stringified arrays, wrong field names, anchor bleed) before tools execute

Packages

Package details

extension

Install pi-tool-repair from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:pi-tool-repair
Package
pi-tool-repair
Version
0.1.1
Published
Jun 4, 2026
Downloads
not available
Author
monotykamary
License
MIT
Types
extension
Size
83.9 KB
Dependencies
0 dependencies · 0 peers
Pi manifest JSON
{
  "extensions": [
    "./tool-repair.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

🔧 pi-tool-repair

Validate-then-repair for pi

Fixes the finite set of tool-call mistakes open models make — before tools execute.

pi extension license


Open models aren't bad at tool calling — the harness is.

By adding a thin repair layer, DeepSeek V4 Pro beat Opus 4.7 in 6/10 internal evals — without changing the model. The same four mistakes repeat across DeepSeek, GLM, Qwen, and others. Each fix is 30–100 lines. Order matters.

Reverse-engineered from Command Code's tool parsing pipeline.

What it fixes

Problem Model sends After repair
null for optional fields {"path": "/foo", "offset": null} {"path": "/foo"}
Arrays as JSON strings "[\"a\",\"b\"]" ["a","b"]
{} where array expected {"include": {}} (dropped)
Bare string → array "foo" ["foo"]
Wrong field names {"file_path": "/foo"} {"path": "/foo"}
Bare string as root input "/path/to/file" {"path": "/path/to/file"}
Schema anchor bleed (Kimi K2) "^pattern$" in values "pattern"
Leaked tool grammar (opt-in) <|DSML|tool_calls>... pi toolCall block

Install

With pi install (recommended):

pi install https://github.com/monotykamary/pi-tool-repair

With npm:

npm install pi-tool-repair

Manual — add to ~/.pi/agent/settings.json:

{
  "packages": ["git:github.com/monotykamary/pi-tool-repair"]
}

Local development — add the extension path directly:

{
  "extensions": ["./path/to/pi-tool-repair/tool-repair.ts"]
}

Reload with /reload after any install method.

How it works

┌────────────────────────────────────────────────────────────┐
│ Phase 0: Schema poisoning (before_provider_request)        │
│                                                            │
│ Strip regex anchors from JSON Schema patterns for models   │
│ where they leak into generated values (Kimi K2, MiniMax)   │
│                                                            │
│ Fixes what YOU send the model — not what the model sends   │
└──────────────────────────┬─────────────────────────────────┘
                           │
                           ▼
               Model generates tool call
                           │
                           ▼
┌────────────────────────────────────────────────────────────┐
│ Phase 1: Grammar leak repair (message_end, opt-in)         │
│                                                            │
│ Detect raw XML/sentinel tool grammars emitted as text or   │
│ thinking, strip them from visible output, and recover them │
│ as pi toolCall blocks when complete and safe.              │
└──────────────────────────┬─────────────────────────────────┘
                           │
                           ▼
┌────────────────────────────────────────────────────────────┐
│ Phase 2: Validate-then-repair (tool_call)                  │
│                                                            │
│  1. Validate input against schema (if known tool)          │
│   ↳ Valid? Ship it untouched.                              │
│  2. Walk the validator's issue list                        │
│   ↳ Apply targeted repairs only at the exact failed paths  │
│  3. Re-validate the repaired input                         │
│   ↳ Still invalid? Let the tool handle it.                 │
│  4. Log outcome (debug mode)                               │
└────────────────────────────────────────────────────────────┘

Repair rules (in order)

Order matters — parseJsonStringifiedArray must run before wrapBareStringAsArray or you get double-wrapping.

# Rule What it catches
1 renameAliasedField file_pathpath, querypattern, etc.
2 dropNullOrUndefined null/undefined for optional fields
3 dropEmptyObjectPlaceholder {} where array expected
4 parseJsonStringifiedArray "[\"a\",\"b\"]"["a","b"]
5 wrapBareStringAsArray "foo"["foo"]
6 wrapRootStringAsObject "/path"{"path": "/path"}

Why validate-then-repair (not preprocess-then-validate)

Preprocessing inputs before validation silently corrupts valid data — rewriting file content that happened to look like JSON, for example. The better design:

  1. Parse the input as-is. If valid, ship it untouched.
  2. On failure, walk the validator's issue list and apply repairs only at the exact paths that failed.
  3. Re-validate. The schema localizes the bug for you — you only spend repair effort where it's actually needed.

Configuration

Grammar leak repair (disabled by default)

Raw XML/sentinel tool-call grammar recovery is opt-in because it can turn assistant text into tool execution. Enable it in ~/.pi/agent/extensions/pi-tool-repair.json:

{
  "grammarRepair": {
    "enabled": true,
    "mode": "recover",
    "requireKnownTool": true,
    "grammars": [
      "dsml",
      "invoke",
      "qwen",
      "kimi",
      "mistral",
      "llama",
      "glm",
      "granite",
      "minimax-text",
      "olmo"
    ]
  }
}

Modes:

Mode Behavior
recover Strip leaked markup and append recovered pi toolCall blocks.
strip Strip leaked markup only; do not execute recovered calls.

Safety gates:

  • requireKnownTool: true only recovers calls whose name is in pi's active tool registry.
  • Markup inside fenced code blocks is ignored so syntax discussions and examples are preserved.
  • Incomplete or unparseable blocks are left alone.
  • If the provider already emitted native toolCall blocks, leaked shadow text is stripped but duplicate calls are not added.

Covered grammar families: DeepSeek DSML, MiniMax/Anthropic <invoke>, Qwen/Hermes <tool_call>, Kimi sentinels, Mistral [TOOL_CALLS], Llama <|python_tag|>, GLM arg_key/arg_value, Granite JSON <tool_call>, MiniMax-Text-01 TypeScript calls, and OLMo3 <function_calls> pythonic calls. See docs/tool-call-grammar-leakage-survey.md for the survey.

Debug logging

Set PI_TOOL_REPAIR_DEBUG=1 or grammarRepair.debug: true to log repair diagnostics to stderr:

[pi-tool-repair] tool=read outcome=recovered rules=dropNullOrUndefined hints=1
  input: {"path":"/foo","offset":null}
  repaired: {"path":"/foo"}
  hint[0]: Dropped null `offset` from tool "read"...

Covered tools

Repair rules apply to pi's built-in tools: read, write, edit, bash, grep, find, ls.

Anchor bleed models

Phase 0 schema sanitization activates for models matching these patterns:

Pattern Models
/kimi-k2/i Kimi K2 variants
/minimax/i MiniMax variants
/glm/i GLM variants

To add more models, edit anchorBleedModels in src/index.ts.

Field aliases

The extension maps common model mistakes (wrong field names) to the canonical field name. For example, when calling read, the model can send file_path, absolutePath, filepath, target_file, etc. — all map to path.

Tool Canonical Aliases
read path absolutePath, file_path, filePath, filepath, pathname, target_file, targetFile, file, absolute_path, fileAbsolutePath
grep pattern query, regex, search, q, expression, text
write path absolutePath, file_path, filePath, filepath, pathname, target_file, targetFile
write content text, body, data, contents, fileContent
edit path absolutePath, file_path, filePath, filepath, pathname, target_file, targetFile
edit oldText old_string, oldString, old, old_str, oldStr, from, old_value, oldText, old_text, oldContent, old_content
edit newText new_string, newString, new, new_str, newStr, to, new_value, newText, new_text, newContent, new_content
ls path absolutePath, directory, dir, folder, directoryPath
find pattern query, glob, expression, search, include
bash command cmd, shell, script, commandLine

Development

npm install
npm test              # run tests
npm run test:watch    # watch mode
npm run test:coverage # coverage report
npm run typecheck     # type checking
npm run lint:dead     # dead code detection

Related projects

Project Description
pi-retry Automatic retry for 400/413/connection errors
pi-fast-resume Instant session picker (6ms vs 5.6s)
pi-hide-providers Hide providers and models from the selector
pi-double-esc Prevent accidental Escape aborts
pi-loop Close the verification loop on task completion
pi-fireworks-provider Fireworks AI provider (origin of the Kimi anchor-bleed fix)

License

MIT