Ctrl K
ComunidadGuías…

evidence/CodexParallel-probe.mjs

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// 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();