Skip to content

fix(hook): return revised input from Pi tool_call handler - #3453

Open
thinhngotony wants to merge 1 commit into
rtk-ai:developfrom
thinhngotony:fix/pi-hook-tool-call-input
Open

fix(hook): return revised input from Pi tool_call handler#3453
thinhngotony wants to merge 1 commit into
rtk-ai:developfrom
thinhngotony:fix/pi-hook-tool-call-input

Conversation

@thinhngotony

Copy link
Copy Markdown

Summary

  • Fixes the merged Pi extension (hooks/pi/rtk.ts, feat(hook): add pi support #1741, released in rtk 0.42) so rtk rewrite results actually take effect — today the extension never rewrites a single command.
  • Root cause: the Pi/OMP tool_call dispatcher builds the args a tool actually executes with from a separate copy of event.input; it only substitutes a handler's returned { input } (ToolCallEventResult). The handler mutated event.input.command in place and returned nothing, so the rewrite was silently discarded on every call.
  • Fix: return { input: { ...event.input, command: rewritten } } instead of mutating, preserving the other BashToolInput fields via spread.
  • Updates hooks/pi/README.md's "Specifics" section, which documented the broken mutate-in-place mechanism as the intended design.
  • Adds a regression test (test_pi_plugin_returns_revised_input_instead_of_mutating_in_place) asserting the shipped plugin source returns the revision and does not rely on mutating event.input in place.

Why this matters

This is the same file discussed in #1365 (draft OMP-specific integration): that PR's review thread and #591 both assume "mutate event.input.command" is a working mechanism ("The extension handler modifies event.input.command within the tool_call. The subsequent tool execution will then use the modified parameters." — #591 comment). It isn't, per the OMP ExtensionAPI type contract (ToolCallEventResult.input, shared-events.d.ts) and confirmed empirically: after applying this exact fix locally and running a real omp -p --auto-approve "git status" session, rtk's own history.db recorded git status → rtk git status; before the fix, nothing was ever rewritten despite the extension loading and probing successfully.

Test plan

  • cargo fmt --all -- --check
  • cargo clippy --all-targets
  • cargo test (2563 passed, 0 failed, incl. new regression test)
  • bun --eval 'await import("./hooks/pi/rtk.ts")'
  • Manual end-to-end: built a fresh omp session (Oh My Pi, the extension consumer for feat(omp): add extension-based rewrite integration for Oh My Pi #1365/Add support for Oh-My-Pi #591), installed this exact fixed source as its tool_call handler, ran git status through the agent's bash tool. rtk's local history.db recorded the command as original_cmd: git status, rtk_cmd: rtk git status — confirming the executed shell command was rewritten, not just displayed.

Related: #401, #591, #1365, #1741

The Pi/OMP tool_call dispatcher builds the args a tool actually
executes with from a separate copy of event.input; it only swaps in
a handler's RETURNED { input } (ToolCallEventResult). The Pi extension
mutated event.input.command in place and returned nothing, so
rtk rewrite's result was silently discarded and bash commands never
actually ran through rtk.

Return { input: { ...event.input, command: rewritten } } instead,
preserving the other BashToolInput fields via spread. Verified
end-to-end against a live OMP session: the executed shell command
changed from 'git status' to 'rtk git status', confirmed via rtk's
own history.db.

Adds a regression test asserting the plugin source returns the
revised input rather than mutating event.input in place, and updates
hooks/pi/README.md to describe the correct mechanism.
Copilot AI lite review requested due to automatic review settings August 6, 2026 08:43
@CLAassistant

CLAassistant commented Aug 6, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

Copilot AI 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.

Pull request overview

Fixes the Pi coding-agent hook integration so rtk rewrite results actually affect the executed bash tool call by returning a ToolCallEventResult with a revised input payload (instead of mutating event.input in place, which the dispatcher does not observe).

Changes:

  • Update hooks/pi/rtk.ts tool_call handler to return { input: { ...event.input, command: rewritten } } when a rewrite applies.
  • Update hooks/pi/README.md to document the return-based mechanism (not mutate-in-place).
  • Add a regression test ensuring the shipped Pi plugin source returns revised input and does not rely on in-place mutation.

Review note (maintainability):

  • The new regression test’s “no mutation” assertion is currently keyed to a single exact substring ("event.input.command = rewritten"). This is easy to evade with whitespace changes (e.g., event.input.command=rewritten) or slightly different assignment formatting; consider strengthening the check to reject both "event.input.command =" and "event.input.command=", and/or assert the presence of the intended spread-return pattern more explicitly.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
src/hooks/init.rs Adds a regression test to prevent reintroducing the “mutate input in place” bug in the shipped Pi plugin source.
hooks/pi/rtk.ts Fixes the Pi tool_call handler to return revised { input } so the dispatcher uses the rewritten command.
hooks/pi/README.md Updates documentation to match the dispatcher contract (ToolCallEventResult.input) and the fixed implementation.

@thinhngotony

thinhngotony commented Aug 6, 2026

Copy link
Copy Markdown
Author

@aeppling @xavierpestel-ai Can you guys help me to review this?

@zktaiga

zktaiga commented Aug 7, 2026

Copy link
Copy Markdown

This is a no-op on pi. ToolCallEventResult has no input field, and the doc comment says what to do instead:

https://github.com/earendil-works/pi/blob/709aa03194301afd008a07d64ff1bf12e4f7ece6/packages/coding-agent/src/core/extensions/types.ts#L1071-L1074

The dispatcher only ever reads .block off the handler result — anything else on the returned object is discarded:

https://github.com/earendil-works/pi/blob/709aa03194301afd008a07d64ff1bf12e4f7ece6/packages/coding-agent/src/core/extensions/runner.ts#L932-L952

event.input is the same object that reaches execution, which is why mutating it works today:

https://github.com/earendil-works/pi/blob/709aa03194301afd008a07d64ff1bf12e4f7ece6/packages/coding-agent/docs/extensions.md?plain=1#L759

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.

4 participants