coderouter-cli 2.7.9__tar.gz → 2.8.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.
Files changed (266) hide show
  1. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/CHANGELOG.md +142 -0
  2. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/PKG-INFO +1 -1
  3. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/adapters/agent_cli.py +248 -57
  4. coderouter_cli-2.8.0/coderouter/adapters/registry.py +60 -0
  5. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/config/schemas.py +50 -14
  6. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/plugins/base.py +54 -26
  7. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/plugins/loader.py +8 -5
  8. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/plugins/registry.py +13 -2
  9. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/routing/fallback.py +7 -3
  10. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/external-agents.en.md +136 -14
  11. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/external-agents.md +136 -14
  12. coderouter_cli-2.8.0/docs/designs/agent-cli-plugin-extraction.md +410 -0
  13. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/designs/external-agents-adapter.md +28 -1
  14. coderouter_cli-2.8.0/docs/designs/orchestration-companion.md +109 -0
  15. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers-agent-cli.yaml +105 -43
  16. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/pyproject.toml +1 -1
  17. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_agent_cli.py +282 -13
  18. coderouter_cli-2.8.0/tests/test_plugin_adapter.py +404 -0
  19. coderouter_cli-2.7.9/coderouter/adapters/registry.py +0 -23
  20. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/.gitignore +0 -0
  21. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/LICENSE +0 -0
  22. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/README.en.md +0 -0
  23. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/README.md +0 -0
  24. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/__init__.py +0 -0
  25. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/__main__.py +0 -0
  26. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/adapters/__init__.py +0 -0
  27. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/adapters/anthropic_native.py +0 -0
  28. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/adapters/base.py +0 -0
  29. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/adapters/openai_compat.py +0 -0
  30. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/cli.py +0 -0
  31. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/cli_stats.py +0 -0
  32. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/config/__init__.py +0 -0
  33. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/config/capability_registry.py +0 -0
  34. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/config/env_file.py +0 -0
  35. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/config/loader.py +0 -0
  36. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/cost.py +0 -0
  37. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/data/__init__.py +0 -0
  38. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/data/model-capabilities.yaml +0 -0
  39. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/doctor.py +0 -0
  40. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/doctor_apply.py +0 -0
  41. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/env_security.py +0 -0
  42. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/errors.py +0 -0
  43. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/gguf_introspect.py +0 -0
  44. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/__init__.py +0 -0
  45. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/_fingerprint.py +0 -0
  46. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/backend_health.py +0 -0
  47. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/context_budget.py +0 -0
  48. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/continuous_probe.py +0 -0
  49. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/drift_actions.py +0 -0
  50. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/drift_detection.py +0 -0
  51. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/memory_budget.py +0 -0
  52. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/memory_pressure.py +0 -0
  53. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/self_healing.py +0 -0
  54. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/guards/tool_loop.py +0 -0
  55. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/hardware.py +0 -0
  56. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/ingress/__init__.py +0 -0
  57. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/ingress/anthropic_routes.py +0 -0
  58. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/ingress/app.py +0 -0
  59. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/ingress/dashboard_routes.py +0 -0
  60. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/ingress/launcher_routes.py +0 -0
  61. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/ingress/metrics_routes.py +0 -0
  62. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/ingress/openai_routes.py +0 -0
  63. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/language_tax.py +0 -0
  64. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/launcher_speculative.py +0 -0
  65. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/logging.py +0 -0
  66. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/metrics/__init__.py +0 -0
  67. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/metrics/collector.py +0 -0
  68. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/metrics/prometheus.py +0 -0
  69. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/output_filters.py +0 -0
  70. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/plugins/__init__.py +0 -0
  71. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/routing/__init__.py +0 -0
  72. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/routing/adaptive.py +0 -0
  73. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/routing/auto_router.py +0 -0
  74. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/routing/budget.py +0 -0
  75. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/routing/capability.py +0 -0
  76. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/state/__init__.py +0 -0
  77. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/state/audit_log.py +0 -0
  78. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/state/replay.py +0 -0
  79. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/state/request_log.py +0 -0
  80. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/state/store.py +0 -0
  81. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/state/suggest_rules.py +0 -0
  82. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/token_estimation.py +0 -0
  83. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/token_estimation_accurate.py +0 -0
  84. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/translation/__init__.py +0 -0
  85. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/translation/anthropic.py +0 -0
  86. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/translation/convert.py +0 -0
  87. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/coderouter/translation/tool_repair.py +0 -0
  88. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/README.en.md +0 -0
  89. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/README.md +0 -0
  90. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/assets/dashboard-demo.png +0 -0
  91. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/claude-code-llamacpp-vllm.en.md +0 -0
  92. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/claude-code-llamacpp-vllm.md +0 -0
  93. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/gguf_dl.en.md +0 -0
  94. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/gguf_dl.md +0 -0
  95. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/hf-ollama-models.en.md +0 -0
  96. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/hf-ollama-models.md +0 -0
  97. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/install-backends.en.md +0 -0
  98. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/install-backends.md +0 -0
  99. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/launcher-quickstart.en.md +0 -0
  100. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/launcher-quickstart.md +0 -0
  101. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/launcher.en.md +0 -0
  102. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/launcher.md +0 -0
  103. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/llamacpp-direct.en.md +0 -0
  104. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/llamacpp-direct.md +0 -0
  105. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/lmstudio-direct.en.md +0 -0
  106. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/lmstudio-direct.md +0 -0
  107. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/backends/verify-ollama-0.23.1.md +0 -0
  108. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/architecture.en.md +0 -0
  109. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/architecture.md +0 -0
  110. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/context-budget.en.md +0 -0
  111. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/context-budget.md +0 -0
  112. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/continuous-probing.en.md +0 -0
  113. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/continuous-probing.md +0 -0
  114. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/drift-detection.en.md +0 -0
  115. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/drift-detection.md +0 -0
  116. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/partial-stitch.en.md +0 -0
  117. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/concepts/partial-stitch.md +0 -0
  118. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/designs/v1.5-dashboard-mockup.html +0 -0
  119. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/designs/v1.6-auto-router-verification.md +0 -0
  120. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/designs/v1.6-auto-router.md +0 -0
  121. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/free-tier-guide.en.md +0 -0
  122. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/free-tier-guide.md +0 -0
  123. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/language-tax.en.md +0 -0
  124. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/language-tax.md +0 -0
  125. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/remote-access.en.md +0 -0
  126. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/remote-access.md +0 -0
  127. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/security.en.md +0 -0
  128. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/security.md +0 -0
  129. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/troubleshooting.en.md +0 -0
  130. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/troubleshooting.md +0 -0
  131. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/usage-guide.en.md +0 -0
  132. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/guides/usage-guide.md +0 -0
  133. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/low-memory-integration.en.md +0 -0
  134. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/low-memory-integration.md +0 -0
  135. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/openrouter-roster/CHANGES.md +0 -0
  136. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/openrouter-roster/README.md +0 -0
  137. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/openrouter-roster/latest.json +0 -0
  138. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/retrospectives/v0.4.md +0 -0
  139. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/retrospectives/v0.5-verify.md +0 -0
  140. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/retrospectives/v0.5.md +0 -0
  141. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/retrospectives/v0.6.md +0 -0
  142. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/retrospectives/v0.7.md +0 -0
  143. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/retrospectives/v1.0-verify.md +0 -0
  144. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/retrospectives/v1.0.md +0 -0
  145. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/start/quickstart.en.md +0 -0
  146. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/start/quickstart.md +0 -0
  147. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/start/when-do-i-need-coderouter.en.md +0 -0
  148. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/docs/start/when-do-i-need-coderouter.md +0 -0
  149. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/.env.example +0 -0
  150. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/README.md +0 -0
  151. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers-multiagent.yaml +0 -0
  152. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers.context-budget-test.yaml +0 -0
  153. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers.llamacpp-vllm.yaml +0 -0
  154. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers.nvidia-nim.yaml +0 -0
  155. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers.ollama-auto-custom.yaml +0 -0
  156. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers.ollama-auto.yaml +0 -0
  157. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers.ollama-free-chain.yaml +0 -0
  158. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers.raspberrypi.yaml +0 -0
  159. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/examples/providers.yaml +0 -0
  160. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/scripts/demo_traffic.sh +0 -0
  161. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/scripts/openrouter_roster_diff.py +0 -0
  162. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/scripts/smoke_v2_2.sh +0 -0
  163. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/scripts/verify-providers.yaml +0 -0
  164. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/scripts/verify_ollama_0_23.py +0 -0
  165. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/scripts/verify_v0_5.sh +0 -0
  166. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/scripts/verify_v1_0.sh +0 -0
  167. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/__init__.py +0 -0
  168. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/conftest.py +0 -0
  169. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_adapter_anthropic.py +0 -0
  170. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_audit_log.py +0 -0
  171. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_auto_router.py +0 -0
  172. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_auto_router_cjk.py +0 -0
  173. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_backend_health.py +0 -0
  174. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_budget.py +0 -0
  175. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_capability.py +0 -0
  176. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_capability_degraded_payload.py +0 -0
  177. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_capability_registry.py +0 -0
  178. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_capability_registry_cache_control.py +0 -0
  179. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_capability_tool_choice.py +0 -0
  180. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_claude_code_suitability.py +0 -0
  181. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_cli.py +0 -0
  182. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_cli_bind_warning.py +0 -0
  183. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_cli_stats.py +0 -0
  184. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_config.py +0 -0
  185. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_config_shim_actions.py +0 -0
  186. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_context_budget.py +0 -0
  187. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_continuous_probe.py +0 -0
  188. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_dashboard_endpoint.py +0 -0
  189. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_doctor.py +0 -0
  190. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_doctor_apply.py +0 -0
  191. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_doctor_cache_probe.py +0 -0
  192. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_drift_actions.py +0 -0
  193. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_drift_detection.py +0 -0
  194. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_drift_detection_integration.py +0 -0
  195. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_env_file.py +0 -0
  196. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_env_security.py +0 -0
  197. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_errors.py +0 -0
  198. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_examples_yaml.py +0 -0
  199. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback.py +0 -0
  200. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_anthropic.py +0 -0
  201. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_cache_control.py +0 -0
  202. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_cache_observed.py +0 -0
  203. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_cache_strip.py +0 -0
  204. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_empty_response.py +0 -0
  205. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_misconfig_warn.py +0 -0
  206. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_paid_gate.py +0 -0
  207. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_thinking.py +0 -0
  208. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fallback_tool_choice_emulate.py +0 -0
  209. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_h1_prometheus_drift.py +0 -0
  210. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_h2_h6_translation.py +0 -0
  211. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_h3_client_reuse.py +0 -0
  212. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_h4_h5_guards.py +0 -0
  213. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_h7_probe_restore.py +0 -0
  214. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_h8_launcher_auth.py +0 -0
  215. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_mA_routing.py +0 -0
  216. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_mB_adapters.py +0 -0
  217. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_mC_convert.py +0 -0
  218. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_mD_logging_metrics.py +0 -0
  219. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_mE_config_validation.py +0 -0
  220. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_fix_mF_ingress.py +0 -0
  221. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_gguf_introspect.py +0 -0
  222. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_guards_tool_loop.py +0 -0
  223. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_hardware.py +0 -0
  224. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_ingress_anthropic.py +0 -0
  225. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_ingress_count_tokens.py +0 -0
  226. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_ingress_profile.py +0 -0
  227. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_language_tax.py +0 -0
  228. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_language_tax_integration.py +0 -0
  229. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_launcher_mtp.py +0 -0
  230. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_launcher_mtp_fallback.py +0 -0
  231. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_launcher_provider_sync.py +0 -0
  232. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_memory_budget.py +0 -0
  233. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_memory_pressure.py +0 -0
  234. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_metrics_cache.py +0 -0
  235. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_metrics_collector.py +0 -0
  236. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_metrics_cost.py +0 -0
  237. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_metrics_endpoint.py +0 -0
  238. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_metrics_jsonl.py +0 -0
  239. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_metrics_prometheus.py +0 -0
  240. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_metrics_prometheus_cache.py +0 -0
  241. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_models_passthrough.py +0 -0
  242. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_openai_compat.py +0 -0
  243. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_openrouter_roster_diff.py +0 -0
  244. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_output_filters.py +0 -0
  245. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_output_filters_adapters.py +0 -0
  246. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_partial_stitch.py +0 -0
  247. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_plugins_integration.py +0 -0
  248. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_plugins_loader.py +0 -0
  249. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_plugins_registry.py +0 -0
  250. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_reasoning_strip.py +0 -0
  251. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_repair_byte_fallback.py +0 -0
  252. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_request_log.py +0 -0
  253. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_role_normalization.py +0 -0
  254. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_routing_adaptive.py +0 -0
  255. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_self_healing.py +0 -0
  256. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_setup_sh.py +0 -0
  257. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_state_store.py +0 -0
  258. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_token_estimation.py +0 -0
  259. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_token_estimation_accurate.py +0 -0
  260. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_tokens_saved_metric.py +0 -0
  261. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_tool_repair.py +0 -0
  262. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_tool_repair_lenient.py +0 -0
  263. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_tool_repair_r4.py +0 -0
  264. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_toolrepair_bench.py +0 -0
  265. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_translation_anthropic.py +0 -0
  266. {coderouter_cli-2.7.9 → coderouter_cli-2.8.0}/tests/test_translation_reverse.py +0 -0
