Skip to content

fix(ai): moonshot tool-schema projection drops prefixItems when items is also present — position constraints are silently replaced #1574

Open

Description

@dezren39

fix(ai): moonshot tool-schema projection drops prefixItems when items is also present — position constraints are silently replaced

Summary

In packages/ai/src/protocols/utils/tool-schema.ts, moonshotNode translates 2020-12 tuple schemas for Moonshot/Kimi targets. When a node carries prefixItems alone, the positions are folded into an items anyOf (the function's tested approximation). But when a node carries both prefixItems and items — the standard 2020-12 way to say "these positions have these types, the rest have that type" — the prefixItems branch returns [] (tool-schema.ts:31-34, if ("items" in schema) return []) and the positions vanish. The surviving items schema then applies to every element, including position 0, so the projected schema does not merely lose the position constraint — it contradicts it.

Evidence (verified this run against origin/main e57af29, laptop probe tree)

Probe importing the real module (ToolSchemaProjection.moonshot):

  • Input: {type:"object", properties:{ pair:{ type:"array", prefixItems:[{type:"string"}], items:{type:"integer"} } }}
  • Output: {"type":"object","properties":{"pair":{"type":"array","items":{"type":"integer"}}}} — element 0, required to be a string by the source schema, is now declared an integer like every other element.
  • Control, prefixItems alone: [{type:"string"},{type:"integer"}] → items:{anyOf:[{type:"string"},{type:"integer"}]} — the fold the both-present case skips.

Existing coverage (packages/ai/test/tool-schema-projection.test.ts): the tuple test (~line 199) covers an items array alone and prefixItems alone, never the two together — this combination is untested, unlike the neighboring deliberate behaviors in the same function (the $ref-sibling strip at tool-schema.ts:25 is intentional and pinned by that same test; it is explicitly out of scope here).

Why it matters

ToolSchemaProjection runs on every tool schema for Kimi/Moonshot models, including models reached through gateways and OpenAI-compatible endpoints (model-name selection in modelCompatibility). Fixed-shape tuple parameters (coordinate pairs with a trailing free list, [name, ...values] shapes) are exactly the schemas that carry both keywords. Today those tools are advertised to the model with a wrong element type for their leading positions — the model generates arguments the tool then rejects, or the provider validates against a schema the tool never declared.

Acceptance criteria

  • A node carrying both prefixItems and items projects to a schema that keeps every prefix position's constraint reachable (e.g. fold the prefix branches together with the items schema into the same anyOf the prefixItems-only path already produces), instead of discarding prefixItems.
  • prefixItems-only and items-only (array and schema forms) projections are byte-identical to today.
  • A regression test in packages/ai/test/tool-schema-projection.test.ts covers the both-present combination, including a single-element prefixItems.
  • The deliberate $ref-sibling strip and the unevaluatedItems drop are unchanged.

Filed by new-issue-creator-opencode (fleet role map: developing-today/code#2131; implementation anomalyco#2152). Probe tree: laptop ~/git/fleet/probe-2026-11-03-creator3 at origin/main e57af29.

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    task/small-fixQueue packet kind: bounded fix with reproduction and targeted tests
    source/creatorFiled by the new-issue-creator job
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    app/aiPackage: ai (pkgs/ or apps/ in this repo)
    effort/sSmall: expected under 2 hours of work
    on Nov 3, 2026
  2. dezren39 commented on Nov 3, 2026

    @dezren39
    Author

    🤖 Agent note · state: filed

    • Agent: new-issue-creator job (new-issue-creator-opencode)
    • Session / ID: cron new-issue-creator-opencode, run 2026-11-03 16:35 CST
    • Found via: filed this run
    • Task: Filed this issue — ToolSchemaProjection.moonshot drops a node's prefixItems whenever items is also present (tool-schema.ts:31-34), so the surviving items schema governs every position and contradicts the source at the prefixed positions. Verified on origin/main e57af29 in a laptop probe tree importing the real module: {prefixItems:[{type:"string"}], items:{type:"integer"}} → {items:{type:"integer"}} (element 0 must be a string; projected as integer). The prefixItems-only fold to an items-anyOf works and is tested; the both-present combination is untested. The neighboring $ref-sibling strip (tool-schema.ts:25) is deliberate and test-pinned — explicitly out of scope.
    • Expectation: effort s — fold the prefix branches together with the items schema into the anyOf the prefixItems-only path already produces; done when a regression test covers the both-present combination and prefixItems-only / items-only projections are byte-identical, with the packages/ai suite green.
    • Links: queue packet enqueued to repo-health-opencode-pilot this run
  3. added
    queue/claimedA queue worker has claimed the task for this item
    work/assignedAn agent holds this item via the queue (claimed task)
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 4, 2026
  4. dezren39 commented on Nov 4, 2026

    @dezren39
    Author

    Picked up by lane A (opencode) queue worker (task 8891) and handed off to the opencode execution lane — the laptop claimant launches the bound session for this task.

  5. dezren39 commented on Nov 4, 2026

    @dezren39
    Author

    Handed off to the opencode execution lane (task 8891). The laptop claimant will launch the bound session to work this item.

  6. dezren39 commented on Nov 4, 2026

    @dezren39
    Author

    PR #1647 opened — task 8891 (lane opencode-a), parked on CI/Codex: the moonshot projection now folds prefixItems together with the trailing items schema into one anyOf instead of discarding the prefix positions. Regression test covers single- and multi-element prefixes; suite 13/13. Resume: disposition Codex, verify CI, merge, complete task 8891, clean labels here.

  7. added
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    and removed
    queue/claimedA queue worker has claimed the task for this item
    on Nov 4, 2026
  8. dezren39 commented on Nov 4, 2026

    @dezren39
    Author

    🤖 Agent note · picked up

  9. dezren39 commented on Nov 4, 2026

    @dezren39
    Author

    🤖 Agent note · parked

  10. added
    status/parkedDeliberately paused with a recorded resume condition
    and removed on Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    app/aiPackage: ai (pkgs/ or apps/ in this repo)area/typescriptTypeScript code areaeffort/sSmall: expected under 2 hours of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)queue/parkedQueue task is parked (waiting on CI, time, or an event)source/creatorFiled by the new-issue-creator jobstatus/parkedDeliberately paused with a recorded resume conditiontask/small-fixQueue packet kind: bounded fix with reproduction and targeted testswork/assignedAn agent holds this item via the queue (claimed task)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions