Skip to content

fix(cli): emit string enums as Type.String({ enum }) in Pi adapter (#2787) - #2807

Open
divakantpandey wants to merge 2 commits into
DeusData:mainfrom
divakantpandey:fix/2787-pi-extension-string-enum
Open

divakantpandey wants to merge 2 commits into
DeusData:mainfrom
divakantpandey:fix/2787-pi-extension-string-enum

Conversation

@divakantpandey

Copy link
Copy Markdown

Fixes #2787

Problem

When codebase-memory-mcp install generates the Pi client extension (cbmem.ts), convert_json_schema_to_typebox() translates JSON Schema string enums into:

Type.Union([Type.Literal('opt1'), Type.Literal('opt2')], { description: '...' })

TypeBox serializes this pattern as anyOf: [{ const: 'opt1' }, { const: 'opt2' }]. Gemini / Antigravity function declaration validation rejects const and anyOf with:

Antigravity API error (400): Invalid JSON payload received. Unknown name "const" at 'request.tools[0].function_declarations[...].parameters.properties[...].value.any_of[0]': Cannot find field.

Solution

Emit string enums as standard TypeBox string enums:

Type.String({ enum: ['opt1', 'opt2'], description: '...' })

which TypeBox serializes directly as { type: 'string', enum: ['opt1', 'opt2'], description: '...' }. This is standard JSON Schema supported by all major function-calling providers (Gemini, Antigravity, Anthropic, OpenAI, Bedrock).

Verification

  • Added client_adapter_pi_emits_string_enum_issue2787 and updated client_adapter_pi_emits_parameters_and_execute in tests/test_agent_clients.c.
  • scripts/test.sh --suites agent_clients (ASan + UBSan): 40/40 tests passed.
  • scripts/test.sh --contracts-only: All static contracts passed.
  • scripts/check-dco.sh: Commit carries valid Signed-off-by.
  • scripts/build.sh: Production binary builds cleanly.

Note: This contribution was prepared with the assistance of Antigravity.

@github-actions

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

…#2787 test

The DeusData#2787 change added client_adapter_pi_emits_string_enum_issue2787 directly below client_adapter_pi_wraps_result_and_honors_abort and, in the same hunk, removed that test's check that the generated abort handler kills the cli child (if (!child.killed) child.kill();). The generator still emits the line; restore the assertion byte-for-byte so the abort path keeps its coverage.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
@DeusData

Copy link
Copy Markdown
Owner

Hi @divakantpandey, welcome, and thank you for your first contribution here! This is a really clean fix. You found the exact spot where the Pi generator turned string enums into a Type.Union of Type.Literal, switched it to Type.String({ enum: [...] }) so the serialized schema becomes a plain { type: 'string', enum: [...] } that Gemini and Antigravity accept, and pinned it with a regression test against the real index_repository mode values. As a bonus, the restructure also fixes a latent edge case: an enum with no string members used to produce malformed TypeScript, and now it falls back to a plain Type.String.

We pushed one small commit on top of your branch so this can land right away:

  1. test(cli): restore the abort-kill assertion in client_adapter_pi_wraps_result_and_honors_abort. When the new test was added right below it, the existing check that the generated abort handler kills the child process (if (!child.killed) child.kill();) was removed from that earlier test. The generator still emits that line unchanged, so this only restores coverage and changes no behaviour. We put it back so the abort path stays tested. Your new test and your production change are untouched.

Since the branch has moved, please pull before pushing again. Thanks again, this unblocks every Pi user on a Gemini model!

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Generated Pi extension (cbmem.ts) breaks Gemini/Antigravity: Type.Union of Type.Literal serializes to anyOf/const

2 participants