@@ -9,6 +9,148 @@ are kept verbatim where the Japanese text itself is the subject).
9
9
 
10
10
  ---
11
11
 
12
+ ## [v2.8.0] — 2026-07-11 (Plugin SDK: Adapter hook wired — Phase 2a of the agent_cli extraction)
13
+
14
+ Opens the Plugin SDK's Adapter surface: the previously dead-end `Adapter`
15
+ Protocol (name-only) is now a real engine-integrated hook, so an external
16
+ plugin package can provide new `kind` values for providers.yaml without
17
+ touching Core. This is Phase 2a of the agent_cli plugin-extraction design
18
+ (`docs/designs/agent-cli-plugin-extraction.md`): hook wiring only — the
19
+ in-core `agent_cli` adapter is unchanged and keeps working exactly as in
20
+ v2.7.10; the actual move into `coderouter-plugin-agents` is Phase 2b/2c.
21
+
22
+ ### Added
23
+
24
+ - **`Adapter` plugin Protocol** (`coderouter/plugins/base.py`) — `kind: str`
25
+ plus synchronous `build(config: ProviderConfig) -> BaseAdapter`. A factory
26
+ shape (kind → adapter), unlike the per-request hooks: construction happens
27
+ once at startup and does no I/O. The `adapter` entry-point group
28
+ (`coderouter.adapter`, singular — matching the loader's group mechanics)
29
+ moved from the future list into the active groups; the existing two-stage
30
+ gate (installed + listed in `plugins.enabled`) applies as-is.
31
+ - **`build_adapter` plugin resolution** (`coderouter/adapters/registry.py`)
32
+ — resolution order is in-core kinds first (`openai_compat` / `anthropic` /
33
+ `agent_cli`; a plugin can never shadow them), then plugin-provided kinds,
34
+ then a `ValueError` listing every known kind plus an install/enable hint.
35
+ Both engine call sites (startup adapter cache + runtime
36
+ `register_provider`) pass the plugin registry.
37
+ - **Tests** (`tests/test_plugin_adapter.py`, new, 10) — plugin-kind
38
+ resolution, in-core shadowing prevention, unknown-kind error contents,
39
+ two-stage gate via the real entry-point loader path, runtime
40
+ `register_provider` with a plugin kind, and an E2E request through
41
+ `POST /v1/chat/completions` served by a plugin-provided adapter.
42
+
43
+ ### Changed
44
+
45
+ - **`ProviderConfig.kind`: `Literal[...]` → `str`** (`config/schemas.py`) —
46
+ plugin kinds cannot be known at config-load time (plugins are discovered
47
+ after the config is parsed), so the closed Literal had to open. Fail-fast
48
+ for a typo'd kind moves one step later but stays at startup: the engine
49
+ builds adapters for **all** providers eagerly in its constructor, so an
50
+ unknown kind still aborts `serve` at load with a message that now lists
51
+ in-core and plugin kinds (strictly more helpful than the old pydantic
52
+ error). Recorded as a design deviation in
53
+ `agent-cli-plugin-extraction.md` §8.1, including the residual limitation
54
+ that `doctor` (HTTP-probe based) does not surface typo'd kinds.
55
+ - Plugin-group bookkeeping: `adapter` no longer warns as
56
+ "plugin-group-not-yet-active"; `PluginRegistry` gained an `adapters`
57
+ view alongside `input_filters` / `observers`.
58
+
59
+ ## [v2.7.10] — 2026-07-11 (agent_cli Phase 1c: antigravity — Phase 1 complete)
60
+
61
+ Completes agent_cli Phase 1: `agent: antigravity` (the Google Antigravity
62
+ CLI, command `agy`) is now implemented, joining `claude` (1a), `codex`
63
+ (1b), and `grok` (1d) — all four Phase 1 backends are now in place. This
64
+ target replaces the originally-planned `gemini`: on the author's Mac, the
65
+ legacy Gemini CLI (`@google/gemini-cli` 0.50.x) now fails individual-account
66
+ OAuth outright with a verified `IneligibleTierError: This client is no
67
+ longer supported for Gemini Code Assist for individuals. To continue using
68
+ Gemini, please migrate to the Antigravity suite of products:
69
+ https://antigravity.google` (`reasonCode: UNSUPPORTED_CLIENT`,
70
+ `tierId: free-tier`) — Google discontinued Gemini CLI for individual
71
+ accounts on 2026-06-18. The same legacy CLI also still hits the
72
+ trusted-directory gate with exit 55 (`--skip-trust` /
73
+ `GEMINI_CLI_TRUST_WORKSPACE=true`), a pre-existing constraint unrelated to
74
+ the discontinuation. Google's stated successor is the Antigravity CLI
75
+ (`agy`) — a separate Go rewrite, not a fork of gemini-cli — which keeps
76
+ individual Google-account OAuth alive, including the free tier. Verified
77
+ against agy **1.1.1** (author's Mac, 2026-07-11), its flag surface differs
78
+ meaningfully from gemini-cli's: no `--output-format json` (plain-text
79
+ output only), `--mode plan|accept-edits` instead of an approval-mode flag,
80
+ a `--dangerously-skip-permissions` full-bypass flag, a CLI-side
81
+ `--print-timeout` (the first agent_cli target with its own timeout), no
82
+ `--max-turns`, and no stdin channel for the prompt at all — piping stdin to
83
+ `agy` hangs (field-verified). `agent: gemini` remains schema-declared but
84
+ is now rejected with a migration-pointer message instead of a generic
85
+ not-implemented one.
86
+
87
+ ### Added
88
+
89
+ - **`agent: antigravity` builder/parser in `AgentCliAdapter`**
90
+ (`coderouter/adapters/agent_cli.py`) — invokes the Antigravity CLI
91
+ one-shot as `agy -p <prompt> --model <model> --mode plan --print-timeout
92
+ <exec_timeout_s>s` (read_only default). The prompt rides on argv as the
93
+ value of `-p` — agy has no `--prompt-file` equivalent and hangs if stdin
94
+ is piped (field-verified), so this is the fourth prompt-delivery pattern
95
+ among the four backends (claude/codex use stdin, grok uses
96
+ `--prompt-file`, antigravity uses argv). Documented limitation: Linux's
97
+ `MAX_ARG_STRLEN` (~128KiB) caps prompt size, and the prompt is visible in
98
+ `ps` output — there is no way around this with agy's current flag
99
+ surface. Output is plain text: stdout is decoded UTF-8, defensively
100
+ stripped of ANSI escapes, and trimmed; an empty result raises a
101
+ retryable `AdapterError`. There is no `--output-format json`, so token
102
+ usage, session id, and structured errors are all unavailable — usage is
103
+ reported as all zeros (same treatment as grok), and
104
+ `coderouter_session_id` is never populated.
105
+ - **`sandbox_mode` → antigravity flag mapping** — `read_only` →
106
+ `--mode plan`; `edit` → `--mode accept-edits`; `full_auto` →
107
+ `--mode accept-edits --dangerously-skip-permissions`. As with the other
108
+ three backends, the effective mode is clamped to read-only whenever
109
+ `allow_file_writes=false`.
110
+ - **`--print-timeout` as a CLI-side first wall** — generated from
111
+ `AgentCliConfig.exec_timeout_s` (e.g. `exec_timeout_s=600` →
112
+ `--print-timeout 600s`), letting agy self-terminate before the outer
113
+ `asyncio.wait_for` + process-group `SIGKILL` fires. antigravity is the
114
+ only agent_cli target with a double timeout wall.
115
+ - **`max_turns` ignored for antigravity** — agy has no
116
+ `--max-turns`-equivalent flag; `AgentCliConfig.max_turns` has no effect
117
+ when `agent: antigravity`, same as codex.
118
+ - **`command` defaults to `"agy"`** — the only agent_cli target whose
119
+ default executable name differs from the `agent` field value (the binary
120
+ is named for the CLI, not the product).
121
+ - **5th `Literal` value on `AgentCliConfig.agent`** — `"antigravity"` joins
122
+ `claude`/`codex`/`gemini`/`grok`.
123
+
124
+ ### Changed
125
+
126
+ - **`agent: gemini` now rejected with a migration message** instead of the
127
+ generic "not implemented yet" wording — constructing the adapter with
128
+ `agent: gemini` now raises `AdapterError: Google discontinued Gemini CLI
129
+ for individual accounts (June 2026). Use agent='antigravity' instead.`
130
+ (`retryable=False`).
131
+ - **Schema docstrings** (`coderouter/config/schemas.py`) — `AgentCliConfig`
132
+ field descriptions updated for the four-backend reality
133
+ (claude/codex/antigravity/grok implemented, gemini rejected), including
134
+ the `command` and `max_turns` docstrings noting antigravity's `agy`
135
+ default and ignored `max_turns`.
136
+ - **Docs & examples** — `docs/backends/external-agents.md`/`.en.md` gain a
137
+ full antigravity section (config example, adapter argv, the
138
+ argv-vs-stdin-vs-`--prompt-file` prompt-delivery comparison across all
139
+ four backends, mode mapping table, plain-text parsing and zero usage,
140
+ the `--print-timeout` double wall, OAuth setup with the `agy models`
141
+ smoke check, the display-string model-name caveat including that Claude
142
+ models are reachable through agy, the stdin-pipe-hang warning, and the
143
+ version-pin caveat) and lead with the gemini-discontinuation story
144
+ (updated implementation-status wording: Phase 1 complete, all four
145
+ backends). The design doc `docs/designs/external-agents-adapter.md` gets
146
+ a new appendix §11.5 recording the gemini-to-antigravity delta (body
147
+ text left as the historical record); its header status line now reads
148
+ Phase 1 (1a/1b/1c/1d) complete. `examples/providers-agent-cli.yaml`
149
+ replaces the Phase 1c gemini preview block with a documented (still
150
+ commented-out) antigravity block, with the now-empty
151
+ "unimplemented preview" framing removed since nothing remains
152
+ unimplemented.
153
+
12
154
  ## [v2.7.9] — 2026-07-11 (agent_cli Phase 1b: codex)
13
155
 
14
156
  Adds the third target to the `agent_cli` adapter: `agent: codex` (the OpenAI
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: coderouter-cli
3
- Version: 2.7.9
3
+ Version: 2.8.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
@@ -1,9 +1,9 @@
1
1
  """External coding-agent CLI adapter (``kind="agent_cli"``).
2
2
 
3
3
  This adapter invokes an external coding-agent CLI (Claude Code / Codex /
4
- Gemini / Grok) as a single one-shot ``exec`` and returns the agent's final
5
- answer as one ``prompt in → text out`` transformation. It is the in-core
6
- implementation of the external-agents-adapter design
4
+ Antigravity / Grok) as a single one-shot ``exec`` and returns the agent's
5
+ final answer as one ``prompt in → text out`` transformation. It is the
6
+ in-core implementation of the external-agents-adapter design
7
7
  (``docs/designs/external-agents-adapter.md``).
8
8
 
9
9
  Design in one paragraph
@@ -18,10 +18,10 @@ CodeRouter merely performs the one conversion. Following the
18
18
  ``openai_compat`` precedent, a *single* adapter class fronts *multiple*
19
19
  target agents, dispatched on the ``agent`` field.
20
20
 
21
- Implemented agents (Phase 1a + 1b + 1d)
22
- ========================================
21
+ Implemented agents (Phase 1 complete: 1a + 1b + 1c + 1d)
22
+ ==========================================================
23
23
 
24
- Three targets are implemented here:
24
+ Four targets are implemented here:
25
25
 
26
26
  * ``claude`` (Claude Code CLI, Phase 1a) — the most stable CLI, the most
27
27
  fine-grained safety controls, and the only one that emits
@@ -42,22 +42,44 @@ Three targets are implemented here:
42
42
  ``ProviderConfig.cost``, design §5.1.6). ``--no-memory`` is always passed
43
43
  so a user-level cross-session memory setting cannot leak state between
44
44
  requests.
45
-
46
- The remaining agent (gemini) is declared in the config schema so
47
- providers.yaml is forward-compatible, but constructing an adapter for it
48
- raises a clear ``AdapterError`` until its phase (1c) lands.
45
+ * ``antigravity`` (Antigravity CLI, command ``agy``, Phase 1c, in lieu of
46
+ ``gemini``) headless one-shot via ``agy -p <prompt> --mode ...``.
47
+ Google discontinued the legacy Gemini CLI's OAuth for individual
48
+ accounts in June 2026 (field-verified ``IneligibleTierError`` /
49
+ ``UNSUPPORTED_CLIENT``); its successor, the Antigravity CLI, is a
50
+ separate Go implementation (not a gemini-cli fork) fulfilling the design's
51
+ "gemini" slot. It has no stdin or ``--prompt-file`` channel — piped stdin
52
+ hangs the CLI (field-verified on agy 1.1.1) — so the prompt rides argv
53
+ (see the security note below). Output is plain text with no
54
+ ``--output-format`` flag, no token/cost figures, and no session id, so
55
+ usage is reported as zeros and meta is empty, mirroring grok's rationale.
56
+ It is also the only agent with a CLI-side self-termination flag
57
+ (``--print-timeout``), layered *underneath* the adapter's own
58
+ ``asyncio.wait_for`` + PGID SIGKILL rather than replacing it.
59
+
60
+ ``gemini`` itself is declared in the config schema for backward-compatible
61
+ config parsing, but constructing an adapter for it raises a clear
62
+ ``AdapterError`` with a migration pointer to ``agent="antigravity"``.
49
63
 
50
64
  Security (design §6, non-negotiable)
51
65
  ====================================
52
66
 
53
67
  * **allowlist argv only** — the child is launched with
54
68
  :func:`asyncio.create_subprocess_exec` and a list argv. ``shell=True`` is
55
- never used; the prompt is never subject to shell interpretation (claude
56
- and codex read it from stdin codex's argv carries a trailing ``-``
57
- sentinel making that explicit; grok reads it from a private ``0600``
58
- prompt file inside the resolved workdir its ``-p`` requires the prompt
59
- as an argv value, and argv would both hit Linux's ~128KiB
60
- ``MAX_ARG_STRLEN`` on huge prompts and leak the text into ``ps`` output).
69
+ never used. Prompt delivery is one of three mechanisms depending on the
70
+ agent: claude and codex read it from stdin (codex's argv carries a
71
+ trailing ``-`` sentinel making that explicit); grok reads it from a
72
+ private ``0600`` prompt file inside the resolved workdir (its ``-p``
73
+ requires the prompt as an argv value, and argv would both hit Linux's
74
+ ~128KiB ``MAX_ARG_STRLEN`` on huge prompts and leak the text into ``ps``
75
+ output); antigravity has neither a stdin nor a ``--prompt-file`` channel
76
+ (piped stdin hangs the CLI, field-verified), so its prompt is carried on
77
+ argv — accepting the same ``MAX_ARG_STRLEN`` cap and local ``ps``
78
+ visibility that grok's file delivery was specifically designed to avoid.
79
+ No shell is involved in any case (list argv, never shell text), and the
80
+ documented threat model (an isolated, single-operator workstation) treats
81
+ local ``ps`` visibility as an accepted, documented limitation for
82
+ antigravity rather than a defect.
61
83
  * **default read-only** — ``allow_file_writes=False`` /
62
84
  ``sandbox_mode="read_only"`` are the defaults, mapped to claude's
63
85
  ``--permission-mode plan``. Writes require explicit opt-in and the sandbox
@@ -82,6 +104,7 @@ import asyncio
82
104
  import contextlib
83
105
  import json
84
106
  import os
107
+ import re
85
108
  import shutil
86
109
  import signal
87
110
  import time
@@ -148,6 +171,29 @@ _CODEX_SANDBOX_ARGS = {
148
171
  "full_auto": ["-s", "workspace-write"],
149
172
  }
150
173
 
174
+ # sandbox_mode → antigravity ``--mode`` flags (design §5.4, Antigravity CLI
175
+ # 1.1.1, verified via facts-antigravity.md). ``agy --help`` only enumerates
176
+ # two ``--mode`` values (``plan`` / ``accept-edits``), so ``full_auto`` maps
177
+ # onto ``accept-edits`` plus the separate ``--dangerously-skip-permissions``
178
+ # flag (auto-approves all tool executions) rather than a third ``--mode``
179
+ # value. ``--sandbox`` is deliberately never used here: it has a known bypass
180
+ # bug when combined with ``--dangerously-skip-permissions`` (agy issue #36).
181
+ _ANTIGRAVITY_MODE_ARGS = {
182
+ "read_only": ["--mode", "plan"],
183
+ "edit": ["--mode", "accept-edits"],
184
+ "full_auto": ["--mode", "accept-edits", "--dangerously-skip-permissions"],
185
+ }
186
+
187
+ # Compiled ANSI escape sequence stripper for antigravity's plain-text output
188
+ # (design §5.1.6). Covers CSI sequences (``ESC [ ... final-byte``) plus bare
189
+ # OSC-style ``ESC ]`` sequences terminated by BEL or ``ESC \`` (kept simple —
190
+ # antigravity's TUI chrome is not fully specified, so this is a defensive
191
+ # best-effort strip, not a full ANSI parser).
192
+ _ANSI_RE = re.compile(
193
+ r"\x1b\[[0-9;?]*[ -/]*[@-~]" # CSI ... final byte
194
+ r"|\x1b\][^\x07\x1b]*(?:\x07|\x1b\\)" # OSC ... BEL or ST
195
+ )
196
+
151
197
 
152
198
  def _chunk_text(text: str, size: int = _STREAM_CHUNK_CHARS) -> Iterator[str]:
153
199
  """Split ``text`` into ``size``-char pieces for the pseudo-stream."""
@@ -156,20 +202,26 @@ def _chunk_text(text: str, size: int = _STREAM_CHUNK_CHARS) -> Iterator[str]:
156
202
 
157
203
 
158
204
  class AgentCliAdapter(BaseAdapter):
159
- """Invoke an external coding-agent CLI one-shot (claude + codex + grok).
205
+ """Invoke an external coding-agent CLI one-shot (claude + codex + grok +
206
+ antigravity).
160
207
 
161
208
  The ``agent`` field selects the argv builder / output parser via the
162
209
  dispatch tables built in :meth:`__init__`, mirroring how
163
210
  ``openai_compat`` fronts many HTTP backends from one class.
164
211
  """
165
212
 
213
+ _IMPLEMENTED_AGENTS = ("claude", "codex", "grok", "antigravity")
214
+
166
215
  def __init__(self, config: ProviderConfig) -> None:
167
216
  """Bind to a ``ProviderConfig`` and reject unsupported agents.
168
217
 
169
- Constructing an adapter for an agent other than ``claude`` /
170
- ``codex`` / ``grok`` raises a non-retryable :class:`AdapterError`
171
- the remaining target (gemini) is declared in the schema but not
172
- implemented until its phase (design §9).
218
+ ``gemini`` gets its own rejection message (non-retryable): Google
219
+ discontinued the Gemini CLI's OAuth for individual accounts in June
220
+ 2026, so this adapter points the operator at ``antigravity``
221
+ instead of a generic "not implemented" message. Any other
222
+ unimplemented value (future-proofing; currently none, since the
223
+ schema's ``Literal`` only allows the five known agents) falls back
224
+ to a generic message listing what IS implemented.
173
225
  """
174
226
  super().__init__(config)
175
227
  if config.agent_cli is None: # pragma: no cover - schema enforces this
@@ -179,34 +231,50 @@ class AgentCliAdapter(BaseAdapter):
179
231
  retryable=False,
180
232
  )
181
233
  self.acfg: AgentCliConfig = config.agent_cli
182
- if self.acfg.agent not in ("claude", "codex", "grok"):
234
+ if self.acfg.agent not in self._IMPLEMENTED_AGENTS:
235
+ if self.acfg.agent == "gemini":
236
+ raise AdapterError(
237
+ "agent 'gemini' is not supported: Google discontinued "
238
+ "the Gemini CLI for individual accounts (June 2026; "
239
+ "IneligibleTierError). Use agent='antigravity' "
240
+ "(Antigravity CLI, command 'agy') instead.",
241
+ provider=config.name,
242
+ retryable=False,
243
+ )
183
244
  raise AdapterError(
184
- f"agent {self.acfg.agent!r} is not implemented yet "
185
- f"(implemented: claude, codex, grok). Wait for Phase 1c.",
245
+ f"agent {self.acfg.agent!r} is not implemented "
246
+ f"(implemented: {', '.join(self._IMPLEMENTED_AGENTS)}).",
186
247
  provider=config.name,
187
248
  retryable=False,
188
249
  )
189
250
  # agent → argv builder / output parser dispatch tables. claude landed
190
- # in Phase 1a, codex in Phase 1b, grok in Phase 1d; Phase 1c adds
191
- # gemini.
251
+ # in Phase 1a, codex in Phase 1b, grok in Phase 1d, antigravity in
252
+ # Phase 1c — all four (design §9) are now implemented.
192
253
  self._builders = {
193
254
  "claude": self._build_claude_argv,
194
255
  "codex": self._build_codex_argv,
195
256
  "grok": self._build_grok_argv,
257
+ "antigravity": self._build_antigravity_argv,
196
258
  }
197
259
  self._parsers = {
198
260
  "claude": self._parse_claude,
199
261
  "codex": self._parse_codex,
200
262
  "grok": self._parse_grok,
263
+ "antigravity": self._parse_antigravity,
201
264
  }
202
- # Prompt delivery is per-agent: claude and codex read their prompt
203
- # from stdin (10MB cap; codex's argv carries a trailing "-" sentinel
204
- # making that explicit), which keeps argv free of the (potentially
205
- # huge) prompt text. grok's ``-p`` REQUIRES the prompt as its argv
206
- # value (piped stdin is only appended as extra context, verified on
207
- # v0.2.93), so grok gets the prompt via ``--prompt-file`` instead —
208
- # see ``_write_prompt_file`` for the rationale.
265
+ # Prompt delivery is per-agent, one of three mechanisms: claude and
266
+ # codex read their prompt from stdin (10MB cap; codex's argv carries
267
+ # a trailing "-" sentinel making that explicit), which keeps argv
268
+ # free of the (potentially huge) prompt text. grok's ``-p``
269
+ # REQUIRES the prompt as its argv value (piped stdin is only
270
+ # appended as extra context, verified on v0.2.93), so grok gets the
271
+ # prompt via ``--prompt-file`` instead — see ``_write_prompt_file``
272
+ # for the rationale. antigravity has NEITHER a stdin nor a
273
+ # ``--prompt-file`` channel — piped stdin hangs the CLI outright
274
+ # (field-verified on agy 1.1.1) — so its prompt rides argv instead;
275
+ # see ``_build_antigravity_argv`` for the tradeoff this accepts.
209
276
  self._uses_stdin = self.acfg.agent in ("claude", "codex")
277
+ self._uses_argv = self.acfg.agent == "antigravity"
210
278
 
211
279
  # ------------------------------------------------------------------
212
280
  # BaseAdapter contract
@@ -255,10 +323,16 @@ class AgentCliAdapter(BaseAdapter):
255
323
 
256
324
  # Agents that cannot take the prompt on stdin (grok) get it through a
257
325
  # private temp file inside the workdir; it is ALWAYS removed in the
258
- # ``finally`` below, including on timeout / exception paths.
259
- prompt_file = None if self._uses_stdin else self._write_prompt_file(prompt, workdir)
326
+ # ``finally`` below, including on timeout / exception paths. Argv
327
+ # agents (antigravity) get neither a file nor stdin bytes — the
328
+ # prompt text is handed straight to the builder instead.
329
+ prompt_file = (
330
+ None
331
+ if self._uses_stdin or self._uses_argv
332
+ else self._write_prompt_file(prompt, workdir)
333
+ )
260
334
  try:
261
- argv = self._builders[self.acfg.agent](workdir, prompt_file)
335
+ argv = self._builders[self.acfg.agent](workdir, prompt_file, prompt)
262
336
  # Resolve the executable to an absolute path so argv[0] is a
263
337
  # concrete binary independent of the child's minimal PATH
264
338
  # (design §6 allowlist).
@@ -297,6 +371,10 @@ class AgentCliAdapter(BaseAdapter):
297
371
  retryable=False,
298
372
  ) from exc
