pi-tool-repair

extensionmaintained

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

by · v0.1.12 · published 1d ago

$ pi install npm:pi-tool-repair
downloads/mo
855
stars
10
last push
1d ago
open issues
1

Signals

license: MITtestspi manifest: missinginstall size: —deps: 0peer deps: 0

Download trend

2.9K downloads · last 12 weeks (weekly)

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

ProblemModel sendsAfter repair
null for optional fields{"path":"/foo","offset":null}{"path":"/foo"}
Arrays as JSON strings{"edits":"[{...}]"}{"edits":[{...}]}
{} where an optional array is expected{"include":{}}(dropped)
Bare string where an array is expected{"include":"foo"}{"include":["foo"]}
Wrong field names{"file_path":"/foo"}{"path":"/foo"}
Numeric strings{"limit":"20"}{"limit":20}
Bare string as root input"/path/to/file"{"path":"/path/to/file"}
fabric_exec code arrays{"code":["const x=1;","return x;"]}newline-joined code
Schema anchor bleed (Kimi K2)"^pattern$" in values"pattern"
Leaked tool grammar (opt-in)<|DSML|tool_calls>...pi toolCall block
Phantom tool usestopReason:"toolUse" with no callretryable error

Install

With pi install (recommended):

pi install npm:pi-tool-repair

Or install from GitHub:

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

With npm:

npm install npm: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

before_provider_request
  └─ model-gated schema anchor sanitization

model response → message_end
  ├─ strip leaked grammar tokens from native toolCall blocks
  ├─ recover complete leaked grammar calls when enabled
  ├─ validate raw arguments against each active tool's live schema
  ├─ apply only known aliases and schema-directed repairs
  ├─ commit a candidate only when it re-validates
  └─ turn phantom toolUse responses into retryable errors

Pi then runs its normal prepare → validate → execute pipeline

Pi 0.84 validates tool arguments before emitting tool_call. Repair therefore runs on the finalized assistant message, while the provider's raw arguments are still available and before Pi's validation can reject or coerce them.

Repair rules

RuleWhat it catches
renameAliasedFieldfile_pathpath, querypattern, option aliases, etc.
dropNullOrUndefinednull/undefined for schema-optional fields
dropEmptyObjectPlaceholder{} where an optional array is expected
parseJsonStringifiedArray"[\"a\",\"b\"]"["a","b"]
wrapBareStringAsArray"foo"["foo"] when the schema expects an array
wrapRootStringAsObject"/path"{"path":"/path"} for known string-primary tools
coerceNumericString"20"20 when the live schema expects a number
convertTimeoutMillisecondstimeoutMstimeout seconds for bash
joinStringArrayall-string fabric_exec.code arrays → one newline-joined string

Why validate-then-repair

The extension reads the schemas from pi.getAllTools() instead of maintaining a parallel copy. Schema-valid input with no known compatibility aliases is returned unchanged. Invalid input is cloned, repaired only at schema-declared fields, and revalidated; an unrepairable candidate is discarded so Pi reports the original error. Canonical fields win when both canonical and alias spellings are present.

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:

ModeBehavior
recoverStrip leaked markup and append recovered pi toolCall blocks.
stripStrip 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 not recovered as tool calls. For DSML, dangling or truncated marker tokens (e.g. a stream that died at <|DSML|tool_calls with no closing >) are still stripped from visible text so the raw marker doesn't persist in the transcript.
  • 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

Schema-directed null, array, and numeric repairs apply to every active tool whose live schema Pi exposes. The curated alias table covers Pi's built-in read, write, edit, bash, grep, find, and ls tools. fabric_exec root strings and all-string code arrays are also supported.

Pi Fabric compatibility

With Pi Fabric full code mode, pi-tool-repair sees fabric_exec as the active model-facing tool. It repairs the outer call, leaked provider grammars, anchor bleed, and phantom tool-use responses before Pi validation. Nested pi.* calls are created later by Fabric's TypeScript guest, so Fabric owns their alias and optional-null normalization before its registry validation. No duplicate tool registration or wrapper is required.

Anchor bleed models

Phase 0 schema sanitization activates for models matching these patterns:

PatternModels
/kimi-k2/iKimi K2 variants
/minimax/iMiniMax variants
/glm/iGLM 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.

Alias summary
ToolCanonicalAliases
readpathabsolutePath, file_path, filePath, filepath, pathname, target_file, targetFile, file, absolute_path, fileAbsolutePath
readoffset, limitstart, max
greppatternquery, regex, search, q, expression, text
grepglob, ignoreCase, context, limitglobPattern, ic, caseInsensitive, ctx, max
writepath, contentpath aliases above; text, body, data, contents, fileContent
editpathpath aliases above
editoldTextold_string, oldString, old, old_str, oldStr, from, old_value, old_text, oldContent, old_content
editnewTextnew_string, newString, new, replacement, new_str, newStr, to, new_value, new_text, newContent, new_content
lspath, limitpath aliases plus directory, dir, folder, directoryPath; max
findpattern, limitquery, regex, glob, expression, search, include, name, filename; max
bashcommandcmd, shell, cmdline, script, commandLine

Development

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

Related projects

ProjectDescription
pi-retryAutomatic retry for 400/413/connection errors
pi-fast-resumeInstant session picker (6ms vs 5.6s)
pi-hide-providersHide providers and models from the selector
pi-double-escPrevent accidental Escape aborts
pi-loopClose the verification loop on task completion
pi-fireworks-providerFireworks AI provider (origin of the Kimi anchor-bleed fix)

License

MIT