feat(agent): support Standard Schema validators - #107
Open
LukasParke wants to merge 13 commits into
Open
Conversation
LukasParke
marked this pull request as ready for review
August 11, 2026 23:02
- reject thenable-returning validators (not just instanceof Promise) in validateSchemaSync so async validators can't silently pass setContext - restore Zod parity for Standard Schema context updates: filter unknown keys and store the validator's (possibly transformed) output values - reword async-validator error; fix stale isVoidSchema doc; use the ObjectSchema alias for sharedContextSchema
Importing ObjectSchema from ./schema.js added a 16th internal edge to model-result.ts, tripping sentrux's no_god_files gate. Revert to the external type imports (unresolved, uncounted) to stay at fan-out 15.
…and error propagation - convertSchemaToJsonSchema: explicit inputJsonSchema now wins for Zod too (z4.toJSONSchema throws on unrepresentable constructs, where the escape hatch is the only way through) - validatePartialAgainstSchema: Object.hasOwn instead of `in` so prototype-named keys (constructor, toString) are filtered again - unifiedExecutionResult: normalize caught values eagerly so `throw undefined` / Promise.reject() still surface as tool errors - isVoidSchema: probe Standard Schema validators with undefined so v.void() custom hooks skip result validation like z.void()
…void probe, changeset example - validatePartialAgainstSchema: persist raw caller-supplied values (Zod parity); validator output is only used to filter unknown keys. Storing transformed output poisoned the store for type-changing validators. - isVoidSchema: a Standard Schema is void only if it accepts undefined AND rejects null/string/number/object probes, so v.any()/v.optional() keep result validation like their Zod equivalents. - changeset: add the fenced consumer example required by .agents/skills/public-api-examples.
…alidators The unified log sink used validateSchemaSync, so a tool declaring an async Standard Schema event validator threw out of its own body on the first ctx.log while the same event passed via yield. Zod keeps the sync throw; non-Zod entries are validated out-of-band and forwarded on success (invalid ones dropped with a warning).
Value probing cannot distinguish 'accepts only undefined' from 'undefined | T' (v.optional(v.object(...)) rejects every finite sentinel set), so the sentinel probe could silently disable result validation for ordinary optional schemas. Non-Zod result schemas are now always validated: side-effect-only handlers returning undefined still pass a v.void() result schema, and handlers returning real values on a void hook are warned — stricter than Zod, but sound.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds Standard Schema v1 support to
@openrouter/agent, allowing tool input, output, event, context, shared-context, check, and custom-hook schemas to use validators such as Valibot, ArkType, and Effect Schema without requiring Zod schemas.What changed
schema['~standard'].validate(value)with sync and async validation supportz4.parse,safeParse, andz4.toJSONSchemabehaviorinputJsonSchemaas the explicit wire-schema escape hatch for non-Zod input validators~-key sanitization for generated and caller-supplied JSON SchemaDesign decisions
JSON Schema strategy
Input JSON Schema conversion now uses a three-tier chain:
z4.toJSONSchema(..., { target: 'draft-7' })fast path, including Zod versions older than 4.2.StandardJSONSchemaV1companion trait; the agent callsschema['~standard'].jsonSchema.input({ target: 'draft-07' })and falls through if conversion throws.inputJsonSchemaremains the deterministic explicit override and fallback. Explicit caller intent wins over the trait.This adds no runtime dependency:
StandardJSONSchemaV1ships in the existing@standard-schema/specdependency. Zod 4.2+, ArkType 2.1.28+, Zod Mini, VineJS, and Sury implement the trait natively; Valibot supports it viatoStandardJsonSchema()from@valibot/to-json-schema.Only input schemas need conversion because output, event, context, shared-context, check, and custom-hook schemas remain local validators. Generated and supplied schemas retain recursive
~-key sanitization.SDK boundary
The agent converts every client tool to the SDK's existing function-tool wire shape before making a request. The boundary remains the already-untyped
parameters: Record<string, unknown>JSON Schema handoff, so this PR does not depend on the parallel@openrouter/sdkStandard Schema work. No SDK schema types are exposed or cast into the agent's public validator surface.Context mutation
Initial context validation supports any synchronous Standard Schema validator.
ctx.setContext()andctx.setSharedContext()remain synchronous APIs, so they reject validators whose~standard.validatereturns a Promise. Zod keeps its existing per-field partial-update behavior; generic Standard Schema context updates validate the merged context object.Test coverage
~metadata sanitizationinputJsonSchemafailure for non-Zod toolsVerification:
pnpm buildpnpm typecheckpnpm lintpnpm test— 918 agent tests and 168 MCP tests passedAPI example