299
373
 
374
+ # Non-stdin agents (grok's file delivery, antigravity's argv
375
+ # delivery) get None here, so ``communicate()`` closes stdin
376
+ # immediately without writing to it — verified required for
377
+ # antigravity, whose CLI hangs if anything is piped to stdin.
300
378
  stdin_bytes = prompt.encode("utf-8") if self._uses_stdin else None
301
379
  try:
302
380
  stdout, stderr = await asyncio.wait_for(
@@ -366,7 +444,9 @@ class AgentCliAdapter(BaseAdapter):
366
444
  # claude argv builder + output parser
367
445
  # ------------------------------------------------------------------
368
446
 
369
- def _build_claude_argv(self, workdir: str, prompt_file: str | None = None) -> list[str]:
447
+ def _build_claude_argv(
448
+ self, workdir: str, prompt_file: str | None = None, prompt: str | None = None
449
+ ) -> list[str]:
370
450
  """Assemble the ``claude -p`` argv (design §5.1.5 / §5.4).
371
451
 
372
452
  Shape::
@@ -375,12 +455,13 @@ class AgentCliAdapter(BaseAdapter):
375
455
  --permission-mode <plan|acceptEdits> --add-dir <workdir>
376
456
 
377
457
  The prompt is fed on stdin (not argv), so it never appears here and
378
- ``prompt_file`` is ignored (it exists only to keep the builder
379
- signature uniform across agents). ``--bare`` is deliberately NOT
380
- added it would skip OAuth/keychain reads and break subscription
381
- auth (design §5.3.4).
458
+ both ``prompt_file`` and ``prompt`` are ignored (they exist only to
459
+ keep the builder signature uniform across agents see
460
+ ``_build_antigravity_argv`` for the one agent that needs ``prompt``).
461
+ ``--bare`` is deliberately NOT added — it would skip OAuth/keychain
462
+ reads and break subscription auth (design §5.3.4).
382
463
  """
383
- del prompt_file # claude takes the prompt on stdin, not from a file.
464
+ del prompt_file, prompt # claude takes the prompt on stdin.
384
465
  model = self.acfg.model or self.config.model
385
466
  argv = [self.acfg.command, "-p", "--output-format", "json", "--model", model]
386
467
  if self.acfg.max_turns is not None:
@@ -494,7 +575,9 @@ class AgentCliAdapter(BaseAdapter):
494
575
  # codex argv builder + output parser (Phase 1b)
495
576
  # ------------------------------------------------------------------
496
577
 
497
- def _build_codex_argv(self, workdir: str, prompt_file: str | None = None) -> list[str]:
578
+ def _build_codex_argv(
579
+ self, workdir: str, prompt_file: str | None = None, prompt: str | None = None
580
+ ) -> list[str]:
498
581
  """Assemble the ``codex exec`` argv (design §5.1.5 / §5.4, verified
499
582
  against codex-cli 0.144.1 — see ``_codex/facts-codex.md``).
500
583
 
@@ -504,19 +587,20 @@ class AgentCliAdapter(BaseAdapter):
504
587
  -m <model> -C <workdir> -s <read-only|workspace-write> -
505
588
 
506
589
  The prompt is fed on stdin (not argv), so it never appears here and
507
- ``prompt_file`` is ignored (it exists only to keep the builder
508
- signature uniform across agents) — the trailing ``-`` makes the
509
- stdin intent explicit to ``codex exec``, which otherwise treats a
510
- bare invocation with no PROMPT arg the same way but reads more
511
- ambiguously in a fixed argv list. ``--skip-git-repo-check`` is
512
- ALWAYS passed because the isolated workdir is not a git repository
513
- (without it the CLI exits 1). ``--ephemeral`` is ALWAYS passed so no
514
- session state persists to disk, matching the adapter's stateless
515
- one-shot ethos (the same rationale as grok's ``--no-memory``). codex
516
- has no ``--max-turns`` equivalent, so ``AgentCliConfig.max_turns`` is
517
- silently ignored here (documented in the schema).
590
+ both ``prompt_file`` and ``prompt`` are ignored (they exist only to
591
+ keep the builder signature uniform across agents) — the trailing
592
+ ``-`` makes the stdin intent explicit to ``codex exec``, which
593
+ otherwise treats a bare invocation with no PROMPT arg the same way
594
+ but reads more ambiguously in a fixed argv list. ``--skip-git-repo-
595
+ check`` is ALWAYS passed because the isolated workdir is not a git
596
+ repository (without it the CLI exits 1). ``--ephemeral`` is ALWAYS
597
+ passed so no session state persists to disk, matching the adapter's
598
+ stateless one-shot ethos (the same rationale as grok's
599
+ ``--no-memory``). codex has no ``--max-turns`` equivalent, so
600
+ ``AgentCliConfig.max_turns`` is silently ignored here (documented in
601
+ the schema).
518
602
  """
519
- del prompt_file # codex takes the prompt on stdin, not from a file.
603
+ del prompt_file, prompt # codex takes the prompt on stdin.
520
604
  model = self.acfg.model or self.config.model
521
605
  argv = [
522
606
  self.acfg.command,
@@ -666,7 +750,9 @@ class AgentCliAdapter(BaseAdapter):
666
750
  # grok argv builder + output parser (Phase 1d)
667
751
  # ------------------------------------------------------------------
668
752
 
669
- def _build_grok_argv(self, workdir: str, prompt_file: str | None = None) -> list[str]:
753
+ def _build_grok_argv(
754
+ self, workdir: str, prompt_file: str | None = None, prompt: str | None = None
755
+ ) -> list[str]:
670
756
  """Assemble the grok headless argv (design §5, grok CLI v0.2.93).
671
757
 
672
758
  Shape::
@@ -680,8 +766,11 @@ class AgentCliAdapter(BaseAdapter):
680
766
  would hit Linux's ~128KiB ``MAX_ARG_STRLEN`` on large prompts and
681
767
  leak the text into ``ps`` output. ``--no-memory`` is deliberate: it
682
768
  enforces the one-request-one-transformation statelessness even if
683
- the user's grok config enables cross-session memory.
769
+ the user's grok config enables cross-session memory. ``prompt`` is
770
+ ignored (it exists only to keep the builder signature uniform across
771
+ agents — grok never takes it on argv).
684
772
  """
773
+ del prompt
685
774
  if prompt_file is None: # pragma: no cover - generate() always supplies it
686
775
  raise AdapterError(
687
776
  "grok argv requires a prompt file",
@@ -774,6 +863,108 @@ class AgentCliAdapter(BaseAdapter):
774
863
  meta["coderouter_session_id"] = session_id
775
864
  return result, usage, meta
776
865
 
866
+ # ------------------------------------------------------------------
867
+ # antigravity argv builder + output parser (Phase 1c, in lieu of gemini)
868
+ # ------------------------------------------------------------------
869
+
870
+ def _build_antigravity_argv(
871
+ self, workdir: str, prompt_file: str | None = None, prompt: str | None = None
872
+ ) -> list[str]:
873
+ """Assemble the ``agy -p`` argv (design §5.1.5 / §5.4, verified
874
+ against Antigravity CLI 1.1.1 — see ``_codex/facts-antigravity.md``).
875
+
876
+ Shape::
877
+
878
+ agy -p <prompt> --model <m> --mode <plan|accept-edits>
879
+ [--dangerously-skip-permissions] --print-timeout <n>s
880
+
881
+ ``workdir`` is unused here — antigravity picks up its working
882
+ directory from the child process's ``cwd`` (set by ``generate()``),
883
+ and this adapter deliberately never passes ``--add-dir`` (design
884
+ keeps the argv minimal; only claude's multi-root model needs it).
885
+ ``prompt_file`` is unused (antigravity has no such flag).
886
+
887
+ Prompt-delivery tradeoff (read this before touching the ``-p``
888
+ line): agy has no stdin channel — piping content to stdin makes the
889
+ real CLI hang waiting for a response that never comes (field-
890
+ verified on 1.1.1) — and no ``--prompt-file`` equivalent either. The
891
+ prompt therefore rides argv as the ``-p`` value, which is the *only*
892
+ delivery mechanism the CLI offers. This caps practical prompt size
893
+ at Linux's ~128KiB ``MAX_ARG_STRLEN`` and exposes the prompt text to
894
+ ``ps`` on the local host — exactly the two costs grok's
895
+ ``--prompt-file`` delivery was built to avoid (see the module
896
+ docstring's security section). No shell is involved (list argv, not
897
+ shell text), and the adapter's threat model — an isolated,
898
+ single-operator workstation — accepts local ``ps`` visibility as a
899
+ documented limitation rather than a defect; there is no safer
900
+ channel to fall back to.
901
+
902
+ ``--print-timeout`` is antigravity's own self-termination clock,
903
+ derived from ``exec_timeout_s`` — it is the CLI's *first* wall
904
+ against a hung call; the adapter's outer ``asyncio.wait_for`` +
905
+ process-group ``SIGKILL`` remains the second, unconditional wall
906
+ (design §6). ``max_turns`` is never emitted: agy has no ``--max-
907
+ turns``-equivalent flag (like codex), so ``AgentCliConfig.max_turns``
908
+ is silently ignored here (documented in the schema). No ``--sandbox``
909
+ (known bypass bug alongside ``--dangerously-skip-permissions``,
910
+ agy issue #36) and no ``--add-dir`` are ever passed.
911
+ """
912
+ del prompt_file # antigravity has no prompt-file flag.
913
+ if prompt is None: # pragma: no cover - generate() always supplies it
914
+ raise AdapterError(
915
+ "antigravity argv requires the prompt text",
916
+ provider=self.name,
917
+ retryable=False,
918
+ )
919
+ model = self.acfg.model or self.config.model
920
+ argv = [self.acfg.command, "-p", prompt, "--model", model]
921
+ argv += self._antigravity_mode_args()
922
+ argv += ["--print-timeout", f"{int(self.acfg.exec_timeout_s)}s"]
923
+ return argv
924
+
925
+ def _antigravity_mode_args(self) -> list[str]:
926
+ """Map ``sandbox_mode`` → antigravity ``--mode`` flags, clamped.
927
+
928
+ Same clamp as claude/codex/grok (design §5.4): when
929
+ ``allow_file_writes`` is False the effective mode is forced to
930
+ ``read_only`` regardless of ``sandbox_mode``, so writes always
931
+ require the explicit opt-in.
932
+ """
933
+ mode = self.acfg.sandbox_mode if self.acfg.allow_file_writes else "read_only"
934
+ return list(_ANTIGRAVITY_MODE_ARGS[mode])
935
+
936
+ def _parse_antigravity(
937
+ self, stdout: bytes, stderr: bytes
938
+ ) -> tuple[str, dict[str, Any], dict[str, Any]]:
939
+ """Parse antigravity's plain-text ``-p`` output (verified agy 1.1.1).
940
+
941
+ There is no ``--output-format`` flag at all (agy's ``--help`` does
942
+ not list one) — output is whatever the model printed, decorated
943
+ with whatever terminal styling the CLI applied even in non-TTY runs.
944
+ Parsing is therefore: UTF-8 decode (defensively, replacing invalid
945
+ bytes) → strip ANSI escape sequences (``_ANSI_RE``, best-effort, see
946
+ its definition) → ``.strip()`` surrounding whitespace. Empty output
947
+ raises a retryable :class:`AdapterError` so the chain can fall
948
+ through. There is no token/cost figure and no session id anywhere
949
+ in agy's output, so ``usage`` is all-zeros (cost stays 0 unless the
950
+ operator sets ``ProviderConfig.cost``, same rationale as grok,
951
+ design §5.1.6) and ``meta`` is empty.
952
+ """
953
+ text = _ANSI_RE.sub("", stdout.decode("utf-8", "replace")).strip()
954
+ if not text:
955
+ raise AdapterError(
956
+ "antigravity produced no stdout",
957
+ provider=self.name,
958
+ retryable=True,
959
+ )
960
+ usage: dict[str, Any] = {
961
+ "prompt_tokens": 0,
962
+ "completion_tokens": 0,
963
+ "total_tokens": 0,
964
+ }
965
+ meta: dict[str, Any] = {}
966
+ return text, usage, meta
967
+
777
968
  # ------------------------------------------------------------------
778
969
  # helpers: prompt rendering, response shaping, env, workdir, kill
779
970
  # ------------------------------------------------------------------