Merge upstream/main (Auto Router, host guard, doctor, passthroughs) into fork#10
Merged
Conversation
…utions#14) Adds a Host-header allowlist middleware so a page the user visits cannot use DNS rebinding to drive the loopback shim with their BYOK credentials. Loopback names, the configured bind host, and CODEX_SHIM_ALLOWED_HOSTS are accepted; everything else is rejected with 403 before any upstream call. 53 tests pass locally. Co-authored-by: aaronjmars <aaronjmars@users.noreply.github.com>
Patches the Codex Desktop sidebar recent-thread loader alongside the existing model-picker ASAR patch so native openai chats stay visible while Desktop is routed through codex_shim. Also recomputes ElectronAsarIntegrity in Info.plist after repacking app.asar and restores/recomputes it during restore-app. Tested on Codex Desktop 26.519.41501 / codex-cli 0.133.0-alpha.1 (macOS arm64). 40 tests pass locally. Fixes #10. Co-authored-by: tharuxpert <tharuxpert@users.noreply.github.com>
) Re-do of #9 against post-sybil-solutions#13/sybil-solutions#14 main. Adds /picker, /api/models, /api/switch behind the existing Host-header allowlist; switching rewrites model and provider name in ~/.codex/config.toml so Codex Desktop shows the selected model's display name. Cross-platform Codex restart (Windows/macOS). Best-effort last_request.json dump + upstream-error slug logging on the chat-completions path. Translate.py hardening from the original #9 already landed via 0da7b74/215d32e and is intentionally not re-applied. 68 tests pass locally. Closes #9. Co-authored-by: MarioCodarin <MarioCodarin@users.noreply.github.com>
Add /v1/responses/compact support. ChatGPT passthrough forwards to the native ChatGPT compact endpoint; BYOK OpenAI/chat and Anthropic routes run a non-streaming compaction prompt and return a Responses-shaped compacted window. Translate native Responses-only tools into deterministic BYOK function fallbacks for computer_use, web_search, apply_patch, and local_shell while keeping MCP function tools passed through. Tests: git diff --check; python3 -m pytest tests -q (72 passed); python3 -m compileall codex_shim. Generated with [Devin](https://cli.devin.ai/docs)
Preserve upstream usage from OpenAI chat-completion stream chunks and Anthropic message_delta stream events, then include it on the final response.completed event so Codex can track token counts and trigger auto-compaction after streamed BYOK turns. Adds regression tests for both stream shapes. Tests: git diff --check; python3 -m pytest tests -q (74 passed); python3 -m compileall codex_shim. Generated with [Devin](https://cli.devin.ai/docs)
Preserve Responses input_image content, computer_call_output screenshots, and visual function_call_output payloads through BYOK translation. OpenAI-chat providers receive image_url parts; Anthropic providers receive image blocks. Keeps text-only Anthropic messages as strings for strict compatibility. Tests: git diff --check; python3 -m pytest tests -q (78 passed); python3 -m compileall codex_shim. Generated with [Devin](https://cli.devin.ai/docs)
Adds auth-gated Cursor Composer subscription passthrough via cursor-agent CLI and fixes pre-merge subprocess cleanup/docs issues.\n\nVerified:\n- git diff --check\n- python3 -m pytest tests/test_cursor_passthrough.py -q\n- python3 -m pytest tests/ -q\n- python3 -m compileall codex_shim/ -q\n\nGenerated with [Devin](https://cli.devin.ai/docs)
## Summary - Add a Windows launcher for live quota/cost-enriched model display names. - Harden launcher path discovery so it works outside the contributor's local machine. - Preserve quota-enriched GPT-5.5 catalog names across model switch and app launch flows. ## Verification - PYTHONPATH="/home/terp/repos/codex-shim" python3 -m pytest "/home/terp/repos/codex-shim/tests" -q - PYTHONPATH="/home/terp/repos/codex-shim" python3 -m compileall "/home/terp/repos/codex-shim/codex_shim" -q - git diff --check Generated with [Devin](https://cli.devin.ai/docs)
Anthropic route headers now send only x-api-key (plus anthropic-version) and no longer also attach Authorization: Bearer <apiKey>. Some Anthropic-compatible gateways reject requests that carry both headers. Providers that genuinely require a bearer token can still supply one via extraHeaders. Co-authored-by: OnlyTerp <121772140+OnlyTerp@users.noreply.github.com>
Adds an optional "Auto (smart routing)" picker entry (slug codex-auto) that routes each Codex task to the cheapest configured model that can handle it. A cheap classifier model scores every candidate 0.0-1.0 from a capability card; the shim picks the cheapest candidate whose score clears a threshold (default 0.7), caches the decision per task, and falls back safely on any error so a request never breaks. The classifier never sees price, so it can't be biased toward expensive models. - codex_shim/router.py: config loading, task-signal extraction, classifier prompt, score parsing, cheapest-among-viable selection, per-task cache. - server.py: applies the router on /v1/responses, /v1/responses/compact, and /v1/chat/completions; runs the classifier over the configured backend; gates the virtual model in /v1/models, /api/models, /health, and the picker. - catalog.py + cli.py: catalog entry and `codex-shim list`/`model use` support. - Configured via an optional `router` block in ~/.codex-shim/models.json. Env knobs: CODEX_SHIM_DISABLE_ROUTER, CODEX_SHIM_ROUTER_TIMEOUT, CODEX_SHIM_ROUTER_MAX_TOKENS, CODEX_SHIM_ROUTER_LOG. - docs/AUTO_ROUTER.md, README section, and a runnable offline proof at examples/auto_router_demo.py. Verification: - python3 -m pytest tests/ -q (113 passed) - python3 -m compileall codex_shim/ examples/ -q - python3 examples/auto_router_demo.py (RESULT: PASS) Generated with [Devin](https://cli.devin.ai/docs)
… loop, concurrency) Expands Auto Router coverage from 27 to 48 offline tests with a real-ShimServer integration suite proving the production paths: - streaming /v1/responses through the router (response.completed + usage) - /v1/responses/compact through the router - /v1/chat/completions through the router - the agent tool-call loop: one classification per task is reused across follow-up turns (cache), while distinct tasks each get classified - OpenAI-shaped AND Anthropic-shaped classifiers, plus an Anthropic candidate - the exact HTTP the shim sends to the classifier (payload + auth headers, capability cards and task text present) - threshold tuning flows through from config (low keeps cheap, high escalates) - graceful fallback when the classifier returns non-JSON - discovery gating: health count excludes the virtual model, disable-env and unavailable candidates hide it, no-credential candidates are skipped - the picker switch accepts the auto slug - concurrency: many simultaneous requests route correctly under the shared cache Verification: - python3 -m pytest tests/ -q (134 passed) - python3 -m compileall codex_shim/ examples/ -q - python3 examples/auto_router_demo.py (RESULT: PASS) Generated with [Devin](https://cli.devin.ai/docs)
…lutions#30) _join_url previously special-cased only `/v1` as a known version segment, so bases such as Volces Ark's coding endpoint at `https://ark.cn-beijing.volces.com/api/coding/v3` ended up with a redundant `/v1/` injected (`/api/coding/v3/v1/chat/completions`) and returned 404. Generalise the check with a regex (`(?:^|/)v\d+$`) so anything ending in `/v1`, `/v3`, `/v4`, etc. is recognised and the endpoint is appended as-is. Order is preserved so the `/messages` fallback still kicks in for bare Anthropic-style hosts. This makes the existing test_join_url_handles_versioned_bases pass (it had been failing on upstream/main since merge — see the merge commit's verification note); no test changes.
…text-only (sybil-solutions#31) Reproduction (from production shim.log): an Auto Router request with an image went through the path where the classifier returned all-zero scores, which triggered pick_candidate -> None -> fallback_slug. The old fallback_slug honoured the configured `default` unconditionally, returning `deepseek-v4-flash` (text-only) for an image task. The shim forwarded the request to api.deepseek.com which rejected it with a confusing 400: Failed to deserialize the JSON body into the target type: messages[116]: unknown variant `image_url`, expected `text` That breaks the Auto Router's contract that routing must never break a request. Fix === `fallback_slug` now takes a new keyword-only flag `has_image_task` (default False, so old call sites and tests keep their behaviour). When True, the pool is restricted to candidates with `supports_images=True` *before* the default/cheapest selection runs. If no vision-capable candidate exists, fallback returns None so the caller can surface the failure explicitly rather than silently producing a 400 from a text-only upstream. The three internal call sites in router.resolve_auto (no-classifier, classifier-error, empty-scores) and the one final-fallback site in server.ShimServer._resolve_auto_model now thread the existing image signal (signal["has_images"] / router.has_images(body)) through. Tests ===== Three new tests in test_router.py encode the user-visible contract: test_fallback_slug_skips_image_incapable_when_task_has_image test_fallback_slug_image_task_with_no_vision_candidate_returns_none test_fallback_slug_default_kwarg_preserves_old_call_sites Full suite: 124 passed (was 121 + 3 new).
* feat: add OpenCode Go model refresh * fix: tighten display_name pattern, remove slug heuristic, add generated_by to test - Replace fragile v/k/m prefix checks in display_name_from_model_id with generic isalpha+isdigit pattern - Remove slug+URL heuristic from _is_opencode_go_row (was matching stale rows by URL alone); only check generated_by field - Add missing generated_by field to test fixture dict * fix: preserve user's existing settings key when writing OpenCode Go rows write_opencode_go_models used to pop customModels/launchModels/launch_models and force-write to 'models', silently migrating the user's schema. A user with only legacy customModels would see their config key replaced and their existing rows re-mapped without warning. Pick the first key the user already has; default to 'models' for new files. The preserved-row logic is unchanged.
…ybil-solutions#26) * feat: add Anthropic Messages bridge * fix: empty string for tool-only assistant content, deduplicate helpers, add bridge tests - Fix content: None → empty string in _anthropic_assistant_message_to_chat for tool-only assistant messages (some providers reject null content) - Deduplicate _chat_stream_finish_to_anthropic_stop and _anthropic_usage_from_normalized from server.py; import canonical versions from translate.py instead - Add 4 new tests: tool call streaming, reasoning streaming, Anthropic-shaped error responses, and Anthropic pass-through streaming - Add 2 new translate tests: tool-only content and reasoning blocks * fix: uuid message ids, model slug in passthrough SSE, preserve tool names - Use uuid4() for Anthropic message_id instead of timestamp (collision risk) - Rewrite model field in pass-through SSE message_start events - Preserve original tool names in _anthropic_tools_to_chat_tools and _anthropic_tool_choice_to_chat (remove _sanitize_tool_name mangling) --------- Co-authored-by: codex <codex@openai.com> Co-authored-by: test <test@local>
…etail, subscription docs - Add codex-shim doctor with OK/WARN/FAIL/INFO diagnostics - Protect /api/switch with per-process X-Codex-Shim-Picker-Token - Replace literal patch-app needles with regex + APPLIED markers - Normalize image detail original→high for chat-completions providers - Add docs/subscription-integration.md and CHANGELOG/README updates Co-authored-by: Cursor <cursoragent@cursor.com>
…open-backlog Integrate open backlog: doctor, CSRF, patch-app, image detail, docs
fix: native tool type mapping for freeform apply_patch and web_search - Maps original Responses tool types through streaming and non-streaming paths - Emit custom_tool_call for apply_patch (freeform) instead of generic function_call - Emit web_search_call for web_search tools so Codex Desktop handles them correctly - Add server-side DuckDuckGo web search for non-streaming BYOK responses - Add comprehensive tests for all tool type mapping scenarios
Brings the fork up to date with 0xSero/codex-shim (19 commits behind): Auto Router, host-header guard (DNS-rebind protection), Cursor Composer and OpenCode passthrough, Anthropic-Messages bridge for OpenAI-compatible chat providers, web /picker UI, native tool-type mapping, streaming-usage fixes, and a read-only `doctor` diagnostics command. Conflicts (codex_shim/cli.py only): - Imports: unioned both sides — kept our `getpass` (provider setup prompts) alongside upstream's `collections.Counter` and `importlib.util` (doctor). - Command dispatch: kept upstream's new `doctor` subcommand, adapted its call to the fork's local `port` variable (`status(port)` / `doctor(args.settings, port)`) instead of upstream's `args.port`, matching the surrounding dispatch. Fork-only provider-setup shortcuts (`setup`/`openrouter`/`minimax` and tests/test_provider_workflows.py) are preserved and coexist with upstream's doctor command. Full suite: 187 passed. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the fork up to date with
0xSero/codex-shim— was 19 commits behind.What this pulls in
router.py,docs/AUTO_ROUTER.md, demo + integration tests)hostguard.py) — security fixdoctorcommand — read-only local diagnosticscursor_passthrough.py)opencode_go.py)/pickerUI, native tool-type mapping, streaming-usage normalization, and assortedserver.py/translate.pyfixesConflict resolution (
codex_shim/cli.pyonly)getpass(provider-setup prompts) alongside upstream'scollections.Counterandimportlib.util(used bydoctor).doctorsubcommand, but adapted its call to the fork's localportvariable (status(port)/doctor(args.settings, port)) to match the surrounding dispatch, instead of upstream'sargs.port.Fork-only code preserved
The provider-setup shortcuts (
setup/openrouter/minimax) andtests/test_provider_workflows.pyare fork-only and coexist with upstream'sdoctorcommand. Verifiedsetup --help,doctor --help, and a livedoctorrun.Verification
python3 -m compileall codex_shim/clean187 passed(was 45 pre-merge; upstream adds router/hostguard/cursor/native-tool/doctor suites)Notes / follow-ups
mainand does not include PR Make provider setup append models instead of overwriting #9 (setup append-models). The two are independent; Make provider setup append models instead of overwriting #9 can rebase on top after this merges.doctor-commandbranch is now superseded by upstream'sdoctor— recommend closing/dropping it rather than merging, to avoid a duplicate command.🤖 Generated with Claude Code