Client or integration
Other
Provider or upstream service
Codex-CLI
OpenCodex version
2.24.1
Endpoint or capability
/v1/chat/completions and /v1/responses - tool calls
Current behaviour
A JSON integer emitted by the provider in a string-declared field passes through the bridge untouched. Codex then rejects the call before the tool runs. Affected in practice: Codex's code-mode wait tool (cell_id declared {"type": "string"}). Through cursor/gpt-5.6-sol, every wait call arrives as {"cell_id":4,...} and fails. Measured across 3 full Codex sessions: 19/19 wait calls rejected. The model never self-corrects (it retries varying yield_time_ms/max_tokens), so this is a hard failure loop, not a degradation - same failure shape as #1611. Prompt-level mitigation does not work: with an explicit rule in context ("cell_id is a STRING, always quote it") the provider still emitted the integer on every call.
Expected behaviour
The proxy repairs the representation artifact against the declared schema, as it already does for the mirror case: #1611 fixed integer-declared fields arriving as integral floats (120000.0 -> 120000). A JSON integer in a string-declared field has exactly one faithful string reading (4 -> "4"), so it can be repaired under the same boundary logic; any non-integer value in a string field stays untouched and still fails, so no plausible lie is manufactured.
Minimal redacted request or reproduction
# Proxy running on the default port. Tool schema declares cell_id as a string.
curl -s http://127.0.0.1:10100/v1/chat/completions -H 'Content-Type: application/json' -d '{
"model": "cursor/gpt-5.6-sol",
"messages": [
{"role": "system", "content": "You manage exec cells. When a script yields, resume it with the wait tool."},
{"role": "user", "content": "Script running with cell ID 4. Resume it and get its output."}
],
"tools": [{
"type": "function",
"function": {
"name": "wait",
"description": "Waits on a yielded exec cell and returns new output or completion.",
"parameters": {
"type": "object",
"properties": {
"cell_id": {"type": "string", "description": "Identifier of the running exec cell."},
"yield_time_ms": {"type": "number"},
"max_tokens": {"type": "number"}
},
"required": ["cell_id"],
"additionalProperties": false
}
}
}],
"tool_choice": "required",
"max_tokens": 200
}'
Actual response or error
Tool-call arguments returned by the proxy (schema declares cell_id as string):
{"cell_id":4,"yield_time_ms":30000,"max_tokens":4000}
When this reaches Codex (code-mode `wait` tool, same schema), the call is rejected before the tool runs:
failed to parse function arguments: invalid type: integer `4`, expected a string at line 1 column 12
Upstream documentation
https://platform.openai.com/docs/guides/function-calling (tool-call arguments must conform to the declared JSON Schema; strict mode guarantees it). No cursor-specific public spec: expected behaviour is the concrete client requirement of Codex CLI 0.147.0, which parses wait.cell_id as a Rust String and rejects integers.
Suggested mapping or implementation notes
src/lib/tool-argument-integers.ts already does schema-aware repair for integral floats in integer-declared fields, wired at both bridge flush points (streaming and non-streaming). Extending coerceValue with the symmetric string case covers this: when the resolved schema declares "string" (directly or via anyOf/oneOf/allOf branches) and the value is a JSON integer, re-emit it as its decimal string. The cheap-reject regex at the top of coerceIntegerToolArguments (/\d\.\d/) would also need to admit integer payloads for string-declared parameters.
Additional context and attachments
Mirror of #1611 (closed): same provider-side serialization-artifact class, opposite direction. Also checked 2.24.2: tool-argument-integers.ts unchanged, case still uncovered.
Checks
Client or integration
Other
Provider or upstream service
Codex-CLI
OpenCodex version
2.24.1
Endpoint or capability
/v1/chat/completions and /v1/responses - tool calls
Current behaviour
A JSON integer emitted by the provider in a string-declared field passes through the bridge untouched. Codex then rejects the call before the tool runs. Affected in practice: Codex's code-mode
waittool (cell_iddeclared{"type": "string"}). Throughcursor/gpt-5.6-sol, everywaitcall arrives as{"cell_id":4,...}and fails. Measured across 3 full Codex sessions: 19/19waitcalls rejected. The model never self-corrects (it retries varyingyield_time_ms/max_tokens), so this is a hard failure loop, not a degradation - same failure shape as #1611. Prompt-level mitigation does not work: with an explicit rule in context ("cell_id is a STRING, always quote it") the provider still emitted the integer on every call.Expected behaviour
The proxy repairs the representation artifact against the declared schema, as it already does for the mirror case: #1611 fixed integer-declared fields arriving as integral floats (
120000.0->120000). A JSON integer in a string-declared field has exactly one faithful string reading (4->"4"), so it can be repaired under the same boundary logic; any non-integer value in a string field stays untouched and still fails, so no plausible lie is manufactured.Minimal redacted request or reproduction
Actual response or error
Tool-call arguments returned by the proxy (schema declares cell_id as string): {"cell_id":4,"yield_time_ms":30000,"max_tokens":4000} When this reaches Codex (code-mode `wait` tool, same schema), the call is rejected before the tool runs: failed to parse function arguments: invalid type: integer `4`, expected a string at line 1 column 12Upstream documentation
https://platform.openai.com/docs/guides/function-calling (tool-call arguments must conform to the declared JSON Schema;
strictmode guarantees it). No cursor-specific public spec: expected behaviour is the concrete client requirement of Codex CLI 0.147.0, which parseswait.cell_idas a Rust String and rejects integers.Suggested mapping or implementation notes
src/lib/tool-argument-integers.tsalready does schema-aware repair for integral floats in integer-declared fields, wired at both bridge flush points (streaming and non-streaming). ExtendingcoerceValuewith the symmetric string case covers this: when the resolved schema declares"string"(directly or via anyOf/oneOf/allOf branches) and the value is a JSON integer, re-emit it as its decimal string. The cheap-reject regex at the top ofcoerceIntegerToolArguments(/\d\.\d/) would also need to admit integer payloads for string-declared parameters.Additional context and attachments
Mirror of #1611 (closed): same provider-side serialization-artifact class, opposite direction. Also checked 2.24.2:
tool-argument-integers.tsunchanged, case still uncovered.Checks