Skip to content

fix(core): decode plugin tool input with the schema's own instance - #43460

Open
argszero wants to merge 1 commit into
anomalyco:v2from
argszero:fix-plugin-schema-drift
Open

fix(core): decode plugin tool input with the schema's own instance#43460
argszero wants to merge 1 commit into
anomalyco:v2from
argszero:fix-plugin-schema-drift

Conversation

@argszero

Copy link
Copy Markdown

Issue for this PR

Closes #43322

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When a config plugin bundles a different effect version than the server, every tool input decode failed with Invalid tool input: Expected object — input-independent, for every tool, until the plugin's effect was manually realigned. Root cause: the plugin's schema still passes the host Schema.isSchema check (the ~effect/Schema/Schema TypeId string is stable across versions), so decodeInput ran the host's Schema.decodeUnknownEffect on the foreign AST, which the host decoder cannot interpret.

This PR makes decodeInput in packages/core/src/tool/runtime.ts fall back to the schema's own constructor (schema.makeEffect), which validates with the schema's own instance and is therefore version-agnostic:

  • The host decode runs first, preserving existing behavior and error text for the server's own schemas.
  • On host-decode failure, the schema's own constructor validates the value: it only succeeds when the failure was schema drift rather than an invalid input, so genuine invalid inputs are still rejected with a Tool.Error.
  • The fallback is gated by a new hasTransformations helper that walks the schema AST for encoding nodes (struct fields, arrays, unions, tuples, records). Schemas with transformations or decoding defaults keep the plain host decode, because their make view (type side) differs from their decoded view — a naive fallback there could silently accept input the decoder would reject. Inspection failures (e.g. decoding-default schemas whose AST accessor throws) also keep the plain path.

Reproduced and verified end-to-end against a real foreign schema built with effect@4.0.0-beta.101 loaded into the beta.107 server: valid input now decodes successfully (previously Expected object), and invalid input is rejected with Invalid tool input: Expected number, got "no" at ["bar"].

The encodeOutput path has the same theoretical drift exposure for plugin tools that declare output schemas, but effect schemas expose no version-agnostic encode primitive on the instance, so that is left as a follow-up.

How did you verify your code works?

  • bun test ./test/tool-schema.test.ts from packages/core: 10 passed, including a new regression test that simulates a drifted schema (mutated AST the host decoder rejects while the schema's own constructor still validates) and asserts valid input decodes, invalid input is rejected, and same-host schemas behave unchanged.
  • Existing transformed-codec test (session-runner-tool-registry.test.ts) passes unchanged — it guards the no-fallback-for-transforms gate.
  • bun test from packages/core: 1891 passed, 16 skipped, 0 failed.
  • bun typecheck from packages/core and repo-wide bun typecheck (33 tasks): passed.

Screenshots / recordings

N/A (not a UI change)

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions

Copy link
Copy Markdown
Contributor

Thanks for your contribution!

This PR doesn't have a linked issue. All PRs must reference an existing issue.

Please:

  1. Open an issue describing the bug/feature (if one doesn't exist)
  2. Add Fixes #<number> or Closes #<number> to this PR description

See CONTRIBUTING.md for details.

@argszero
argszero force-pushed the fix-plugin-schema-drift branch 4 times, most recently from 9bffaa7 to 7a43be7 Compare August 19, 2026 23:26
@argszero
argszero force-pushed the fix-plugin-schema-drift branch from 7a43be7 to 98f534d Compare August 20, 2026 01:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant