diff --git a/README.md b/README.md index 3571ea3..01a693b 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,9 @@ also supports `picker`. Codex review and subagents also support `inherit`. Native is always the default. For Codex, Kenari detects whether `codex login` uses ChatGPT or an API key and -keeps native requests on the matching OpenAI upstream. +keeps native requests on the matching OpenAI upstream. The process-scoped +router disables request compression and WebSocket transport so it can inspect +the model ID before selecting the upstream. Automation must set every role: diff --git a/package.json b/package.json index bcb41da..12171d3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@kenarihq/cli", - "version": "0.3.1", + "version": "0.3.2", "description": "Official kenari CLI. Route Claude Code and Codex models per process.", "license": "MIT", "type": "module", diff --git a/src/runtime/codex.js b/src/runtime/codex.js index a30ade1..25207e9 100644 --- a/src/runtime/codex.js +++ b/src/runtime/codex.js @@ -75,6 +75,16 @@ export function codexKenariModels(cache, nativeModels = []) { export function buildCodexLaunch(options) { const inputArgs = options.args || []; for (let index = 0; index < inputArgs.length; index += 1) { + if (inputArgs[index] === '--enable') { + if (inputArgs[index + 1] === 'enable_request_compression') { + throw new KenariError('unsafe Codex routing override: enable_request_compression'); + } + index += 1; + continue; + } + if (inputArgs[index] === '--enable=enable_request_compression') { + throw new KenariError('unsafe Codex routing override: enable_request_compression'); + } if (inputArgs[index] === '--oss' || inputArgs[index] === '--local-provider' || inputArgs[index].startsWith('--local-provider=')) { throw new KenariError(`unsafe Codex routing override: ${inputArgs[index]}`); @@ -85,7 +95,12 @@ export function buildCodexLaunch(options) { if (inline === null && !['-c', '--config'].includes(inputArgs[index])) continue; const value = inline ?? inputArgs[index + 1] ?? ''; const key = value.split('=', 1)[0].trim(); - if (['model_provider', 'openai_base_url', 'model_catalog_json'].includes(key) + if ([ + 'model_provider', + 'openai_base_url', + 'model_catalog_json', + 'features.enable_request_compression', + ].includes(key) || key.startsWith('model_providers.')) { throw new KenariError(`unsafe Codex routing override: ${key}`); } @@ -99,6 +114,7 @@ export function buildCodexLaunch(options) { `model_providers.${CODEX_ROUTER_PROVIDER}.requires_openai_auth=true`, `model_providers.${CODEX_ROUTER_PROVIDER}.supports_websockets=false`, `model_providers.${CODEX_ROUTER_PROVIDER}.supports_standalone_web_search=true`, + 'features.enable_request_compression=false', ]; if (options.catalogPath) { overrides.push(`model_catalog_json=${tomlString(options.catalogPath)}`); diff --git a/test/v2-runtime-supervisor.test.js b/test/v2-runtime-supervisor.test.js index 62837ef..d798c39 100644 --- a/test/v2-runtime-supervisor.test.js +++ b/test/v2-runtime-supervisor.test.js @@ -133,6 +133,7 @@ test('Codex launch injects temporary controls before original args', () => { )); assert.ok(built.args.includes('model_providers.kenari_router.requires_openai_auth=true')); assert.ok(built.args.includes('model_providers.kenari_router.supports_websockets=false')); + assert.ok(built.args.includes('features.enable_request_compression=false')); assert.equal(built.env.OPENAI_API_KEY, 'secret'); assert.equal(built.env.KEEP, 'yes'); const native = [{ @@ -149,6 +150,18 @@ test('Codex launch injects temporary controls before original args', () => { routerUrl: 'http://127.0.0.1:2', args: ['-c', 'openai_base_url="https://bypass.example"'], }), /unsafe Codex routing override/); + for (const args of [ + ['-c', 'features.enable_request_compression=true'], + ['--config=features.enable_request_compression=true'], + ['--enable', 'enable_request_compression'], + ['--enable=enable_request_compression'], + ]) { + assert.throws(() => buildCodexLaunch({ + toolConfig: { roles: {} }, + routerUrl: 'http://127.0.0.1:2', + args, + }), /unsafe Codex routing override/, args.join(' ')); + } }); test('Codex native upstream follows the active login method', () => {