Skip to content

fix(kimi): repair empty items schemas instead of dropping the tool - #7015

Merged
Hmbown merged 2 commits into
codewhale-hq:mainfrom
asto18089:up/kimi-empty-items-repair
Oct 18, 2026
Merged

Hmbown merged 2 commits into
codewhale-hq:mainfrom
asto18089:up/kimi-empty-items-repair

Conversation

@asto18089

@asto18089 asto18089 commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

What

Moonshot's MFJS schema validation fails any empty schema node with MissingType, and sanitize_moonshot_chat_tools answers by silently excluding the whole tool from the request. A tool that ships "items": {} — legal JSON Schema for "items are unconstrained", and the exact shape a browser-automation MCP server in the wild advertised for its args array — therefore never reached Kimi: the model only saw a tool_search reference it could never call and kept emitting the nearest advertised tool instead.

The Kimi parameters sanitizer now removes the empty items keyword instead of the tool: an absent items keyword already means unconstrained items, so the repair is semantics-preserving. Untyped nodes without a semantics-preserving MFJS equivalent (e.g. {"items": {"description": "..."}}) still fail closed with MissingType.

The walker visits children before the node's own items check, so a nested repair can empty the parent ("items": {"items": {}} is vacuously true once the inner keyword is gone, making the outer drop still semantics-preserving), and it visits every schema-value position the validator recurses into: property values, items, object-form additionalProperties, anyOf branches, and $defs entries — including a root items keyword beside $defs.

Tests

  • kimi_parameters_repairs_empty_items_as_unconstrained — the wild-found shape, nested occurrences, and the still-fail-closed described-only node.
  • kimi_parameters_repairs_nested_and_sibling_empty_items — items-in-items, root-items-beside-$defs, additionalProperties and anyOf positions.
  • cargo test -p codewhale-tui --lib schema_sanitize 63 passed / 0 failed; clippy -D warnings clean (lib).

Found in the wild on a browser-automation MCP catalog; ported from the Pinvou/CodeWhale fork (#101). The fork commit's tool-search eviction-reporting half already landed upstream (#6969) and is not included.

No-Issue: Moonshot dropped any tool whose schema has an empty items (port of fork PR #101)

Moonshot MFJS validation fails any empty schema node with MissingType,
and sanitize_moonshot_chat_tools answers by silently excluding the whole
tool from the request — the model only sees a tool_search reference it
can never call and keeps emitting the nearest advertised tool instead.
JSON Schema reads an empty items schema as unconstrained items, which is
exactly the MFJS-legal shape with the keyword absent, so the kimi
parameters sanitizer now removes the empty keyword instead of the tool.
Untyped nodes without a semantics-preserving equivalent (empty property
schemas) still fail closed.

Found in the wild: a browser-automation MCP server advertised
`args: {"type": "array", "items": {}}`, so Kimi requests never carried
the tool no matter how often it was activated.

The walker visits children before the node's own items check so a nested
repair can empty the parent (`"items": {"items": {}}` is vacuously true
once the inner keyword is gone, so dropping the outer keyword stays
semantics-preserving), and visits every schema-value position the
validator recurses into: property values, `items`, object-form
`additionalProperties`, `anyOf` branches, and `$defs` entries.

Signed-off-by: asto <asto18089@126.com>
@asto18089
asto18089 requested a review from Hmbown as a code owner October 18, 2026 02:06

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@github-actions github-actions Bot added the contribution-gate Author not yet in .github/APPROVED_CONTRIBUTORS; a maintainer grants access with /lgtm label Oct 18, 2026
@Hmbown
Hmbown merged commit 7f469f6 into codewhale-hq:main Oct 18, 2026
27 of 28 checks passed
Hmbown pushed a commit that referenced this pull request Oct 18, 2026
Maintainer follow-ups from the review of three merged contributor PRs
from @asto18089.

#7014 (Claude 5 sampling gate): the "never send both" assertion sat after
the older-model loop, so it checked the first claude-opus-4-8 body, which
never carries top_p, and could not catch a regression. It now runs inside
the loop for each 4.x model, and a top_p-only request is checked to pass
through, as the PR body described.

#7015 (Kimi empty `items` repair): once an empty `items` is dropped, a
required array with minItems >= 1 still failed mfjs_schema_can_terminate
with InvalidSchemaNode, so that tool was still excluded. MFJS allows
`items` to be absent, and an unconstrained array can always be satisfied,
so absent `items` now counts as terminating.

#7017 (era-scoped checkpoint recognition): the docs said an unstamped
history "predates stamping". A session that has never been compacted has
no stamped carrier either, so a pasted legacy header still reads as a
checkpoint at a session's first compaction. SURVIVAL_CONTRACT.md and the
history_is_post_provenance doc now state that limit.

Refs #7014
Refs #7015
Refs #7017

Verified: scripts/dev-test.sh tui body_drops_sampling schema_sanitize
moonshot kimi compaction: 355 run, 355 passed (includes
body_drops_sampling_params_for_models_that_reject_them and 141
kimi/moonshot tests). validate_survival_contract.mjs: ok 17 fixtures.
cargo fmt --check clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
@Hmbown

Hmbown commented Oct 18, 2026

Copy link
Copy Markdown
Collaborator

🤖 This reply is from Claude Opus 5.5, an AI agent working on behalf of the Codewhale maintainers. It is posted through @Hmbown's account, but Hunter did not write it.

Merged in 7f469f6. Thank you @asto18089. Repairing items: {} instead of dropping the whole tool is exactly right for Moonshot: MFJS allows items to be absent, so removing the empty node changes nothing about what the schema accepts. You also tested every place the validator recurses into.

One follow-up on main (e3e5632). Once items is removed, a required array with minItems >= 1 still failed mfjs_schema_can_terminate, so that shape still lost its tool. Absent items now counts as terminating there. Not in a release yet.

— Written by Claude Opus 5.5 (AI agent) on behalf of the maintainers.

Hmbown pushed a commit that referenced this pull request Oct 18, 2026
Adds [Unreleased] entries for #7014, #7015, #7017, #7018, #7019 and #7020,
crediting @asto18089 (and @zhuowp for the fork ports). Each merged without
a changelog line. The entry for #7020 names its behavior change: the
app-server's legacy [providers.custom] table now refuses a non-Chat wire
with provider_wire_format_unsupported instead of forwarding a Chat body.

Refs #7014
Refs #7015
Refs #7017
Refs #7018
Refs #7019
Refs #7020
Refs #6997

Verified: sync-changelog --check up to date; web check-facts OK;
check-contributor-credit "Every contributor in the window is credited on
all three surfaces".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contribution-gate Author not yet in .github/APPROVED_CONTRIBUTORS; a maintainer grants access with /lgtm

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants