mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
A schema whose root `$id` was a relative path with a directory part, such as `schemas/tool.json`, could not resolve its local `$ref`s. The `Resolver` constructor applied the root `$id` to `DEFAULT_BASE` once to compute `rootBase`. The compiler, the error explainer and the coercer start from `rootBase` and call `baseFor(root, rootBase)`, which applied the `$id` a second time. Local references therefore resolved against `https://json-schema.invalid/schemas/schemas/tool.json`. The effect was that `compile(schema)` threw `Unresolved $ref #/$defs/count`. With `unresolvedRefs: "reject"`, which is how tool arguments are validated (pi-ai's `validateToolArguments`, used by the agent loop and durable, and durable's `structuredOutput` check), valid arguments were rejected and the tool never ran. Absolute `$id`s and bare names like `tool.json` were not affected, because resolving them twice gives the same URI. The fix: - `rootBase` is now `DEFAULT_BASE`; walkers apply the root's own `$id` through `baseFor`. - `baseFor` indexes the schema before applying an `$id` and then returns the indexed base, so a caller whose base already includes the `$id` cannot apply it again. Without this, `unevaluatedProperties`/`unevaluatedItems` still failed: `build()` passes the already-based node to `annotating()`, which applied the `$id` once more. - The coercer resolves bases through `baseFor` instead of reading `baseOf` directly. Otherwise the `rootBase` change broke the first `parse`/`coerce` of a schema with `$dynamicRef`: the restarted dynamic compiler is not indexed yet, so a relative `$ref` resolved against `DEFAULT_BASE` and conversion was silently skipped. - The explainer drops `$id` from the copy it makes for unevaluated keywords, and resolves `anyOf`/`oneOf` branch `$ref`s against the branch's own base (both threw in `errors()` on main for nested resources with a relative `$id`). Testing: - Regression tests in `validator.test.ts` (relative root `$id` with local `$ref`, with `unevaluatedProperties`/`unevaluatedItems`, error reporting under nested relative resources) and `coerce.test.ts` (first coercion of a `$dynamicRef` schema with a relative external `$ref`). They fail without the change and pass with it. - Full pi-schema suite: 8712 passed. - Independently reviewed against edge cases: draft 7 (inert `$id` beside `$ref`, `#anchor` ids), fragment ids, nested relative chains, anchors, registered schemas referring back to the root, fresh `parse`/`coerce`/`errors` after a dynamic restart. Fixes #11697 Requested by @badlogic --------- Co-authored-by: triagent <triagent@localhost> Co-authored-by: Petr Houška <houskape@gmail.com> |
||
|---|---|---|
| .. | ||
| agent | ||
| ai | ||
| chord | ||
| codemode | ||
| coding-agent | ||
| durable | ||
| env | ||
| evals | ||
| mcp | ||
| schema | ||
| telemetry | ||
| tui | ||