evidence/CodexParallel-probe.mjs
Rama master · réplica de lectura del repositorio original.
// P1 (evidence/ClientSandboxes.md §3.3 C1): Codex CLI 0.164 sends
// `parallel_tool_calls:false` on 29/29 requests and the responses surface used to
// drop the field. After wave 2 forwarded it, the chat route's gate answered 400
// `unsupported_parameter` with DEVIN_CONNECT_TOOL_CHOICE unset — the shipped
// default — so Codex failed on its very first turn.
//
// This probe drives the REAL responses handler and the REAL chat gate; only the
// connect transport is stubbed, so a model call is observable and no bytes leave
// the box. It is the reproduction recorded in evidence/CodexParallelFix.md.
//
// Run with: node --import ./test/setup-env.mjs evidence/CodexParallel-probe.mjs
import { handleResponses } from '../src/handlers/responses.js';
import { __setConnectDeps, __resetConnectDeps } from '../src/handlers/chat.js';
import { getConnectMetrics } from '../src/devin-connect-metrics.js';
process.env.DEVIN_CONNECT = '1';
process.env.DEVIN_CONNECT_TOKEN = 'env-token-shape-only';
delete process.env.WINDSURF_API_KEY;
delete process.env.WINDSURFAPI_STRICT_MODEL;
const state = { hits: 0, lastParams: null };
__setConnectDeps({
toChatCompletion: async (params) => {
state.hits++;
state.lastParams = params;
return {
status: 200,
body: {
id: 'chatcmpl-probe',
object: 'chat.completion',
created: 1,
model: 'gpt-6-luna-max',
choices: [{ index: 0, message: { role: 'assistant', content: 'SERVED' }, finish_reason: 'stop' }],
usage: { prompt_tokens: 1, completion_tokens: 1, total_tokens: 2 },
},
};
},
});
// Codex 0.164 shape, flattened the way the capture shows it reaching the proxy:
// one function tool set + the hint on every request.
function codexRequest(extra = {}) {
return {
model: 'gpt-6-luna-max',
stream: false,
store: false,
input: [
{ type: 'message', role: 'user', content: [{ type: 'input_text', text: 'say ok' }] },
],
tools: [{
type: 'function',
name: 'functions__exec',
description: 'run a command',
parameters: { type: 'object', properties: { command: { type: 'string' } }, required: ['command'] },
}],
tool_choice: 'auto',
...extra,
};
}
async function run(label, body) {
state.hits = 0;
state.lastParams = null;
const before = getConnectMetrics().parallel_tool_calls_hint_ignored;
const r = await handleResponses(body);
const err = r?.body?.error;
console.log(JSON.stringify({
label,
status: r.status,
modelCalls: state.hits,
error: err ? { code: err.code, param: err.param, message: String(err.message).slice(0, 120) } : null,
forwardedParallelToolCalls: state.lastParams ? state.lastParams.parallelToolCalls : undefined,
hintIgnoredDelta: getConnectMetrics().parallel_tool_calls_hint_ignored - before,
}));
return r;
}
console.log('--- passthrough OFF (shipped default: DEVIN_CONNECT_TOOL_CHOICE unset) ---');
delete process.env.DEVIN_CONNECT_TOOL_CHOICE;
await run('codex-shape with parallel_tool_calls:false', codexRequest({ parallel_tool_calls: false }));
await run('same request WITHOUT the field', codexRequest());
await run('parallel_tool_calls:true', codexRequest({ parallel_tool_calls: true }));
console.log('--- passthrough ON (DEVIN_CONNECT_TOOL_CHOICE=1) ---');
process.env.DEVIN_CONNECT_TOOL_CHOICE = '1';
await run('codex-shape with parallel_tool_calls:false', codexRequest({ parallel_tool_calls: false }));
__resetConnectDeps();