coderouter-cli 2.15.0__tar.gz → 2.16.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/CHANGELOG.md +122 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/PKG-INFO +16 -4
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/README.en.md +15 -3
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/README.md +15 -3
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/adapters/anthropic_native.py +4 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/adapters/base.py +20 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/adapters/openai_compat.py +4 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/cli.py +67 -2
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/config/schemas.py +36 -2
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/doctor.py +48 -1
- coderouter_cli-2.16.0/coderouter/doctor_claude_code.py +181 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/context_budget.py +19 -0
- coderouter_cli-2.16.0/coderouter/ingress/anthropic_errors.py +185 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/ingress/anthropic_routes.py +213 -8
- coderouter_cli-2.16.0/coderouter/ingress/tool_search.py +100 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/routing/fallback.py +10 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/vscode_init.py +56 -6
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/claude-code-llamacpp-vllm.en.md +2 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/claude-code-llamacpp-vllm.md +2 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/install-backends.en.md +2 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/install-backends.md +2 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/launcher.en.md +1 -1
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/launcher.md +1 -1
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/llamacpp-direct.en.md +3 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/llamacpp-direct.md +2 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/context-budget.en.md +7 -6
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/context-budget.md +7 -6
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/vscode.md +12 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/start/quickstart.en.md +28 -31
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/start/quickstart.md +25 -31
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/start/when-do-i-need-coderouter.en.md +3 -5
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/start/when-do-i-need-coderouter.md +3 -5
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.cli-session.yaml +1 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.llamacpp-vllm.yaml +2 -1
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.yaml +3 -1
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/pyproject.toml +1 -1
- coderouter_cli-2.16.0/tests/test_anthropic_errors.py +352 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_context_budget.py +55 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_doctor.py +73 -0
- coderouter_cli-2.16.0/tests/test_doctor_claude_code.py +79 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_ingress_anthropic.py +15 -17
- coderouter_cli-2.16.0/tests/test_tool_search.py +136 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_vscode_init.py +80 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/.gitignore +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/LICENSE +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/__main__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/adapters/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/adapters/registry.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/cli_stats.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/config/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/config/capability_registry.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/config/env_file.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/config/loader.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/cost.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/credentials.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/data/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/data/model-capabilities.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/doctor_apply.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/env_security.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/errors.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/gguf_introspect.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/_fingerprint.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/backend_health.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/continuous_probe.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/drift_actions.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/drift_detection.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/memory_budget.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/memory_pressure.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/self_healing.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/guards/tool_loop.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/hardware.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/ingress/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/ingress/app.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/ingress/dashboard_routes.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/ingress/launcher_routes.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/ingress/metrics_routes.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/ingress/openai_routes.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/ingress/ui_auth.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/language_tax.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/launcher_devices.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/launcher_speculative.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/launcher_swap.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/logging.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/metrics/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/metrics/collector.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/metrics/prometheus.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/output_filters.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/plugins/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/plugins/base.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/plugins/loader.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/plugins/registry.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/py.typed +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/rollback.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/routing/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/routing/adaptive.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/routing/auto_router.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/routing/budget.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/routing/capability.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/routing/fallback_trace.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/secret_redaction.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/state/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/state/audit_log.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/state/replay.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/state/request_log.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/state/store.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/state/suggest_rules.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/token_estimation.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/token_estimation_accurate.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/translation/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/translation/anthropic.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/translation/convert.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/coderouter/translation/tool_repair.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/README.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/README.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/assets/dashboard-demo.png +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/external-agents.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/external-agents.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/gguf_dl.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/gguf_dl.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/hf-ollama-models.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/hf-ollama-models.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/launcher-quickstart.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/launcher-quickstart.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/lmstudio-direct.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/lmstudio-direct.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/backends/verify-ollama-0.23.1.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/architecture.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/architecture.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/continuous-probing.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/continuous-probing.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/drift-detection.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/drift-detection.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/partial-stitch.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/partial-stitch.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/stream-truncation.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/concepts/stream-truncation.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/designs/agent-cli-plugin-extraction.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/designs/external-agents-adapter.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/designs/launcher-model-swap.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/designs/launcher-multi-build.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/designs/orchestration-companion.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/designs/v1.5-dashboard-mockup.html +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/designs/v1.6-auto-router-verification.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/designs/v1.6-auto-router.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/free-tier-guide.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/free-tier-guide.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/language-tax.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/language-tax.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/remote-access.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/remote-access.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/security.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/security.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/subagent-routing.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/subagent-routing.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/troubleshooting.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/troubleshooting.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/usage-guide.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/guides/usage-guide.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/low-memory-integration.en.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/low-memory-integration.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/openrouter-roster/CHANGES.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/openrouter-roster/README.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/openrouter-roster/latest.json +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/retrospectives/v0.4.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/retrospectives/v0.5-verify.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/retrospectives/v0.5.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/retrospectives/v0.6.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/retrospectives/v0.7.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/retrospectives/v1.0-verify.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/docs/retrospectives/v1.0.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/.env.example +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/README.md +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers-agent-cli.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers-multiagent.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.context-budget-test.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.nvidia-nim.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.ollama-auto-custom.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.ollama-auto.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.ollama-free-chain.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.opusplan.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.raspberrypi.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/examples/providers.swap.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/scripts/demo_traffic.sh +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/scripts/openrouter_roster_diff.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/scripts/smoke_v2_2.sh +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/scripts/verify-providers.yaml +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/scripts/verify_ollama_0_23.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/scripts/verify_v0_5.sh +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/scripts/verify_v1_0.sh +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/__init__.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/conftest.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_adapter_anthropic.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_agent_cli.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_audit_log.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_auto_router.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_auto_router_cjk.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_backend_health.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_budget.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_capability.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_capability_degraded_payload.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_capability_registry.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_capability_registry_cache_control.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_capability_tool_choice.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_claude_code_suitability.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_cli.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_cli_bind_warning.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_cli_stats.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_config.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_config_cwd_warning.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_config_shim_actions.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_continuous_probe.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_credentials.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_dashboard_endpoint.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_doctor_apply.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_doctor_cache_probe.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_drift_actions.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_drift_detection.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_drift_detection_integration.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_env_file.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_env_security.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_errors.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_examples_yaml.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_examples_yaml_roundtrip.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_anthropic.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_cache_control.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_cache_observed.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_cache_strip.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_empty_response.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_misconfig_warn.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_paid_gate.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_reason.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_thinking.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_tool_choice_emulate.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fallback_tool_integrity.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_h1_prometheus_drift.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_h2_h6_translation.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_h3_client_reuse.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_h4_h5_guards.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_h7_probe_restore.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_h8_launcher_auth.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_mA_routing.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_mB_adapters.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_mC_convert.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_mD_logging_metrics.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_mE_config_validation.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_mF_ingress.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_fix_v213_launcher_hardening.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_gguf_introspect.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_guards_tool_loop.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_hardware.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_import_hygiene.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_ingress_count_tokens.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_ingress_profile.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_language_tax.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_language_tax_integration.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_backend_variants.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_config_bench.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_devices.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_devices_detect.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_devices_routes.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_gui_devices.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_gui_events.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_gui_readiness.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_gui_sweep_close.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_mtp.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_mtp_fallback.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_provider_sync.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_readiness_restart.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_swap.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_swap_review.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_variant_config.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_variant_gui.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_variant_routes.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_launcher_variant_sweep.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_memory_budget.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_memory_pressure.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_metrics_cache.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_metrics_collector.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_metrics_cost.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_metrics_endpoint.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_metrics_jsonl.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_metrics_prometheus.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_metrics_prometheus_cache.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_models_passthrough.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_openai_compat.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_openrouter_roster_diff.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_output_filters.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_output_filters_adapters.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_packaging.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_partial_stitch.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_plugin_adapter.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_plugins_integration.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_plugins_loader.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_plugins_registry.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_reasoning_strip.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_repair_byte_fallback.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_request_log.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_role_normalization.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_rollback.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_routing_adaptive.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_secret_redaction.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_self_healing.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_setup_sh.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_state_store.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_stop_reason_forward_compat.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_stream_truncation.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_token_estimation.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_token_estimation_accurate.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_tokens_saved_metric.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_tool_repair.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_tool_repair_differential.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_tool_repair_lenient.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_tool_repair_r4.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_tool_repair_scanners.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_toolrepair_bench.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_translation_anthropic.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_translation_reverse.py +0 -0
- {coderouter_cli-2.15.0 → coderouter_cli-2.16.0}/tests/test_ui_auth.py +0 -0
|
@@ -9,6 +9,128 @@ are kept verbatim where the Japanese text itself is the subject).
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
+
## [v2.16.0] — 2026-10-24 (Claude Code 2.1.3xx behind a gateway)
|
|
13
|
+
|
|
14
|
+
Claude Code 2.1.285–2.1.309 changed several defaults for sessions behind a
|
|
15
|
+
custom `ANTHROPIC_BASE_URL`: tool search is on, Opus 4.7+ / Sonnet 5+ /
|
|
16
|
+
Fable ids are treated as 1M-context models, auto mode asks the gateway for a
|
|
17
|
+
server-side classifier, and recovery from rejected features or an
|
|
18
|
+
overflowing prompt now depends on reading the gateway's error message. This
|
|
19
|
+
release makes CodeRouter answer in the shape those recoveries expect.
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **Anthropic-shaped errors when the whole chain fails (`/v1/messages`).**
|
|
24
|
+
A failed chain used to come back as FastAPI's `{"detail": "..."}` with
|
|
25
|
+
HTTP 502 whatever the upstreams said. It now uses Anthropic's envelope
|
|
26
|
+
(`{"type": "error", "error": {"type", "message"}}`) and a status chosen
|
|
27
|
+
from what the upstreams said (`coderouter/ingress/anthropic_errors.py`):
|
|
28
|
+
- an upstream context-overflow error (llama.cpp
|
|
29
|
+
`exceed_context_size_error`, vLLM / OpenAI "maximum context length",
|
|
30
|
+
Ollama "prompt too long", …) → **400 `invalid_request_error`, message
|
|
31
|
+
starting `prompt is too long`**, which is what triggers Claude Code's
|
|
32
|
+
reactive compaction. An overflow caused by the *completion* budget
|
|
33
|
+
("… 32000 in the completion", `max_tokens`) is not reported this way —
|
|
34
|
+
compacting the prompt cannot fix it — and goes out as a plain 400;
|
|
35
|
+
- a non-retryable upstream 400 / 422 → 400 with the upstream's own
|
|
36
|
+
message (Claude Code drops a rejected beta header / feature only when it
|
|
37
|
+
can read why);
|
|
38
|
+
- 413 → `request_too_large`; 429 → `rate_limit_error` with the upstream's
|
|
39
|
+
`retry-after` passed through;
|
|
40
|
+
- upstream 401 / 403 and everything else → 502 `api_error` (they concern
|
|
41
|
+
CodeRouter's own upstream credentials, not the client's).
|
|
42
|
+
Upstream messages are secret-redacted and length-capped. `AdapterError`
|
|
43
|
+
now keeps the upstream body (capped at 4096 chars) and `Retry-After`.
|
|
44
|
+
- **Streaming requests get a real HTTP status for pre-content failures.**
|
|
45
|
+
The route now pulls the engine's first event before committing the
|
|
46
|
+
response, so a chain that fails before producing anything is answered
|
|
47
|
+
with the status above instead of `200` plus an in-stream `error` event —
|
|
48
|
+
as api.anthropic.com does. Once the first event exists, nothing changes.
|
|
49
|
+
The wait shares the stream's overall M14 ceiling (one deadline, not two),
|
|
50
|
+
and a client that disconnects during it cancels the upstream attempt.
|
|
51
|
+
- **`deferred_tools_action` (profile; `auto` | `reject` | `expand` |
|
|
52
|
+
`passthrough`, default `auto`).** Claude Code 2.1.304+ sends a server-side
|
|
53
|
+
`tool_search_tool_*` and tools marked `defer_loading: true`; only
|
|
54
|
+
api.anthropic.com runs the search. `auto` passes the request through only
|
|
55
|
+
when every provider in the chain is `kind: anthropic` on api.anthropic.com
|
|
56
|
+
and otherwise answers 400, which makes Claude Code (2.1.305+) retry
|
|
57
|
+
without tool search. `expand` drops the search tool and the flags and
|
|
58
|
+
sends every tool up front.
|
|
59
|
+
- **`context_budget_action: reject`.** At `context_budget_trim_threshold`
|
|
60
|
+
the guard answers 400 `prompt is too long: ~N tokens > M maximum` instead
|
|
61
|
+
of trimming, so Claude Code summarizes the conversation itself rather
|
|
62
|
+
than losing history silently. Works for streaming and non-streaming.
|
|
63
|
+
- **`coderouter vscode-init` writes Claude Code gateway settings** by
|
|
64
|
+
default: `ENABLE_TOOL_SEARCH=false`, `CLAUDE_CODE_AUTO_MODE_SERVER=0`,
|
|
65
|
+
and — with the new `--max-context-tokens N` —
|
|
66
|
+
`CLAUDE_CODE_MAX_CONTEXT_TOKENS=N`. `--no-claude-code-env` opts out. Same
|
|
67
|
+
merge / conflict rules as the existing variables; an export of one of
|
|
68
|
+
them outside the `.envrc` fence conflicts only when this run writes it.
|
|
69
|
+
- **`coderouter doctor --check-claude-code`.** Local-only check of the
|
|
70
|
+
Claude Code install: warns on versions with known gateway bugs (before
|
|
71
|
+
2.1.301: compaction can repeat; 2.1.304: every request fails when the
|
|
72
|
+
gateway rejects tool search) and on missing `ENABLE_TOOL_SEARCH`,
|
|
73
|
+
`CLAUDE_CODE_MAX_CONTEXT_TOKENS` (2.1.285+) and
|
|
74
|
+
`CLAUDE_CODE_AUTO_MODE_SERVER`. Exit 0 OK / 2 needs attention.
|
|
75
|
+
|
|
76
|
+
### Changed
|
|
77
|
+
|
|
78
|
+
- **Breaking for clients that parse error bodies:** `/v1/messages` errors
|
|
79
|
+
for a failed chain are no longer `{"detail": "..."}`, and a streaming
|
|
80
|
+
request whose chain fails before the first event is no longer
|
|
81
|
+
`200` + `event: error`. Both now match the Anthropic API. The
|
|
82
|
+
`/v1/chat/completions` (OpenAI) route is unchanged.
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## [v2.15.1] — 2026-10-24 (docs and defaults for the October 2026 ecosystem)
|
|
87
|
+
|
|
88
|
+
### Added
|
|
89
|
+
|
|
90
|
+
- **`doctor` names llama.cpp's default-port change.** llama.cpp b11521
|
|
91
|
+
(2026-10-09, ggml-org/llama.cpp#30159) moved `llama-server`'s default
|
|
92
|
+
listen port from 8080 to 9931. When the `auth+basic-chat` probe cannot
|
|
93
|
+
connect to a loopback provider on :8080 or :9931, `doctor` now checks the
|
|
94
|
+
other port's `/health` (1.5 s, IPv4 literal) and, if it answers, says so
|
|
95
|
+
in the transport-error detail with the two possible fixes. The config is
|
|
96
|
+
never rewritten. Other hosts and ports send no extra request.
|
|
97
|
+
- **SBOM on every release.** `release.yml` builds a CycloneDX JSON SBOM
|
|
98
|
+
(`cyclonedx-bom==7.4.0`) from a clean venv holding only the wheel and its
|
|
99
|
+
runtime dependencies, and attaches `coderouter-cli-<version>.cdx.json` to
|
|
100
|
+
the GitHub Release draft. It is written outside `dist/`, so the PyPI
|
|
101
|
+
upload is unchanged.
|
|
102
|
+
|
|
103
|
+
### Changed
|
|
104
|
+
|
|
105
|
+
- **Codex CLI instructions corrected.** The quickstart (Pattern B), README
|
|
106
|
+
and the "do I need CodeRouter?" guide told users to set
|
|
107
|
+
`wire_api = "chat"`. Codex removed that option in 2026-02 and now fails
|
|
108
|
+
while loading such a config; it speaks only the Responses API, which
|
|
109
|
+
CodeRouter does not serve yet. The docs now say so and give the
|
|
110
|
+
direct-connect workaround (`codex --oss`, or a `wire_api = "responses"`
|
|
111
|
+
provider pointing at `llama-server`).
|
|
112
|
+
- **llama.cpp port notes.** Backend guides and examples keep their explicit
|
|
113
|
+
`--port 8080` (correct on every build) and now explain the 9931 default
|
|
114
|
+
for servers started without `--port`. The Launcher is unaffected: it
|
|
115
|
+
always passes `--port`, so its 8080 default stays.
|
|
116
|
+
- **README: package-name warning.** The PyPI project named `coderouter` is
|
|
117
|
+
a different product; the install section now says to install
|
|
118
|
+
`coderouter-cli`.
|
|
119
|
+
- **README: repair benchmark and release protections.** The tool-call
|
|
120
|
+
repair row links the benchmark and its numbers, and the Security section
|
|
121
|
+
lists the release protections as layers rather than resting on
|
|
122
|
+
Trusted Publishing alone.
|
|
123
|
+
|
|
124
|
+
### Security
|
|
125
|
+
|
|
126
|
+
- **Lockfile bumps for newly published advisories.** `anyio` 4.13.0 →
|
|
127
|
+
4.14.2 (PYSEC-2026-4024, PYSEC-2026-4025), `certifi` 2026.2.25 →
|
|
128
|
+
2026.10.23 (PYSEC-2026-4320), `fsspec` 2026.4.0 → 2026.9.0
|
|
129
|
+
(PYSEC-2026-4217; reached only through the optional `[accuracy]` extra
|
|
130
|
+
via `huggingface-hub`). Found by the CI dependency audit.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
12
134
|
## [v2.15.0] — 2026-08-21 (fallback explainability + stream truncation detection)
|
|
13
135
|
|
|
14
136
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: coderouter-cli
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.16.0
|
|
4
4
|
Summary: Local-first, free-first, fallback-built-in LLM router. Claude Code / OpenAI compatible.
|
|
5
5
|
Project-URL: Homepage, https://github.com/zephel01/CodeRouter
|
|
6
6
|
Project-URL: Repository, https://github.com/zephel01/CodeRouter
|
|
@@ -66,7 +66,7 @@ Description-Content-Type: text/markdown
|
|
|
66
66
|
## 何ができるか — 30 秒で
|
|
67
67
|
|
|
68
68
|
```
|
|
69
|
-
あなたのエージェント (Claude Code /
|
|
69
|
+
あなたのエージェント (Claude Code / agy ほか ※)
|
|
70
70
|
│
|
|
71
71
|
▼
|
|
72
72
|
┌─ CodeRouter ─┐
|
|
@@ -76,6 +76,8 @@ Description-Content-Type: text/markdown
|
|
|
76
76
|
└──────────────┘
|
|
77
77
|
```
|
|
78
78
|
|
|
79
|
+
※ 現行の codex CLI は Responses API(`/v1/responses`)専用になったため、CodeRouter 経由ではまだ使えません(対応予定)。直結の代替手順 → [quickstart Pattern B](./docs/start/quickstart.md#pattern-b-codex-cli-で使う)
|
|
80
|
+
|
|
79
81
|
**やってくれること:**
|
|
80
82
|
|
|
81
83
|
- ローカルモデルが壊した tool calling を Claude Code に届く前に修復する
|
|
@@ -118,6 +120,8 @@ uvx --from coderouter-cli coderouter serve --port 8088
|
|
|
118
120
|
|
|
119
121
|
恒久インストールしたい場合: `uv tool install coderouter-cli`
|
|
120
122
|
|
|
123
|
+
> **パッケージ名に注意**: PyPI のパッケージ名は **`coderouter-cli`** です。PyPI の `coderouter`(名前だけ同じ)は別の製品なので、`pip install coderouter` と打たないでください。
|
|
124
|
+
|
|
121
125
|
---
|
|
122
126
|
|
|
123
127
|
## Claude Code で使う
|
|
@@ -143,7 +147,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
143
147
|
| Claude Code + ローカル Ollama で tool calling が壊れる | **必須** — tool 修復 (+ 必要なら wire 変換) |
|
|
144
148
|
| Claude Code + ローカルで長時間回すと止まる | **必須級** — 6 系統ガード + self-healing |
|
|
145
149
|
| Ollama v0.14+ / LM Studio にネイティブ直結で動いてる | **便利** — 直結に無い fallback / ガード / 診断を追加 (passthrough で翻訳ゼロ) |
|
|
146
|
-
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら |
|
|
150
|
+
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら(codex は `/v1/responses` 対応後) |
|
|
147
151
|
| Claude API を直接叩いてて問題ない | 不要 |
|
|
148
152
|
|
|
149
153
|
詳細は → [要否判定ガイド](./docs/start/when-do-i-need-coderouter.md)
|
|
@@ -156,7 +160,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
156
160
|
|
|
157
161
|
| 機能 | 何をしてくれるか |
|
|
158
162
|
|---|---|
|
|
159
|
-
| **Tool-call 修復** | ローカルモデルがテキストで吐いた JSON を正しい tool_use
|
|
163
|
+
| **Tool-call 修復** | ローカルモデルがテキストで吐いた JSON を正しい tool_use ブロックに復元。実測は [修復ベンチ](./benchmarks/tool-repair/README.md)(壊れた tool call を出す 4 モデルで成功率 0% → 80〜100%、回帰コーパスで誤修復 0 件。CI でゲート) |
|
|
160
164
|
| **3 層フォールバック** | ローカル → 無料クラウド → 有料の順に自動切替 |
|
|
161
165
|
| **出力フィルタ** | `<think>` タグ漏れ、stop marker 漏れ、byte-fallback (`<0xNN>`) を自動除去/修復 |
|
|
162
166
|
| **Wire 翻訳** | Anthropic 形式 ↔ OpenAI 形式を自動変換 (ネイティブ `/v1/messages` 対応 backend は passthrough で翻訳ゼロ) |
|
|
@@ -333,6 +337,14 @@ CodeRouter は backend ルーター層として独立して動きます。`OPENA
|
|
|
333
337
|
|
|
334
338
|
シークレットは環境変数に置きます。[`docs/security.md`](./docs/guides/security.md) に完全な方針と報告手順があります。
|
|
335
339
|
|
|
340
|
+
リリースは多層で守っています。どれか 1 つを根拠に「安全」とは言いません(正規のパイプラインでビルドされた汚染版が出た例があります)。
|
|
341
|
+
|
|
342
|
+
- PyPI へは Trusted Publishing(OIDC)で公開し、長期の API トークンを置かない
|
|
343
|
+
- PyPI 側の公開許可は `release.yml` の `pypi` 環境に限定している
|
|
344
|
+
- タグは事前検査スクリプトを通してから打つ(version・CHANGELOG・main の一致を機械的に確認)
|
|
345
|
+
- 実行時の依存は 5 個に絞っている
|
|
346
|
+
- 各リリースに SBOM(CycloneDX)を添付する
|
|
347
|
+
|
|
336
348
|
## License
|
|
337
349
|
|
|
338
350
|
MIT
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
## What it does — in 30 seconds
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
Your agent (Claude Code /
|
|
24
|
+
Your agent (Claude Code / agy, etc. *)
|
|
25
25
|
│
|
|
26
26
|
▼
|
|
27
27
|
┌─ CodeRouter ──┐
|
|
@@ -31,6 +31,8 @@ Your agent (Claude Code / codex / agy)
|
|
|
31
31
|
└────────────────┘
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
\* Current codex CLI speaks only the Responses API (`/v1/responses`), so it cannot go through CodeRouter yet (planned). Direct-connect workaround → [quickstart Pattern B](./docs/start/quickstart.en.md#pattern-b-use-it-with-the-codex-cli)
|
|
35
|
+
|
|
34
36
|
**What it does for you:**
|
|
35
37
|
|
|
36
38
|
- Repairs broken tool calling from local models before it reaches Claude Code
|
|
@@ -55,6 +57,8 @@ uvx --from coderouter-cli coderouter serve --port 8088
|
|
|
55
57
|
|
|
56
58
|
For a permanent install: `uv tool install coderouter-cli`
|
|
57
59
|
|
|
60
|
+
> **Mind the package name**: the PyPI package is **`coderouter-cli`**. The PyPI package named `coderouter` is a different product; don't `pip install coderouter`.
|
|
61
|
+
|
|
58
62
|
---
|
|
59
63
|
|
|
60
64
|
## Use with Claude Code
|
|
@@ -79,7 +83,7 @@ That's it. Claude Code works as usual, but your local Ollama is answering behind
|
|
|
79
83
|
|---|---|
|
|
80
84
|
| Claude Code + local Ollama, tool calling breaks | **Yes** — wire translation + tool repair |
|
|
81
85
|
| Claude Code + local, dies after long sessions | **Helpful** — 6 guards + self-healing |
|
|
82
|
-
| codex / agy + Ollama works fine | Optional — if you want fallback |
|
|
86
|
+
| codex / agy + Ollama works fine | Optional — if you want fallback (codex: once `/v1/responses` lands) |
|
|
83
87
|
| Using Claude API directly, no issues | Not needed |
|
|
84
88
|
|
|
85
89
|
Full decision matrix → [Do I need CodeRouter?](./docs/start/when-do-i-need-coderouter.en.md)
|
|
@@ -93,7 +97,7 @@ Full decision matrix → [Do I need CodeRouter?](./docs/start/when-do-i-need-cod
|
|
|
93
97
|
| Feature | What it does |
|
|
94
98
|
|---|---|
|
|
95
99
|
| **Wire translation** | Claude Code (Anthropic format) ↔ Ollama (OpenAI format) auto-converted |
|
|
96
|
-
| **Tool-call repair** | JSON that local models emit as plain text → valid tool_use blocks |
|
|
100
|
+
| **Tool-call repair** | JSON that local models emit as plain text → valid tool_use blocks. Measured in the [repair benchmark](./benchmarks/tool-repair/README.md): four models that break tool calls go from 0% to 80–100% success; zero false repairs on the regression corpus, gated in CI |
|
|
97
101
|
| **3-tier fallback** | Local → free cloud → paid, automatic switching |
|
|
98
102
|
| **Output filters** | Strips leaked `<think>` tags, stop markers, XML tool tags |
|
|
99
103
|
|
|
@@ -268,6 +272,14 @@ CodeRouter runs as an independent backend router layer. Point any project's `OPE
|
|
|
268
272
|
|
|
269
273
|
Secrets go in env vars, not config files. See [`docs/security.en.md`](./docs/guides/security.en.md) for the full policy and reporting instructions.
|
|
270
274
|
|
|
275
|
+
Releases are protected in layers. No single one of these is offered as proof of safety (tampered builds have shipped from legitimate pipelines before).
|
|
276
|
+
|
|
277
|
+
- Published to PyPI via Trusted Publishing (OIDC); no long-lived API token exists
|
|
278
|
+
- PyPI accepts uploads only from `release.yml` running in the `pypi` environment
|
|
279
|
+
- Tags are cut only after a pre-flight script checks version, CHANGELOG and main
|
|
280
|
+
- Five runtime dependencies
|
|
281
|
+
- An SBOM (CycloneDX) is attached to every release
|
|
282
|
+
|
|
271
283
|
## License
|
|
272
284
|
|
|
273
285
|
MIT
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
## 何ができるか — 30 秒で
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
あなたのエージェント (Claude Code /
|
|
24
|
+
あなたのエージェント (Claude Code / agy ほか ※)
|
|
25
25
|
│
|
|
26
26
|
▼
|
|
27
27
|
┌─ CodeRouter ─┐
|
|
@@ -31,6 +31,8 @@
|
|
|
31
31
|
└──────────────┘
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
※ 現行の codex CLI は Responses API(`/v1/responses`)専用になったため、CodeRouter 経由ではまだ使えません(対応予定)。直結の代替手順 → [quickstart Pattern B](./docs/start/quickstart.md#pattern-b-codex-cli-で使う)
|
|
35
|
+
|
|
34
36
|
**やってくれること:**
|
|
35
37
|
|
|
36
38
|
- ローカルモデルが壊した tool calling を Claude Code に届く前に修復する
|
|
@@ -73,6 +75,8 @@ uvx --from coderouter-cli coderouter serve --port 8088
|
|
|
73
75
|
|
|
74
76
|
恒久インストールしたい場合: `uv tool install coderouter-cli`
|
|
75
77
|
|
|
78
|
+
> **パッケージ名に注意**: PyPI のパッケージ名は **`coderouter-cli`** です。PyPI の `coderouter`(名前だけ同じ)は別の製品なので、`pip install coderouter` と打たないでください。
|
|
79
|
+
|
|
76
80
|
---
|
|
77
81
|
|
|
78
82
|
## Claude Code で使う
|
|
@@ -98,7 +102,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
98
102
|
| Claude Code + ローカル Ollama で tool calling が壊れる | **必須** — tool 修復 (+ 必要なら wire 変換) |
|
|
99
103
|
| Claude Code + ローカルで長時間回すと止まる | **必須級** — 6 系統ガード + self-healing |
|
|
100
104
|
| Ollama v0.14+ / LM Studio にネイティブ直結で動いてる | **便利** — 直結に無い fallback / ガード / 診断を追加 (passthrough で翻訳ゼロ) |
|
|
101
|
-
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら |
|
|
105
|
+
| codex / agy + Ollama 直繋ぎで動いてる | オプション — フォールバックが欲しいなら(codex は `/v1/responses` 対応後) |
|
|
102
106
|
| Claude API を直接叩いてて問題ない | 不要 |
|
|
103
107
|
|
|
104
108
|
詳細は → [要否判定ガイド](./docs/start/when-do-i-need-coderouter.md)
|
|
@@ -111,7 +115,7 @@ ANTHROPIC_BASE_URL=http://localhost:8088 ANTHROPIC_AUTH_TOKEN=dummy claude
|
|
|
111
115
|
|
|
112
116
|
| 機能 | 何をしてくれるか |
|
|
113
117
|
|---|---|
|
|
114
|
-
| **Tool-call 修復** | ローカルモデルがテキストで吐いた JSON を正しい tool_use
|
|
118
|
+
| **Tool-call 修復** | ローカルモデルがテキストで吐いた JSON を正しい tool_use ブロックに復元。実測は [修復ベンチ](./benchmarks/tool-repair/README.md)(壊れた tool call を出す 4 モデルで成功率 0% → 80〜100%、回帰コーパスで誤修復 0 件。CI でゲート) |
|
|
115
119
|
| **3 層フォールバック** | ローカル → 無料クラウド → 有料の順に自動切替 |
|
|
116
120
|
| **出力フィルタ** | `<think>` タグ漏れ、stop marker 漏れ、byte-fallback (`<0xNN>`) を自動除去/修復 |
|
|
117
121
|
| **Wire 翻訳** | Anthropic 形式 ↔ OpenAI 形式を自動変換 (ネイティブ `/v1/messages` 対応 backend は passthrough で翻訳ゼロ) |
|
|
@@ -288,6 +292,14 @@ CodeRouter は backend ルーター層として独立して動きます。`OPENA
|
|
|
288
292
|
|
|
289
293
|
シークレットは環境変数に置きます。[`docs/security.md`](./docs/guides/security.md) に完全な方針と報告手順があります。
|
|
290
294
|
|
|
295
|
+
リリースは多層で守っています。どれか 1 つを根拠に「安全」とは言いません(正規のパイプラインでビルドされた汚染版が出た例があります)。
|
|
296
|
+
|
|
297
|
+
- PyPI へは Trusted Publishing(OIDC)で公開し、長期の API トークンを置かない
|
|
298
|
+
- PyPI 側の公開許可は `release.yml` の `pypi` 環境に限定している
|
|
299
|
+
- タグは事前検査スクリプトを通してから打つ(version・CHANGELOG・main の一致を機械的に確認)
|
|
300
|
+
- 実行時の依存は 5 個に絞っている
|
|
301
|
+
- 各リリースに SBOM(CycloneDX)を添付する
|
|
302
|
+
|
|
291
303
|
## License
|
|
292
304
|
|
|
293
305
|
MIT
|
|
@@ -458,6 +458,8 @@ class AnthropicAdapter(BaseAdapter):
|
|
|
458
458
|
provider=self.name,
|
|
459
459
|
status_code=resp.status_code,
|
|
460
460
|
retryable=resp.status_code in _RETRYABLE_STATUSES,
|
|
461
|
+
upstream_body=resp.text,
|
|
462
|
+
retry_after=resp.headers.get("retry-after"),
|
|
461
463
|
)
|
|
462
464
|
|
|
463
465
|
try:
|
|
@@ -570,6 +572,8 @@ class AnthropicAdapter(BaseAdapter):
|
|
|
570
572
|
provider=self.name,
|
|
571
573
|
status_code=resp.status_code,
|
|
572
574
|
retryable=resp.status_code in _RETRYABLE_STATUSES,
|
|
575
|
+
upstream_body=body.decode("utf-8", errors="replace"),
|
|
576
|
+
retry_after=resp.headers.get("retry-after"),
|
|
573
577
|
)
|
|
574
578
|
|
|
575
579
|
# Anthropic SSE block shape:
|
|
@@ -101,6 +101,12 @@ class StreamChunk(BaseModel):
|
|
|
101
101
|
usage: dict[str, Any] | None = None
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
#: v2.16.0: max chars of an upstream error body an :class:`AdapterError`
|
|
105
|
+
#: keeps. Error bodies are small; the cap only bounds a misbehaving upstream
|
|
106
|
+
#: that answers an error status with a large HTML page.
|
|
107
|
+
UPSTREAM_BODY_CAP = 4096
|
|
108
|
+
|
|
109
|
+
|
|
104
110
|
class AdapterError(CodeRouterError):
|
|
105
111
|
"""Raised when a provider call fails in a way the fallback engine should retry on."""
|
|
106
112
|
|
|
@@ -111,6 +117,8 @@ class AdapterError(CodeRouterError):
|
|
|
111
117
|
provider: str,
|
|
112
118
|
status_code: int | None = None,
|
|
113
119
|
retryable: bool = True,
|
|
120
|
+
upstream_body: str | None = None,
|
|
121
|
+
retry_after: str | None = None,
|
|
114
122
|
) -> None:
|
|
115
123
|
"""Construct an AdapterError.
|
|
116
124
|
|
|
@@ -125,11 +133,23 @@ class AdapterError(CodeRouterError):
|
|
|
125
133
|
retryable: When True, the fallback engine may try the next
|
|
126
134
|
provider in the chain. When False, the engine stops
|
|
127
135
|
and surfaces the error as a terminal failure.
|
|
136
|
+
upstream_body: v2.16.0 — the upstream's error body (capped at
|
|
137
|
+
:data:`UPSTREAM_BODY_CAP` chars), kept so the ingress can
|
|
138
|
+
hand the upstream's own wording back to the client.
|
|
139
|
+
Claude Code recovers from several 400s (rejected beta
|
|
140
|
+
headers, tool search, context overflow) only when it can
|
|
141
|
+
read the upstream message. Never logged by this class.
|
|
142
|
+
retry_after: v2.16.0 — the upstream's ``Retry-After`` header
|
|
143
|
+
value, verbatim, when it sent one.
|
|
128
144
|
"""
|
|
129
145
|
super().__init__(message)
|
|
130
146
|
self.provider = provider
|
|
131
147
|
self.status_code = status_code
|
|
132
148
|
self.retryable = retryable
|
|
149
|
+
self.upstream_body = (
|
|
150
|
+
upstream_body[:UPSTREAM_BODY_CAP] if upstream_body is not None else None
|
|
151
|
+
)
|
|
152
|
+
self.retry_after = retry_after
|
|
133
153
|
|
|
134
154
|
def __str__(self) -> str:
|
|
135
155
|
"""Render as ``[provider status=NNN] message`` for log trails."""
|
|
@@ -244,6 +244,8 @@ class OpenAICompatAdapter(BaseAdapter):
|
|
|
244
244
|
provider=self.name,
|
|
245
245
|
status_code=resp.status_code,
|
|
246
246
|
retryable=resp.status_code in _RETRYABLE_STATUSES,
|
|
247
|
+
upstream_body=resp.text,
|
|
248
|
+
retry_after=resp.headers.get("retry-after"),
|
|
247
249
|
)
|
|
248
250
|
|
|
249
251
|
try:
|
|
@@ -381,6 +383,8 @@ class OpenAICompatAdapter(BaseAdapter):
|
|
|
381
383
|
provider=self.name,
|
|
382
384
|
status_code=resp.status_code,
|
|
383
385
|
retryable=resp.status_code in _RETRYABLE_STATUSES,
|
|
386
|
+
upstream_body=body.decode("utf-8", errors="replace"),
|
|
387
|
+
retry_after=resp.headers.get("retry-after"),
|
|
384
388
|
)
|
|
385
389
|
async for line in resp.aiter_lines():
|
|
386
390
|
if not line:
|
|
@@ -165,6 +165,17 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
165
165
|
"for live key values. Exit 0 clean / 2 needs attention / 1 leak."
|
|
166
166
|
),
|
|
167
167
|
)
|
|
168
|
+
doctor.add_argument(
|
|
169
|
+
"--check-claude-code",
|
|
170
|
+
action="store_true",
|
|
171
|
+
help=(
|
|
172
|
+
"v2.16.0: check the local Claude Code install for gateway use — "
|
|
173
|
+
"known-bad versions (<2.1.301, 2.1.304) and the env vars that "
|
|
174
|
+
"matter behind a non-Anthropic gateway (ENABLE_TOOL_SEARCH, "
|
|
175
|
+
"CLAUDE_CODE_MAX_CONTEXT_TOKENS, CLAUDE_CODE_AUTO_MODE_SERVER). "
|
|
176
|
+
"No network. Exit 0 OK / 2 needs attention."
|
|
177
|
+
),
|
|
178
|
+
)
|
|
168
179
|
doctor.add_argument(
|
|
169
180
|
"--config",
|
|
170
181
|
default=None,
|
|
@@ -454,6 +465,28 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
454
465
|
"generation. Tip: keep secrets in a separate .envrc.local."
|
|
455
466
|
),
|
|
456
467
|
)
|
|
468
|
+
vscode_init.add_argument(
|
|
469
|
+
"--max-context-tokens",
|
|
470
|
+
type=int,
|
|
471
|
+
default=None,
|
|
472
|
+
metavar="N",
|
|
473
|
+
help=(
|
|
474
|
+
"v2.16.0: also write CLAUDE_CODE_MAX_CONTEXT_TOKENS=N so Claude "
|
|
475
|
+
"Code compacts before your local model's context fills up "
|
|
476
|
+
"(since 2.1.285 it assumes 1M behind a gateway). Use the "
|
|
477
|
+
"smallest context window in the profile's chain."
|
|
478
|
+
),
|
|
479
|
+
)
|
|
480
|
+
vscode_init.add_argument(
|
|
481
|
+
"--no-claude-code-env",
|
|
482
|
+
action="store_true",
|
|
483
|
+
help=(
|
|
484
|
+
"v2.16.0: do not write the Claude Code gateway settings "
|
|
485
|
+
"(ENABLE_TOOL_SEARCH=false, CLAUDE_CODE_AUTO_MODE_SERVER=0, "
|
|
486
|
+
"and --max-context-tokens). Use when the chain is "
|
|
487
|
+
"api.anthropic.com only."
|
|
488
|
+
),
|
|
489
|
+
)
|
|
457
490
|
vscode_init.add_argument(
|
|
458
491
|
"--dry-run",
|
|
459
492
|
action="store_true",
|
|
@@ -595,10 +628,16 @@ def _run_doctor(args: argparse.Namespace) -> int:
|
|
|
595
628
|
risks AND broken providers can use a single command.
|
|
596
629
|
"""
|
|
597
630
|
check_secrets = bool(getattr(args, "check_secrets", False))
|
|
598
|
-
|
|
631
|
+
check_claude_code = bool(getattr(args, "check_claude_code", False))
|
|
632
|
+
if (
|
|
633
|
+
args.check_model is None
|
|
634
|
+
and args.check_env is None
|
|
635
|
+
and not check_secrets
|
|
636
|
+
and not check_claude_code
|
|
637
|
+
):
|
|
599
638
|
print(
|
|
600
639
|
"doctor: provide --check-model PROVIDER, --check-env [PATH], "
|
|
601
|
-
"and/or --check-
|
|
640
|
+
"--check-secrets and/or --check-claude-code",
|
|
602
641
|
file=sys.stderr,
|
|
603
642
|
)
|
|
604
643
|
return 1
|
|
@@ -614,6 +653,14 @@ def _run_doctor(args: argparse.Namespace) -> int:
|
|
|
614
653
|
if check_secrets:
|
|
615
654
|
worst_exit = max(worst_exit, _run_check_secrets(args.config))
|
|
616
655
|
|
|
656
|
+
if check_claude_code:
|
|
657
|
+
from coderouter.doctor_claude_code import format_report as _cc_format
|
|
658
|
+
from coderouter.doctor_claude_code import run_check as _cc_run
|
|
659
|
+
|
|
660
|
+
cc_report = _cc_run()
|
|
661
|
+
print(_cc_format(cc_report))
|
|
662
|
+
worst_exit = max(worst_exit, cc_report.exit_code)
|
|
663
|
+
|
|
617
664
|
if args.check_model is not None:
|
|
618
665
|
worst_exit = max(worst_exit, _run_check_model(args))
|
|
619
666
|
|
|
@@ -976,6 +1023,7 @@ def _run_vscode_init(args: argparse.Namespace) -> int:
|
|
|
976
1023
|
"""
|
|
977
1024
|
from coderouter.vscode_init import (
|
|
978
1025
|
DEFAULT_PORT,
|
|
1026
|
+
claude_code_env,
|
|
979
1027
|
exit_code_for,
|
|
980
1028
|
format_result,
|
|
981
1029
|
run_vscode_init,
|
|
@@ -991,6 +1039,22 @@ def _run_vscode_init(args: argparse.Namespace) -> int:
|
|
|
991
1039
|
|
|
992
1040
|
port = args.port if args.port is not None else DEFAULT_PORT
|
|
993
1041
|
|
|
1042
|
+
extra_env: dict[str, str] | None = None
|
|
1043
|
+
max_ctx = getattr(args, "max_context_tokens", None)
|
|
1044
|
+
if getattr(args, "no_claude_code_env", False):
|
|
1045
|
+
if max_ctx is not None:
|
|
1046
|
+
print(
|
|
1047
|
+
"vscode-init: --max-context-tokens has no effect with --no-claude-code-env",
|
|
1048
|
+
file=sys.stderr,
|
|
1049
|
+
)
|
|
1050
|
+
return 2
|
|
1051
|
+
else:
|
|
1052
|
+
try:
|
|
1053
|
+
extra_env = claude_code_env(max_context_tokens=max_ctx)
|
|
1054
|
+
except ValueError as exc:
|
|
1055
|
+
print(f"vscode-init: {exc}", file=sys.stderr)
|
|
1056
|
+
return 2
|
|
1057
|
+
|
|
994
1058
|
try:
|
|
995
1059
|
result = run_vscode_init(
|
|
996
1060
|
target,
|
|
@@ -999,6 +1063,7 @@ def _run_vscode_init(args: argparse.Namespace) -> int:
|
|
|
999
1063
|
with_envrc=args.with_envrc,
|
|
1000
1064
|
dry_run=args.dry_run,
|
|
1001
1065
|
force=args.force,
|
|
1066
|
+
extra_env=extra_env,
|
|
1002
1067
|
)
|
|
1003
1068
|
except FileNotFoundError as exc:
|
|
1004
1069
|
# Raised only when target vanishes between the pre-check above
|
|
@@ -1089,7 +1089,13 @@ class FallbackChain(BaseModel):
|
|
|
1089
1089
|
# (``context_budget_preserve_last_n``) are always
|
|
1090
1090
|
# kept, and tool_use / tool_result pairs are preserved
|
|
1091
1091
|
# atomically to avoid breaking agent loops.
|
|
1092
|
-
|
|
1092
|
+
# * ``reject`` — v2.16.0: ``warn`` + at ``context_budget_trim_threshold``
|
|
1093
|
+
# refuse the request with Anthropic's own overflow error
|
|
1094
|
+
# (HTTP 400, ``prompt is too long: ...``). Claude Code
|
|
1095
|
+
# answers that by compacting the conversation itself
|
|
1096
|
+
# (a summary, not a silent cut), so this is the
|
|
1097
|
+
# recommended action for Claude Code sessions.
|
|
1098
|
+
context_budget_action: Literal["off", "warn", "trim", "reject"] = Field(
|
|
1093
1099
|
default="off",
|
|
1094
1100
|
description=(
|
|
1095
1101
|
"v2.0-F (L1): action when estimated request tokens approach "
|
|
@@ -1097,7 +1103,10 @@ class FallbackChain(BaseModel):
|
|
|
1097
1103
|
"disables the guard entirely. ``warn`` emits a log and "
|
|
1098
1104
|
"response header at ``context_budget_warn_threshold``. "
|
|
1099
1105
|
"``trim`` additionally removes old messages at "
|
|
1100
|
-
"``context_budget_trim_threshold`` to reclaim context space."
|
|
1106
|
+
"``context_budget_trim_threshold`` to reclaim context space. "
|
|
1107
|
+
"``reject`` (v2.16.0) instead answers HTTP 400 "
|
|
1108
|
+
"``prompt is too long`` at that threshold so the client "
|
|
1109
|
+
"compacts the conversation itself (Claude Code does)."
|
|
1101
1110
|
),
|
|
1102
1111
|
)
|
|
1103
1112
|
context_budget_warn_threshold: float = Field(
|
|
@@ -1222,6 +1231,31 @@ class FallbackChain(BaseModel):
|
|
|
1222
1231
|
# --- v2.0-H (L6): Mid-stream partial stitching --------------------------
|
|
1223
1232
|
# * ``off`` — discard partial content on mid-stream failure (legacy).
|
|
1224
1233
|
# * ``surface`` — return partial content as a truncated-but-valid response.
|
|
1234
|
+
# v2.16.0 (tool search): Claude Code 2.1.304+ turns tool search on behind
|
|
1235
|
+
# a custom ANTHROPIC_BASE_URL. The request then carries a server-side
|
|
1236
|
+
# tool-search tool (``type: tool_search_tool_*``) and tools marked
|
|
1237
|
+
# ``defer_loading: true``, which only api.anthropic.com executes. A
|
|
1238
|
+
# local model sees the search tool as an ordinary function (and may
|
|
1239
|
+
# "call" it), while every deferred MCP tool lands in its context anyway.
|
|
1240
|
+
# Claude Code retries without tool search when the gateway answers 400.
|
|
1241
|
+
# * ``auto`` — ``passthrough`` when every provider in the chain
|
|
1242
|
+
# is ``kind: anthropic`` on api.anthropic.com,
|
|
1243
|
+
# otherwise ``reject`` (default).
|
|
1244
|
+
# * ``reject`` — answer 400 so the client turns tool search off.
|
|
1245
|
+
# * ``expand`` — drop the search tool and the ``defer_loading``
|
|
1246
|
+
# flags; every tool is sent up front.
|
|
1247
|
+
# * ``passthrough`` — forward unchanged (pre-v2.16.0 behaviour).
|
|
1248
|
+
deferred_tools_action: Literal["auto", "reject", "expand", "passthrough"] = Field(
|
|
1249
|
+
default="auto",
|
|
1250
|
+
description=(
|
|
1251
|
+
"v2.16.0: what to do with Claude Code tool search "
|
|
1252
|
+
"(``defer_loading`` tools / ``tool_search_tool_*``). ``auto`` "
|
|
1253
|
+
"passes it through only for chains made entirely of "
|
|
1254
|
+
"api.anthropic.com providers and rejects it (HTTP 400, which "
|
|
1255
|
+
"makes Claude Code retry without tool search) otherwise."
|
|
1256
|
+
),
|
|
1257
|
+
)
|
|
1258
|
+
|
|
1225
1259
|
partial_stitch_action: Literal["off", "surface"] = Field(
|
|
1226
1260
|
default="off",
|
|
1227
1261
|
description=(
|
|
@@ -635,6 +635,49 @@ _PROBE_TOOL_SPEC_ANTHROPIC = {
|
|
|
635
635
|
}
|
|
636
636
|
|
|
637
637
|
|
|
638
|
+
# v2.15.1: llama.cpp changed ``llama-server``'s default listen port from
|
|
639
|
+
# 8080 to 9931 (ggml-org/llama.cpp#30159, build b11521, 2026-10-09). A server
|
|
640
|
+
# started without ``--port`` on a new build is invisible to a provider that
|
|
641
|
+
# still points at :8080, and the reverse holds for configs written against
|
|
642
|
+
# the new default. When the basic probe cannot connect to one of those two
|
|
643
|
+
# ports on a loopback host, check the other one so the transport error can
|
|
644
|
+
# name the likely cause instead of looking like "server not running".
|
|
645
|
+
_LLAMACPP_PORT_PAIR: dict[int, int] = {8080: 9931, 9931: 8080}
|
|
646
|
+
_LOOPBACK_HOSTS = frozenset({"localhost", "127.0.0.1", "::1"})
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
async def _llamacpp_port_hint(base_url: object) -> str | None:
|
|
650
|
+
"""Return a hint when the other llama.cpp default port answers.
|
|
651
|
+
|
|
652
|
+
Only fires for loopback hosts on port 8080 or 9931. The alternate
|
|
653
|
+
port is probed at ``GET /health`` (llama-server's readiness route)
|
|
654
|
+
with a short timeout on the IPv4 literal — ``localhost`` can resolve
|
|
655
|
+
to ``::1`` first on macOS, where llama-server is not listening. The
|
|
656
|
+
config is never rewritten; the operator decides.
|
|
657
|
+
"""
|
|
658
|
+
try:
|
|
659
|
+
parsed = httpx.URL(str(base_url))
|
|
660
|
+
except (httpx.InvalidURL, TypeError):
|
|
661
|
+
return None
|
|
662
|
+
if parsed.host not in _LOOPBACK_HOSTS or parsed.port not in _LLAMACPP_PORT_PAIR:
|
|
663
|
+
return None
|
|
664
|
+
other = _LLAMACPP_PORT_PAIR[parsed.port]
|
|
665
|
+
try:
|
|
666
|
+
async with httpx.AsyncClient(timeout=1.5) as client:
|
|
667
|
+
resp = await client.get(f"http://127.0.0.1:{other}/health")
|
|
668
|
+
except httpx.HTTPError:
|
|
669
|
+
return None
|
|
670
|
+
if resp.status_code >= 500:
|
|
671
|
+
return None
|
|
672
|
+
return (
|
|
673
|
+
f"Something is answering on port {other} instead. llama.cpp "
|
|
674
|
+
f"builds b11521+ listen on 9931 when started without --port "
|
|
675
|
+
f"(older builds: 8080). Either start llama-server with "
|
|
676
|
+
f"--port {parsed.port}, or change this provider's base_url to "
|
|
677
|
+
f"port {other}"
|
|
678
|
+
)
|
|
679
|
+
|
|
680
|
+
|
|
638
681
|
async def _probe_auth_and_basic_chat(
|
|
639
682
|
provider: ProviderConfig,
|
|
640
683
|
) -> ProbeResult:
|
|
@@ -670,10 +713,14 @@ async def _probe_auth_and_basic_chat(
|
|
|
670
713
|
)
|
|
671
714
|
|
|
672
715
|
if status is None:
|
|
716
|
+
detail = f"could not reach {url}: {raw}"
|
|
717
|
+
hint = await _llamacpp_port_hint(provider.base_url)
|
|
718
|
+
if hint:
|
|
719
|
+
detail = f"{detail}. {hint}"
|
|
673
720
|
return ProbeResult(
|
|
674
721
|
name="auth+basic-chat",
|
|
675
722
|
verdict=ProbeVerdict.TRANSPORT_ERROR,
|
|
676
|
-
detail=
|
|
723
|
+
detail=detail,
|
|
677
724
|
)
|
|
678
725
|
|
|
679
726
|
if status in (401, 403):
|