alysis-code 0.13.0__py3-none-any.whl

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 (458) hide show
  1. alysis_code/__init__.py +3 -0
  2. alysis_code/__main__.py +4 -0
  3. alysis_code/_build_info.py +14 -0
  4. alysis_code/account_login.py +468 -0
  5. alysis_code/agent/README.md +35 -0
  6. alysis_code/agent/__init__.py +11 -0
  7. alysis_code/agent/acceptance_contract.py +2217 -0
  8. alysis_code/agent/blast_radius.py +1403 -0
  9. alysis_code/agent/cache_keepalive.py +227 -0
  10. alysis_code/agent/completion_certificate.py +366 -0
  11. alysis_code/agent/completion_gate.py +306 -0
  12. alysis_code/agent/empty_response_stall.py +403 -0
  13. alysis_code/agent/errors.py +28 -0
  14. alysis_code/agent/llm_calls.py +475 -0
  15. alysis_code/agent/mutation_classification.py +227 -0
  16. alysis_code/agent/prompt_context.py +2508 -0
  17. alysis_code/agent/read_ledger.py +253 -0
  18. alysis_code/agent/regression_baseline.py +642 -0
  19. alysis_code/agent/reproduction_first.py +610 -0
  20. alysis_code/agent/sensitive_output.py +629 -0
  21. alysis_code/agent/session.py +3218 -0
  22. alysis_code/agent/steering.py +191 -0
  23. alysis_code/agent/subagent_execution.py +5177 -0
  24. alysis_code/agent/subagent_workspace.py +666 -0
  25. alysis_code/agent/tools_assembly.py +4728 -0
  26. alysis_code/agent/turn/__init__.py +101 -0
  27. alysis_code/agent/turn/core.py +8483 -0
  28. alysis_code/agent/turn/events.py +113 -0
  29. alysis_code/agent/turn/exploration.py +590 -0
  30. alysis_code/agent/turn/interventions.py +65 -0
  31. alysis_code/agent/turn/read_cache.py +420 -0
  32. alysis_code/agent/turn/snapshot.py +179 -0
  33. alysis_code/agent/turn_contract.py +661 -0
  34. alysis_code/agent/turn_path.py +129 -0
  35. alysis_code/agent/verification.py +2885 -0
  36. alysis_code/agent/verification_commands.py +512 -0
  37. alysis_code/agent/verification_evidence.py +738 -0
  38. alysis_code/agent_loop.py +693 -0
  39. alysis_code/agent_runtimes/__init__.py +51 -0
  40. alysis_code/agent_runtimes/base.py +114 -0
  41. alysis_code/agent_runtimes/builtins.py +129 -0
  42. alysis_code/agent_runtimes/codex_cli.py +664 -0
  43. alysis_code/agent_runtimes/host.py +263 -0
  44. alysis_code/agent_runtimes/registry.py +64 -0
  45. alysis_code/agent_runtimes/service.py +150 -0
  46. alysis_code/agentbox_client.py +416 -0
  47. alysis_code/agentbox_integration.py +310 -0
  48. alysis_code/alysis_cloud.py +152 -0
  49. alysis_code/approval_scope.py +276 -0
  50. alysis_code/assets/README.md +33 -0
  51. alysis_code/assets/__init__.py +126 -0
  52. alysis_code/assets/asset_read_core.py +281 -0
  53. alysis_code/assets/budget_allocator.py +456 -0
  54. alysis_code/assets/comprehender.py +759 -0
  55. alysis_code/assets/index.py +654 -0
  56. alysis_code/assets/ingestion.py +275 -0
  57. alysis_code/assets/legacy_migration.py +413 -0
  58. alysis_code/assets/models.py +263 -0
  59. alysis_code/assets/ocr.py +239 -0
  60. alysis_code/assets/owl/ascii/f-000.txt +13 -0
  61. alysis_code/assets/owl/ascii/f-001.txt +13 -0
  62. alysis_code/assets/owl/ascii/f-002.txt +13 -0
  63. alysis_code/assets/owl/ascii/f-003.txt +13 -0
  64. alysis_code/assets/owl/ascii/f-004.txt +13 -0
  65. alysis_code/assets/owl/ascii/f-005.txt +13 -0
  66. alysis_code/assets/owl/ascii/f-006.txt +13 -0
  67. alysis_code/assets/owl/ascii/f-007.txt +13 -0
  68. alysis_code/assets/owl/ascii/f-008.txt +13 -0
  69. alysis_code/assets/owl/ascii/f-009.txt +13 -0
  70. alysis_code/assets/owl/ascii/f-010.txt +13 -0
  71. alysis_code/assets/owl/ascii/f-011.txt +13 -0
  72. alysis_code/assets/owl/ascii/f-012.txt +13 -0
  73. alysis_code/assets/owl/ascii/f-013.txt +13 -0
  74. alysis_code/assets/owl/ascii/f-014.txt +13 -0
  75. alysis_code/assets/owl/ascii/f-015.txt +13 -0
  76. alysis_code/assets/owl/ascii/f-016.txt +13 -0
  77. alysis_code/assets/owl/ascii/f-017.txt +13 -0
  78. alysis_code/assets/owl/ascii/f-018.txt +13 -0
  79. alysis_code/assets/owl/ascii/f-019.txt +13 -0
  80. alysis_code/assets/owl/ascii/f-020.txt +13 -0
  81. alysis_code/assets/owl/index.html +98 -0
  82. alysis_code/assets/owl/show-owl.sh +761 -0
  83. alysis_code/assets/paths.py +49 -0
  84. alysis_code/assets/plan_binding.py +326 -0
  85. alysis_code/assets/planner_context.py +466 -0
  86. alysis_code/assets/planner_tools.py +184 -0
  87. alysis_code/assets/prompts.py +101 -0
  88. alysis_code/assets/replanner_context.py +239 -0
  89. alysis_code/assets/surface.py +521 -0
  90. alysis_code/assets/untrusted_content.py +48 -0
  91. alysis_code/assets/usage_logger.py +94 -0
  92. alysis_code/assets/worker_mirror.py +428 -0
  93. alysis_code/assets/worker_section.py +303 -0
  94. alysis_code/assets/worker_tools.py +468 -0
  95. alysis_code/atomic_io.py +83 -0
  96. alysis_code/auth_diagnostics.py +272 -0
  97. alysis_code/background_runner.py +366 -0
  98. alysis_code/branding.py +270 -0
  99. alysis_code/budget_policy.py +390 -0
  100. alysis_code/build_identity.py +465 -0
  101. alysis_code/builtin_hooks/__init__.py +7 -0
  102. alysis_code/builtin_hooks/notify_done_windows.py +65 -0
  103. alysis_code/bwrap_etc.py +76 -0
  104. alysis_code/cancellation.py +41 -0
  105. alysis_code/capabilities.py +137 -0
  106. alysis_code/chatgpt_codex_static_provider.py +133 -0
  107. alysis_code/cli.py +51 -0
  108. alysis_code/cli_impl/__init__.py +1 -0
  109. alysis_code/cli_impl/assets_cli.py +537 -0
  110. alysis_code/cli_impl/assets_modal.py +412 -0
  111. alysis_code/cli_impl/chat/__init__.py +156 -0
  112. alysis_code/cli_impl/chat/commands.py +2616 -0
  113. alysis_code/cli_impl/chat/loop.py +4508 -0
  114. alysis_code/cli_impl/chat/mid_turn_policy.py +125 -0
  115. alysis_code/cli_impl/chat/rendering.py +444 -0
  116. alysis_code/cli_impl/chat/state.py +124 -0
  117. alysis_code/cli_impl/chat_resume.py +830 -0
  118. alysis_code/cli_impl/chat_slash_completer.py +258 -0
  119. alysis_code/cli_impl/commands/__init__.py +11 -0
  120. alysis_code/cli_impl/commands/_shared.py +89 -0
  121. alysis_code/cli_impl/commands/auth.py +623 -0
  122. alysis_code/cli_impl/commands/chat_resume_helpers.py +1531 -0
  123. alysis_code/cli_impl/commands/chat_state.py +158 -0
  124. alysis_code/cli_impl/commands/chat_status.py +1248 -0
  125. alysis_code/cli_impl/commands/chat_terminal.py +942 -0
  126. alysis_code/cli_impl/commands/chat_tui_panels.py +1018 -0
  127. alysis_code/cli_impl/commands/cli_common.py +1223 -0
  128. alysis_code/cli_impl/commands/cli_surface.py +77 -0
  129. alysis_code/cli_impl/commands/config.py +131 -0
  130. alysis_code/cli_impl/commands/conventions.py +85 -0
  131. alysis_code/cli_impl/commands/execution_helpers.py +350 -0
  132. alysis_code/cli_impl/commands/extensions.py +401 -0
  133. alysis_code/cli_impl/commands/forge.py +1282 -0
  134. alysis_code/cli_impl/commands/forge_asset_view.py +121 -0
  135. alysis_code/cli_impl/commands/forge_helpers.py +1215 -0
  136. alysis_code/cli_impl/commands/hooks.py +737 -0
  137. alysis_code/cli_impl/commands/ide_bridge.py +31 -0
  138. alysis_code/cli_impl/commands/mcp.py +700 -0
  139. alysis_code/cli_impl/commands/profile.py +453 -0
  140. alysis_code/cli_impl/commands/prompt_helpers.py +307 -0
  141. alysis_code/cli_impl/commands/report.py +88 -0
  142. alysis_code/cli_impl/commands/root.py +1118 -0
  143. alysis_code/cli_impl/commands/sandbox.py +184 -0
  144. alysis_code/cli_impl/commands/server.py +54 -0
  145. alysis_code/cli_impl/commands/sessions.py +252 -0
  146. alysis_code/cli_impl/commands/skills.py +404 -0
  147. alysis_code/cli_impl/commands/startup.py +946 -0
  148. alysis_code/cli_impl/commands/tools.py +335 -0
  149. alysis_code/cli_impl/commands/update.py +364 -0
  150. alysis_code/cli_impl/commands/welcome.py +972 -0
  151. alysis_code/cli_impl/config_menu.py +3882 -0
  152. alysis_code/cli_impl/forge.py +4509 -0
  153. alysis_code/cli_impl/forge_recovery.py +485 -0
  154. alysis_code/cli_impl/setup_wizard.py +2409 -0
  155. alysis_code/cli_impl/tui/__init__.py +58 -0
  156. alysis_code/cli_impl/tui/app.py +4551 -0
  157. alysis_code/cli_impl/tui/config.py +32 -0
  158. alysis_code/cli_impl/tui/config_flow.py +2754 -0
  159. alysis_code/cli_impl/tui/config_overlay.py +566 -0
  160. alysis_code/cli_impl/tui/content.py +78 -0
  161. alysis_code/cli_impl/tui/footer.py +218 -0
  162. alysis_code/cli_impl/tui/forge_status.py +136 -0
  163. alysis_code/cli_impl/tui/markdown.py +244 -0
  164. alysis_code/cli_impl/tui/owl.py +109 -0
  165. alysis_code/cli_impl/tui/plan_meta.py +477 -0
  166. alysis_code/cli_impl/tui/setup_app.py +519 -0
  167. alysis_code/cli_impl/tui/setup_flow.py +1622 -0
  168. alysis_code/cli_impl/tui/state.py +101 -0
  169. alysis_code/cli_impl/tui/subagent_identity.py +66 -0
  170. alysis_code/cli_impl/tui/subagent_panel.py +186 -0
  171. alysis_code/cli_impl/tui/surface.py +796 -0
  172. alysis_code/cli_impl/tui/transcript.py +514 -0
  173. alysis_code/cli_impl/tui/update_prompt.py +79 -0
  174. alysis_code/cli_impl/tui/workspace_guard.py +384 -0
  175. alysis_code/clipboard.py +172 -0
  176. alysis_code/code_review.py +1211 -0
  177. alysis_code/compaction/__init__.py +28 -0
  178. alysis_code/compaction/conversation_compactor.py +2932 -0
  179. alysis_code/compaction/importance.py +177 -0
  180. alysis_code/compaction/settings.py +297 -0
  181. alysis_code/compaction/tool_output_offload.py +447 -0
  182. alysis_code/config.py +3509 -0
  183. alysis_code/conflict_auto_resolver.py +895 -0
  184. alysis_code/context/__init__.py +1 -0
  185. alysis_code/context/tool_schema_budgeter.py +220 -0
  186. alysis_code/crash_diagnostics.py +282 -0
  187. alysis_code/custom_tools/README.md +34 -0
  188. alysis_code/custom_tools/__init__.py +43 -0
  189. alysis_code/custom_tools/discovery.py +903 -0
  190. alysis_code/custom_tools/runtime.py +1516 -0
  191. alysis_code/custom_tools/session.py +227 -0
  192. alysis_code/custom_tools/trust.py +232 -0
  193. alysis_code/diff_paths.py +113 -0
  194. alysis_code/direction_change.py +293 -0
  195. alysis_code/dispatch_timing.py +306 -0
  196. alysis_code/durable_service_manager.py +1236 -0
  197. alysis_code/edit_discipline.py +659 -0
  198. alysis_code/error_text.py +73 -0
  199. alysis_code/execution_budget.py +411 -0
  200. alysis_code/execution_context.py +915 -0
  201. alysis_code/execution_deadline.py +1065 -0
  202. alysis_code/execution_shared.py +1904 -0
  203. alysis_code/extensions/README.md +30 -0
  204. alysis_code/extensions/__init__.py +93 -0
  205. alysis_code/extensions/activation.py +138 -0
  206. alysis_code/extensions/install.py +1436 -0
  207. alysis_code/extensions/manifest.py +487 -0
  208. alysis_code/extensions/models.py +74 -0
  209. alysis_code/extensions/paths.py +56 -0
  210. alysis_code/extensions/registry.json +4 -0
  211. alysis_code/extensions/registry.py +52 -0
  212. alysis_code/extensions/state.py +83 -0
  213. alysis_code/extensions/workspace_trust.py +101 -0
  214. alysis_code/failed_task_evidence.py +369 -0
  215. alysis_code/failure_category.py +315 -0
  216. alysis_code/feedback_report.py +1647 -0
  217. alysis_code/file_classification.py +485 -0
  218. alysis_code/forge.py +2064 -0
  219. alysis_code/forge_completion.py +362 -0
  220. alysis_code/forge_events.py +475 -0
  221. alysis_code/frontmatter_utils.py +95 -0
  222. alysis_code/git_evidence.py +1181 -0
  223. alysis_code/git_ops.py +560 -0
  224. alysis_code/git_safe.py +62 -0
  225. alysis_code/git_worktrees.py +190 -0
  226. alysis_code/hooks/README.md +33 -0
  227. alysis_code/hooks/__init__.py +67 -0
  228. alysis_code/hooks/audit.py +171 -0
  229. alysis_code/hooks/config.py +225 -0
  230. alysis_code/hooks/dispatcher.py +1110 -0
  231. alysis_code/hooks/models.py +447 -0
  232. alysis_code/hooks/trust.py +202 -0
  233. alysis_code/host_actions.py +543 -0
  234. alysis_code/host_browser.py +103 -0
  235. alysis_code/ide/__init__.py +5 -0
  236. alysis_code/ide/activity_events.py +399 -0
  237. alysis_code/ide/approvals.py +337 -0
  238. alysis_code/ide/artifacts.py +153 -0
  239. alysis_code/ide/browser_egress_proxy.py +1076 -0
  240. alysis_code/ide/cdp_websocket_transport.py +1192 -0
  241. alysis_code/ide/change_ledger.py +1721 -0
  242. alysis_code/ide/context_blocks.py +979 -0
  243. alysis_code/ide/event_stream.py +531 -0
  244. alysis_code/ide/forge_protocol.py +3112 -0
  245. alysis_code/ide/forge_request_ledger.py +737 -0
  246. alysis_code/ide/health.py +965 -0
  247. alysis_code/ide/managed_browser.py +2251 -0
  248. alysis_code/ide/management_protocol.py +3414 -0
  249. alysis_code/ide/mcp_oauth_coordinator.py +744 -0
  250. alysis_code/ide/mcp_oauth_lifecycle.py +1504 -0
  251. alysis_code/ide/prompt_queue.py +1070 -0
  252. alysis_code/ide/protocol.py +191 -0
  253. alysis_code/ide/resumable_swarm.py +1543 -0
  254. alysis_code/ide/session_search.py +295 -0
  255. alysis_code/ide/stdio_bridge.py +9935 -0
  256. alysis_code/ide/structured_state.py +1579 -0
  257. alysis_code/ide/swarm_protocol.py +816 -0
  258. alysis_code/integration_gate.py +506 -0
  259. alysis_code/interactive_input_guard.py +39 -0
  260. alysis_code/interactive_plan_mode.py +26 -0
  261. alysis_code/internal_artifacts.py +179 -0
  262. alysis_code/knowledge_base.py +1409 -0
  263. alysis_code/knowledge_capture.py +1190 -0
  264. alysis_code/knowledge_librarian.py +605 -0
  265. alysis_code/language_policy.py +34 -0
  266. alysis_code/litellm_static_provider.py +535 -0
  267. alysis_code/llm/__init__.py +1 -0
  268. alysis_code/llm/anthropic_messages.py +2288 -0
  269. alysis_code/llm/base.py +71 -0
  270. alysis_code/llm/cache_capabilities.py +985 -0
  271. alysis_code/llm/cache_control_blocks.py +244 -0
  272. alysis_code/llm/cache_policy.py +388 -0
  273. alysis_code/llm/factory.py +373 -0
  274. alysis_code/llm/gemini_generate_content.py +2652 -0
  275. alysis_code/llm/gemini_interactions.py +739 -0
  276. alysis_code/llm/metadata.py +450 -0
  277. alysis_code/llm/openai_compat.py +2947 -0
  278. alysis_code/llm/openai_responses.py +2604 -0
  279. alysis_code/llm/protocols.py +609 -0
  280. alysis_code/llm/provider_limits.py +525 -0
  281. alysis_code/llm/request_plan.py +389 -0
  282. alysis_code/llm/request_shape.py +238 -0
  283. alysis_code/llm/streaming.py +108 -0
  284. alysis_code/llm/temperature_compat.py +78 -0
  285. alysis_code/llm/types.py +195 -0
  286. alysis_code/llm/usage_normalization.py +222 -0
  287. alysis_code/llm_error_display.py +315 -0
  288. alysis_code/logging_redaction.py +326 -0
  289. alysis_code/managed_host_deadline.py +196 -0
  290. alysis_code/mcp/README.md +33 -0
  291. alysis_code/mcp/__init__.py +24 -0
  292. alysis_code/mcp/client.py +1137 -0
  293. alysis_code/mcp/config.py +597 -0
  294. alysis_code/mcp/errors.py +113 -0
  295. alysis_code/mcp/forge_scope.py +154 -0
  296. alysis_code/mcp/jsonrpc.py +214 -0
  297. alysis_code/mcp/manager.py +2308 -0
  298. alysis_code/mcp/models.py +666 -0
  299. alysis_code/mcp/oauth.py +972 -0
  300. alysis_code/mcp/oauth_runtime.py +310 -0
  301. alysis_code/mcp/oauth_store.py +276 -0
  302. alysis_code/mcp/prompts.py +329 -0
  303. alysis_code/mcp/resources.py +295 -0
  304. alysis_code/mcp/roots.py +106 -0
  305. alysis_code/mcp/server_requests.py +75 -0
  306. alysis_code/mcp/token_store.py +859 -0
  307. alysis_code/mcp/transport_http.py +1338 -0
  308. alysis_code/mcp/transport_stdio.py +1267 -0
  309. alysis_code/mcp/untrusted_content.py +119 -0
  310. alysis_code/merge_conflict_reviewer.py +729 -0
  311. alysis_code/model_catalog/__init__.py +1 -0
  312. alysis_code/model_catalog/chatgpt_codex_subscription_snapshot.json +186 -0
  313. alysis_code/model_catalog/litellm_model_prices_snapshot.json +44715 -0
  314. alysis_code/model_catalog/litellm_model_prices_snapshot.meta.json +17 -0
  315. alysis_code/model_metadata_policy.py +223 -0
  316. alysis_code/model_metadata_utils.py +103 -0
  317. alysis_code/model_registry.py +1420 -0
  318. alysis_code/model_router.py +147 -0
  319. alysis_code/permission_policy.py +1016 -0
  320. alysis_code/personas.py +451 -0
  321. alysis_code/pipeline_facts.py +233 -0
  322. alysis_code/plan_assistant.py +4763 -0
  323. alysis_code/plan_mode.py +393 -0
  324. alysis_code/plan_reconciliation.py +1228 -0
  325. alysis_code/plan_repair.py +652 -0
  326. alysis_code/plan_validation.py +1099 -0
  327. alysis_code/planning_constraints.py +904 -0
  328. alysis_code/policy.py +95 -0
  329. alysis_code/preview_server.py +457 -0
  330. alysis_code/process_reaping.py +566 -0
  331. alysis_code/profile_presets.py +1834 -0
  332. alysis_code/profiles.py +666 -0
  333. alysis_code/provider_auth/__init__.py +29 -0
  334. alysis_code/provider_auth/base.py +99 -0
  335. alysis_code/provider_auth/openai_codex.py +951 -0
  336. alysis_code/provider_auth/registry.py +76 -0
  337. alysis_code/provider_auth/store.py +125 -0
  338. alysis_code/provider_diagnostics.py +1209 -0
  339. alysis_code/provider_model_catalog.py +685 -0
  340. alysis_code/provider_telemetry.py +1699 -0
  341. alysis_code/provider_url.py +75 -0
  342. alysis_code/reasoning_contracts.py +911 -0
  343. alysis_code/remote_sync.py +350 -0
  344. alysis_code/replanning.py +1195 -0
  345. alysis_code/repo_scan.py +1152 -0
  346. alysis_code/request_estimation.py +296 -0
  347. alysis_code/review_gate.py +617 -0
  348. alysis_code/run_lock.py +1141 -0
  349. alysis_code/run_outcome.py +58 -0
  350. alysis_code/run_provenance.py +774 -0
  351. alysis_code/run_state.py +445 -0
  352. alysis_code/runtime_artifacts.py +116 -0
  353. alysis_code/runtime_context_features.py +78 -0
  354. alysis_code/runtime_kind.py +52 -0
  355. alysis_code/safety/__init__.py +11 -0
  356. alysis_code/safety/mcp_sanitize.py +29 -0
  357. alysis_code/safety/safe_http.py +297 -0
  358. alysis_code/safety/subagent_report.py +184 -0
  359. alysis_code/sandbox_doctor.py +682 -0
  360. alysis_code/sandbox_runner.py +1025 -0
  361. alysis_code/sandbox_settings.py +423 -0
  362. alysis_code/serialized_paths.py +355 -0
  363. alysis_code/server/__init__.py +3 -0
  364. alysis_code/server/app.py +367 -0
  365. alysis_code/server/auth.py +34 -0
  366. alysis_code/server/job_config.py +30 -0
  367. alysis_code/server/settings.py +215 -0
  368. alysis_code/server/store.py +193 -0
  369. alysis_code/server/worker_runner.py +657 -0
  370. alysis_code/service_persistence.py +355 -0
  371. alysis_code/session_artifacts.py +108 -0
  372. alysis_code/session_metrics.py +331 -0
  373. alysis_code/session_store.py +624 -0
  374. alysis_code/skills/README.md +34 -0
  375. alysis_code/skills/__init__.py +104 -0
  376. alysis_code/skills/conventions.py +84 -0
  377. alysis_code/skills/discovery.py +176 -0
  378. alysis_code/skills/eval_models.py +232 -0
  379. alysis_code/skills/eval_runner.py +372 -0
  380. alysis_code/skills/evals.py +1344 -0
  381. alysis_code/skills/install.py +293 -0
  382. alysis_code/skills/loader.py +118 -0
  383. alysis_code/skills/matching.py +103 -0
  384. alysis_code/skills/models.py +71 -0
  385. alysis_code/skills/paths.py +56 -0
  386. alysis_code/skills/prompting.py +500 -0
  387. alysis_code/skills/scaffold.py +142 -0
  388. alysis_code/skills/state.py +441 -0
  389. alysis_code/skills/transactions.py +125 -0
  390. alysis_code/skills/validation.py +304 -0
  391. alysis_code/step_budget.py +238 -0
  392. alysis_code/subagent_labels.py +49 -0
  393. alysis_code/subagents.py +1072 -0
  394. alysis_code/surface/__init__.py +80 -0
  395. alysis_code/surface/base.py +305 -0
  396. alysis_code/surface/console.py +387 -0
  397. alysis_code/surface/events.py +372 -0
  398. alysis_code/surface/hidden_surface.py +529 -0
  399. alysis_code/surface/noop_surface.py +219 -0
  400. alysis_code/surface/rich_surface.py +1555 -0
  401. alysis_code/surface/styles.py +67 -0
  402. alysis_code/surface/theme.py +455 -0
  403. alysis_code/surface/types.py +100 -0
  404. alysis_code/swarm_backend.py +926 -0
  405. alysis_code/swarm_orchestrator.py +4020 -0
  406. alysis_code/swarm_scheduler.py +441 -0
  407. alysis_code/swarm_trace.py +429 -0
  408. alysis_code/swarm_worker.py +2119 -0
  409. alysis_code/swarm_write_guard.py +348 -0
  410. alysis_code/task_dependencies.py +170 -0
  411. alysis_code/task_readiness.py +992 -0
  412. alysis_code/task_scope.py +2148 -0
  413. alysis_code/terminal_manager.py +762 -0
  414. alysis_code/terminal_ownership.py +460 -0
  415. alysis_code/text_normalization.py +30 -0
  416. alysis_code/token_budget.py +97 -0
  417. alysis_code/tools/README.md +34 -0
  418. alysis_code/tools/__init__.py +1 -0
  419. alysis_code/tools/artifacts.py +127 -0
  420. alysis_code/tools/availability.py +188 -0
  421. alysis_code/tools/fs.py +1456 -0
  422. alysis_code/tools/git.py +461 -0
  423. alysis_code/tools/history.py +229 -0
  424. alysis_code/tools/http_timeout.py +78 -0
  425. alysis_code/tools/image_generation.py +552 -0
  426. alysis_code/tools/registry.py +2936 -0
  427. alysis_code/tools/repo_map.py +476 -0
  428. alysis_code/tools/search.py +563 -0
  429. alysis_code/tools/shell.py +135 -0
  430. alysis_code/tools/symbols.py +1350 -0
  431. alysis_code/tools/test_discovery.py +643 -0
  432. alysis_code/tools/web.py +482 -0
  433. alysis_code/tools/web_search.py +2012 -0
  434. alysis_code/tools/web_search_dashscope.py +557 -0
  435. alysis_code/tools/web_search_ddgs.py +221 -0
  436. alysis_code/tools/web_search_provider_adapters.py +1429 -0
  437. alysis_code/tools/web_search_tavily.py +194 -0
  438. alysis_code/updates.py +933 -0
  439. alysis_code/usage_tracker.py +1990 -0
  440. alysis_code/verification_command_analysis.py +1004 -0
  441. alysis_code/verification_contract.py +574 -0
  442. alysis_code/verification_failure_summary.py +273 -0
  443. alysis_code/verification_repair.py +385 -0
  444. alysis_code/verify_gate.py +3129 -0
  445. alysis_code/web_research.py +1872 -0
  446. alysis_code/web_search_adapters.py +66 -0
  447. alysis_code/web_search_policy.py +27 -0
  448. alysis_code/workspace_binding.py +389 -0
  449. alysis_code/workspace_binding_ui.py +408 -0
  450. alysis_code/workspace_context.py +273 -0
  451. alysis_code/workspace_isolation.py +138 -0
  452. alysis_code/workspace_provisioning.py +455 -0
  453. alysis_code-0.13.0.dist-info/METADATA +507 -0
  454. alysis_code-0.13.0.dist-info/RECORD +458 -0
  455. alysis_code-0.13.0.dist-info/WHEEL +4 -0
  456. alysis_code-0.13.0.dist-info/entry_points.txt +3 -0
  457. alysis_code-0.13.0.dist-info/licenses/LICENSE +176 -0
  458. alysis_code-0.13.0.dist-info/licenses/NOTICE +4 -0
@@ -0,0 +1,911 @@
1
+ """Static per-model reasoning contracts — the wire truth table.
2
+
3
+ Source: ``MODEL_CAPABILITY_CONTRACTS_2026-07-19.md`` (19 provider blocks, each
4
+ adversarially verified against official docs). This module is step 1 of that
5
+ report's implementation order: the data nothing at runtime can discover —
6
+ whether a model's reasoning is ``always-on`` / ``optional`` / ``none``, the
7
+ exact wire spelling that controls it, the allowed value strings, and what
8
+ "off" actually does (omit the param, send an explicit disable, impossible, or
9
+ — worst — silently swap the model).
10
+
11
+ Emission rule this table exists to enforce (report hazard ranks 5, 8, 9, 12,
12
+ 16, 18): **allowlist-based emission** — a client may send a reasoning
13
+ parameter only when the contract says this model accepts that exact spelling
14
+ and value. ``UNKNOWN`` means send nothing.
15
+
16
+ The mechanisms here are provider-agnostic; only the data is per-provider.
17
+ Clients keep their own payload shaping (e.g. ``anthropic_messages`` builds the
18
+ ``thinking`` config) and consult this table for the *decision*.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ from dataclasses import dataclass
24
+
25
+ # Reasoning modes.
26
+ ALWAYS_ON = "always-on"
27
+ OPTIONAL = "optional"
28
+ NONE = "none"
29
+ UNKNOWN = "unknown"
30
+
31
+ # Wire spellings (how the knob is expressed on this surface).
32
+ WIRE_REASONING_EFFORT = "reasoning_effort" # flat string param (openai-chat style)
33
+ WIRE_THINKING_TYPE = "thinking_type" # thinking: {"type": ...}
34
+ WIRE_THINKING_ADAPTIVE = "thinking_adaptive" # thinking:{"type":"adaptive"} + output_config effort
35
+ WIRE_BUDGET_TOKENS = "budget_tokens" # thinking:{"type":"enabled","budget_tokens":N}
36
+ WIRE_THINKING_LEVEL = "thinking_level" # generationConfig.thinkingConfig.thinkingLevel
37
+ WIRE_ENABLE_THINKING = "enable_thinking" # boolean (+ optional thinking_budget)
38
+ WIRE_CHAT_TEMPLATE_ENABLE_THINKING = (
39
+ "chat_template_enable_thinking" # chat_template_kwargs: {enable_thinking: bool}
40
+ )
41
+ WIRE_REASONING_OBJECT = "reasoning_object" # reasoning: {effort|max_tokens|enabled|exclude}
42
+ WIRE_REASONING_ENABLED = "reasoning_enabled" # reasoning: {"enabled": bool}
43
+ WIRE_NONE = "none" # no reasoning knob exists on this surface
44
+
45
+ # Off-path semantics.
46
+ OFF_OMIT = "omit-param" # leaving the param out means no thinking
47
+ OFF_EXPLICIT = "explicit-disable" # a documented disable value exists
48
+ OFF_IMPOSSIBLE = "impossible" # reasoning cannot be turned off (disable = error)
49
+ OFF_SWAPS_MODEL = "swaps-model" # "off" silently substitutes a different model
50
+ OFF_UNKNOWN = "unknown"
51
+
52
+
53
+ @dataclass(frozen=True)
54
+ class ReasoningContract:
55
+ """What one model accepts on one provider surface."""
56
+
57
+ mode: str = UNKNOWN
58
+ wire: str = WIRE_NONE
59
+ values: tuple[str, ...] = () # exact allowed effort/level strings ("" knob → empty)
60
+ default: str = "" # server default value; "" when unknown / not applicable
61
+ off: str = OFF_UNKNOWN
62
+ replay_reasoning_content: bool = False
63
+ accepts_tool_choice_while_reasoning: bool = True
64
+ emits_flat_reasoning_effort: bool = False # verified on Chat Completions
65
+ notes: str = ""
66
+
67
+ def allows_value(self, value: str | None) -> bool:
68
+ normalized = str(value or "").strip().casefold()
69
+ if not normalized:
70
+ return False
71
+ return normalized in self.values
72
+
73
+ @property
74
+ def toggleable(self) -> bool:
75
+ return self.mode == OPTIONAL and self.off in {OFF_OMIT, OFF_EXPLICIT}
76
+
77
+
78
+ UNKNOWN_CONTRACT = ReasoningContract()
79
+
80
+
81
+ def reasoning_labels_allowed_by_contract(
82
+ contract: ReasoningContract,
83
+ labels: list[str] | tuple[str, ...],
84
+ *,
85
+ current: str,
86
+ ) -> list[str]:
87
+ """Filter a reasoning picker to the active model's documented contract."""
88
+
89
+ if contract is UNKNOWN_CONTRACT:
90
+ return list(labels)
91
+ effort_wires = {
92
+ WIRE_REASONING_EFFORT,
93
+ WIRE_THINKING_LEVEL,
94
+ WIRE_THINKING_ADAPTIVE,
95
+ }
96
+ filter_by_values = bool(contract.values) and contract.wire in effort_wires
97
+ toggle_only = contract.wire == WIRE_CHAT_TEMPLATE_ENABLE_THINKING
98
+ allowed: list[str] = []
99
+ for label in labels:
100
+ normalized = label.casefold()
101
+ if label == current:
102
+ allowed.append(label)
103
+ continue
104
+ if normalized == "off":
105
+ if contract.mode == ALWAYS_ON and contract.off != OFF_SWAPS_MODEL:
106
+ continue
107
+ allowed.append(label)
108
+ continue
109
+ if normalized == "auto":
110
+ allowed.append(label)
111
+ continue
112
+ if toggle_only:
113
+ continue
114
+ if filter_by_values and normalized not in contract.values:
115
+ continue
116
+ allowed.append(label)
117
+ return allowed
118
+
119
+
120
+ # Per provider_key: ordered (prefix, contract) rules; first match wins. Exact
121
+ # ids sort naturally before shorter prefixes where it matters.
122
+ _C = ReasoningContract
123
+ _CONTRACTS: dict[str, tuple[tuple[str, ReasoningContract], ...]] = {
124
+ "openai": (
125
+ # tools + reasoning_effort != "none" on /v1/chat/completions is a 400
126
+ # on the 5.6/5.4 families (report hazard #1) — the *value* allowlist is
127
+ # identical on both endpoints; routing is the client's decision.
128
+ (
129
+ "gpt-5.6-",
130
+ _C(
131
+ mode=OPTIONAL,
132
+ wire=WIRE_REASONING_EFFORT,
133
+ values=("none", "low", "medium", "high", "xhigh", "max"),
134
+ default="medium",
135
+ off=OFF_EXPLICIT,
136
+ notes="chat surface 400s on tools + effort!=none; 'minimal' is dead on 5.x",
137
+ ),
138
+ ),
139
+ (
140
+ "gpt-5.4-",
141
+ _C(
142
+ mode=OPTIONAL,
143
+ wire=WIRE_REASONING_EFFORT,
144
+ values=("none", "low", "medium", "high", "xhigh", "max"),
145
+ off=OFF_EXPLICIT,
146
+ notes="same chat-surface tools+effort 400 as 5.6 when effort raised",
147
+ ),
148
+ ),
149
+ (
150
+ "gpt-5.3-codex",
151
+ _C(
152
+ mode=ALWAYS_ON,
153
+ wire=WIRE_REASONING_EFFORT,
154
+ values=("low", "medium", "high", "xhigh"),
155
+ off=OFF_IMPOSSIBLE,
156
+ notes="effort 'none' is a 400 unsupported_value",
157
+ ),
158
+ ),
159
+ ),
160
+ "anthropic": (
161
+ (
162
+ "claude-fable-5",
163
+ _C(
164
+ mode=ALWAYS_ON,
165
+ wire=WIRE_THINKING_ADAPTIVE,
166
+ values=("low", "medium", "high", "xhigh", "max"),
167
+ off=OFF_IMPOSSIBLE,
168
+ notes="thinking:{'type':'disabled'} is rejected; always adaptive",
169
+ ),
170
+ ),
171
+ (
172
+ "claude-haiku-4-5",
173
+ _C(
174
+ mode=OPTIONAL,
175
+ wire=WIRE_BUDGET_TOKENS,
176
+ off=OFF_EXPLICIT,
177
+ notes="extended thinking only (budget_tokens >= 1024); no effort param",
178
+ ),
179
+ ),
180
+ (
181
+ "claude-sonnet-5",
182
+ _C(
183
+ mode=OPTIONAL,
184
+ wire=WIRE_THINKING_ADAPTIVE,
185
+ values=("low", "medium", "high", "xhigh", "max"),
186
+ off=OFF_OMIT,
187
+ notes="extended shape = 400; non-default temperature/top_p/top_k = 400",
188
+ ),
189
+ ),
190
+ (
191
+ "claude-opus-5",
192
+ _C(
193
+ mode=OPTIONAL,
194
+ wire=WIRE_THINKING_ADAPTIVE,
195
+ values=("low", "medium", "high", "xhigh", "max"),
196
+ default="high",
197
+ off=OFF_EXPLICIT,
198
+ notes="thinks by DEFAULT — omitting the param is adaptive, not off "
199
+ "(unlike 4.8/4.7); disable is accepted only at effort <= high, so "
200
+ "'disabled' + xhigh/max = 400; extended shape and non-default "
201
+ "sampling params = 400",
202
+ ),
203
+ ),
204
+ (
205
+ "claude-opus-4-8",
206
+ _C(
207
+ mode=OPTIONAL,
208
+ wire=WIRE_THINKING_ADAPTIVE,
209
+ values=("low", "medium", "high", "xhigh", "max"),
210
+ off=OFF_OMIT,
211
+ notes="extended shape = 400; non-default sampling params = 400",
212
+ ),
213
+ ),
214
+ (
215
+ "claude-opus-4-7",
216
+ _C(
217
+ mode=OPTIONAL,
218
+ wire=WIRE_THINKING_ADAPTIVE,
219
+ values=("low", "medium", "high", "xhigh", "max"),
220
+ off=OFF_OMIT,
221
+ notes="extended shape = 400; non-default sampling params = 400",
222
+ ),
223
+ ),
224
+ ),
225
+ "gemini": (
226
+ (
227
+ "gemini-3.7-flash",
228
+ _C(
229
+ mode=ALWAYS_ON,
230
+ wire=WIRE_THINKING_LEVEL,
231
+ values=("low", "medium", "high"),
232
+ default="medium",
233
+ off=OFF_IMPOSSIBLE,
234
+ notes="'minimal' is explicitly unsupported on Gemini 3.7 Flash",
235
+ ),
236
+ ),
237
+ (
238
+ "gemini-3.1-pro",
239
+ _C(
240
+ mode=ALWAYS_ON,
241
+ wire=WIRE_THINKING_LEVEL,
242
+ values=("low", "medium", "high"),
243
+ off=OFF_IMPOSSIBLE,
244
+ notes="'minimal' documented Not supported on 3.1 Pro",
245
+ ),
246
+ ),
247
+ (
248
+ "gemini-3",
249
+ _C(
250
+ mode=ALWAYS_ON,
251
+ wire=WIRE_THINKING_LEVEL,
252
+ values=("minimal", "low", "medium", "high"),
253
+ off=OFF_IMPOSSIBLE,
254
+ notes="no 3.x model can disable thinking; economy calls pin 'minimal'",
255
+ ),
256
+ ),
257
+ ),
258
+ "deepseek": (
259
+ (
260
+ "deepseek-",
261
+ _C(
262
+ mode=OPTIONAL,
263
+ wire=WIRE_REASONING_EFFORT,
264
+ values=("low", "high", "max"),
265
+ default="high",
266
+ off=OFF_EXPLICIT,
267
+ replay_reasoning_content=True,
268
+ accepts_tool_choice_while_reasoning=False,
269
+ notes="low|high|max are the exact effort values; thinking:{type} is the "
270
+ "separate on/off toggle; "
271
+ "reasoning_content is required across every tools-enabled assistant turn; "
272
+ "thinking mode rejects tool_choice; compatibility aliases are not exposed",
273
+ ),
274
+ ),
275
+ ),
276
+ # The Alysis Code hosted proxy forwards request bodies verbatim to DeepSeek,
277
+ # so its models follow the deepseek contract exactly (same wire, same
278
+ # levels, same off semantics).
279
+ "alysis": (
280
+ (
281
+ "deepseek-",
282
+ _C(
283
+ mode=OPTIONAL,
284
+ wire=WIRE_REASONING_EFFORT,
285
+ values=("low", "high", "max"),
286
+ default="high",
287
+ off=OFF_EXPLICIT,
288
+ replay_reasoning_content=True,
289
+ accepts_tool_choice_while_reasoning=False,
290
+ notes="hosted proxy passes reasoning fields through to DeepSeek unchanged; "
291
+ "mirrors the deepseek provider contract",
292
+ ),
293
+ ),
294
+ ),
295
+ "nvidia": (
296
+ (
297
+ "deepseek-ai/deepseek-v4-pro",
298
+ _C(
299
+ mode=OPTIONAL,
300
+ wire=WIRE_REASONING_EFFORT,
301
+ values=("high", "max"),
302
+ default="high",
303
+ off=OFF_EXPLICIT,
304
+ notes="NVIDIA-hosted DeepSeek V4 Pro accepts none|high|max on the flat "
305
+ "reasoning_effort field",
306
+ ),
307
+ ),
308
+ (
309
+ "deepseek-ai/deepseek-v4-flash",
310
+ _C(
311
+ mode=OPTIONAL,
312
+ wire=WIRE_REASONING_EFFORT,
313
+ values=("high", "max"),
314
+ default="high",
315
+ off=OFF_EXPLICIT,
316
+ notes="NVIDIA-hosted DeepSeek V4 Flash accepts none|high|max on the flat "
317
+ "reasoning_effort field",
318
+ ),
319
+ ),
320
+ (
321
+ "nvidia/nemotron-3-super-120b-a12b",
322
+ _C(
323
+ mode=OPTIONAL,
324
+ wire=WIRE_REASONING_EFFORT,
325
+ values=("low", "high"),
326
+ default="high",
327
+ off=OFF_EXPLICIT,
328
+ notes="NVIDIA's hosted endpoint accepts none|low|high and returns private "
329
+ "reasoning on the structured reasoning_content channel",
330
+ ),
331
+ ),
332
+ (
333
+ "nvidia/nemotron-3-ultra-550b-a55b",
334
+ _C(
335
+ mode=OPTIONAL,
336
+ wire=WIRE_REASONING_EFFORT,
337
+ values=("medium", "high"),
338
+ default="high",
339
+ off=OFF_EXPLICIT,
340
+ notes="NVIDIA's hosted endpoint accepts none|medium|high",
341
+ ),
342
+ ),
343
+ (
344
+ "nvidia/nemotron-3-nano-30b-a3b",
345
+ _C(
346
+ mode=OPTIONAL,
347
+ wire=WIRE_CHAT_TEMPLATE_ENABLE_THINKING,
348
+ off=OFF_EXPLICIT,
349
+ notes="the hosted endpoint documents the chat-template toggle, not a flat "
350
+ "reasoning_effort enum",
351
+ ),
352
+ ),
353
+ ),
354
+ "qwen": (
355
+ (
356
+ "qwen3.8-max",
357
+ _C(
358
+ mode=OPTIONAL,
359
+ wire=WIRE_REASONING_EFFORT,
360
+ values=("low", "medium", "xhigh"),
361
+ default="xhigh",
362
+ off=OFF_EXPLICIT,
363
+ replay_reasoning_content=True,
364
+ notes="effort is flat; enable_thinking is the separate on/off toggle",
365
+ ),
366
+ ),
367
+ (
368
+ "qwen3-coder",
369
+ _C(mode=NONE, wire=WIRE_NONE, off=OFF_OMIT, notes="coder line has no thinking"),
370
+ ),
371
+ (
372
+ "qwen",
373
+ _C(
374
+ mode=OPTIONAL,
375
+ wire=WIRE_ENABLE_THINKING,
376
+ off=OFF_EXPLICIT,
377
+ notes="3.7 line thinks by default — omitting the flag bills thinking tokens",
378
+ ),
379
+ ),
380
+ ),
381
+ "zhipu": (
382
+ (
383
+ "glm-5.2",
384
+ _C(
385
+ mode=OPTIONAL,
386
+ wire=WIRE_THINKING_TYPE,
387
+ values=("high",),
388
+ off=OFF_EXPLICIT,
389
+ notes="reasoning_effort low/medium coerce to high; OpenRouter-style "
390
+ "reasoning:{} object is silently ignored",
391
+ ),
392
+ ),
393
+ (
394
+ "glm-4.7-flash",
395
+ _C(mode=OPTIONAL, wire=WIRE_THINKING_TYPE, off=OFF_EXPLICIT),
396
+ ),
397
+ (
398
+ "glm-4.7",
399
+ _C(
400
+ mode=ALWAYS_ON,
401
+ wire=WIRE_THINKING_TYPE,
402
+ off=OFF_IMPOSSIBLE,
403
+ notes="thinks compulsorily despite a documented toggle",
404
+ ),
405
+ ),
406
+ (
407
+ "glm-",
408
+ _C(mode=OPTIONAL, wire=WIRE_THINKING_TYPE, off=OFF_EXPLICIT),
409
+ ),
410
+ ),
411
+ "zai_coding_plan": (
412
+ # Sources: https://docs.z.ai/guides/llm/glm-5.3
413
+ # https://docs.z.ai/guides/llm/glm-5-turbo
414
+ # https://docs.z.ai/guides/llm/glm-4.7
415
+ (
416
+ "glm-5.3",
417
+ _C(
418
+ mode=ALWAYS_ON,
419
+ wire=WIRE_REASONING_EFFORT,
420
+ values=("low", "high", "max"),
421
+ default="max",
422
+ off=OFF_IMPOSSIBLE,
423
+ emits_flat_reasoning_effort=True,
424
+ notes=(
425
+ "Coding Plan surface: disabling thinking is coerced to low rather than "
426
+ "turning reasoning off; aliases are normalized server-side"
427
+ ),
428
+ ),
429
+ ),
430
+ (
431
+ "glm-5-turbo",
432
+ _C(
433
+ mode=ALWAYS_ON,
434
+ wire=WIRE_REASONING_EFFORT,
435
+ values=("low", "high", "max"),
436
+ default="max",
437
+ off=OFF_IMPOSSIBLE,
438
+ emits_flat_reasoning_effort=True,
439
+ notes="Coding Plan reasoning floor follows the shared effort normalizer",
440
+ ),
441
+ ),
442
+ (
443
+ "glm-4.7",
444
+ _C(
445
+ mode=ALWAYS_ON,
446
+ wire=WIRE_REASONING_EFFORT,
447
+ values=("low", "high", "max"),
448
+ default="max",
449
+ off=OFF_IMPOSSIBLE,
450
+ emits_flat_reasoning_effort=True,
451
+ notes="Coding Plan reasoning floor follows the shared effort normalizer",
452
+ ),
453
+ ),
454
+ ),
455
+ "moonshot": (
456
+ # Platform surface (api.moonshot.ai / .cn). The kimi-code membership
457
+ # surface has different contracts — provider_key "kimi-code" below.
458
+ # Source: https://www.kimi.com/help/kimi-api/api-model-selection
459
+ (
460
+ "kimi-k2.7-code",
461
+ _C(
462
+ mode=ALWAYS_ON,
463
+ wire=WIRE_THINKING_TYPE,
464
+ values=("enabled",),
465
+ off=OFF_IMPOSSIBLE,
466
+ notes="thinking:{'type':'disabled'} = 400 'invalid thinking'; "
467
+ "temperature!=1.0 or top_p!=0.95 = 400",
468
+ ),
469
+ ),
470
+ (
471
+ "kimi-k3",
472
+ _C(
473
+ mode=ALWAYS_ON,
474
+ wire=WIRE_REASONING_EFFORT,
475
+ values=("low", "high", "max"),
476
+ default="max",
477
+ off=OFF_IMPOSSIBLE,
478
+ emits_flat_reasoning_effort=True,
479
+ notes="uses reasoning_effort, not thinking; do not send the K2.x param",
480
+ ),
481
+ ),
482
+ (
483
+ "kimi-k2.6",
484
+ _C(
485
+ mode=OPTIONAL,
486
+ wire=WIRE_THINKING_TYPE,
487
+ off=OFF_EXPLICIT,
488
+ notes="the only toggleable model on this surface; replay reasoning_content "
489
+ "in tool loops",
490
+ ),
491
+ ),
492
+ ),
493
+ "kimi-code": (
494
+ # Source: https://www.kimi.com/code/docs/en/kimi-code/models.html
495
+ (
496
+ "k3",
497
+ _C(
498
+ mode=ALWAYS_ON,
499
+ wire=WIRE_REASONING_EFFORT,
500
+ values=("low", "high", "max"),
501
+ default="high",
502
+ off=OFF_SWAPS_MODEL,
503
+ emits_flat_reasoning_effort=True,
504
+ notes="disabling thinking silently routes the request to K2.6 — surface "
505
+ "the substitution, never treat it as a speed knob",
506
+ ),
507
+ ),
508
+ (
509
+ "kimi-for-coding",
510
+ _C(
511
+ mode=ALWAYS_ON,
512
+ wire=WIRE_THINKING_TYPE,
513
+ values=("enabled",),
514
+ off=OFF_SWAPS_MODEL,
515
+ notes="thinking defaults on; 'off' is a silent K2.6 substitution",
516
+ ),
517
+ ),
518
+ ),
519
+ "minimax": (
520
+ (
521
+ "MiniMax-M3",
522
+ _C(
523
+ mode=OPTIONAL,
524
+ wire=WIRE_THINKING_TYPE,
525
+ values=("enabled", "adaptive", "disabled"),
526
+ off=OFF_EXPLICIT,
527
+ notes="api-reference enum lists only disabled|adaptive (conflict recorded); "
528
+ "anthropic surface defaults thinking DISABLED",
529
+ ),
530
+ ),
531
+ (
532
+ "MiniMax-M2",
533
+ _C(
534
+ mode=NONE,
535
+ wire=WIRE_NONE,
536
+ off=OFF_OMIT,
537
+ notes="M2.x exposes no reasoning control — send nothing",
538
+ ),
539
+ ),
540
+ ),
541
+ "bytedance": (
542
+ (
543
+ "doubao-",
544
+ _C(
545
+ mode=UNKNOWN,
546
+ wire=WIRE_THINKING_TYPE,
547
+ off=OFF_UNKNOWN,
548
+ notes="probe-gated; reasoning_effort 'high' live-observed as 400 "
549
+ "InvalidParameter on the coding surface — send nothing until probed",
550
+ ),
551
+ ),
552
+ ),
553
+ "groq": (
554
+ # Source: https://console.groq.com/docs/reasoning
555
+ (
556
+ "groq/compound",
557
+ _C(
558
+ mode=NONE,
559
+ wire=WIRE_NONE,
560
+ off=OFF_OMIT,
561
+ notes="also rejects client-provided tools entirely",
562
+ ),
563
+ ),
564
+ (
565
+ "qwen/qwen3.6-27b",
566
+ _C(
567
+ mode=OPTIONAL,
568
+ wire=WIRE_REASONING_EFFORT,
569
+ values=("none", "default"),
570
+ off=OFF_EXPLICIT,
571
+ emits_flat_reasoning_effort=True,
572
+ ),
573
+ ),
574
+ (
575
+ "openai/gpt-oss-",
576
+ _C(
577
+ mode=ALWAYS_ON,
578
+ wire=WIRE_REASONING_EFFORT,
579
+ values=("low", "medium", "high"),
580
+ default="medium",
581
+ off=OFF_IMPOSSIBLE,
582
+ emits_flat_reasoning_effort=True,
583
+ notes="reasoning_format 'raw' + tools/JSON = 400; "
584
+ "include_reasoning + reasoning_format together = 400",
585
+ ),
586
+ ),
587
+ ),
588
+ "cerebras": (
589
+ # Source: https://inference-docs.cerebras.ai/api-reference/chat-completions
590
+ (
591
+ "gpt-oss-120b",
592
+ _C(
593
+ mode=ALWAYS_ON,
594
+ wire=WIRE_REASONING_EFFORT,
595
+ values=("low", "medium", "high"),
596
+ default="medium",
597
+ off=OFF_IMPOSSIBLE,
598
+ emits_flat_reasoning_effort=True,
599
+ notes="no 'none'; disable_reasoning dead since 2026-07-21; "
600
+ "tools + response_format together = rejected",
601
+ ),
602
+ ),
603
+ (
604
+ "zai-glm-4.7",
605
+ _C(
606
+ mode=OPTIONAL,
607
+ wire=WIRE_REASONING_EFFORT,
608
+ values=("none",),
609
+ off=OFF_EXPLICIT,
610
+ emits_flat_reasoning_effort=True,
611
+ notes="only 'none' is documented as a control value; low/medium/high are not "
612
+ "documented on this Cerebras model",
613
+ ),
614
+ ),
615
+ (
616
+ "gemma-4-31b",
617
+ _C(
618
+ mode=OPTIONAL,
619
+ wire=WIRE_REASONING_EFFORT,
620
+ values=("none", "low", "medium", "high"),
621
+ default="none",
622
+ off=OFF_EXPLICIT,
623
+ ),
624
+ ),
625
+ ),
626
+ "mistral": (
627
+ (
628
+ "mistral-medium-",
629
+ _C(
630
+ mode=OPTIONAL,
631
+ wire=WIRE_REASONING_EFFORT,
632
+ values=("none", "high"),
633
+ off=OFF_EXPLICIT,
634
+ notes="only 'high'/'none' have documented semantics; content becomes "
635
+ "typed chunks (thinking/text) while reasoning — replay ThinkChunks",
636
+ ),
637
+ ),
638
+ (
639
+ "mistral-small-",
640
+ _C(
641
+ mode=OPTIONAL,
642
+ wire=WIRE_REASONING_EFFORT,
643
+ values=("none", "high"),
644
+ off=OFF_EXPLICIT,
645
+ ),
646
+ ),
647
+ ("mistral-large-", _C(mode=NONE, wire=WIRE_NONE, off=OFF_OMIT)),
648
+ ("codestral-", _C(mode=NONE, wire=WIRE_NONE, off=OFF_OMIT)),
649
+ ("ministral-", _C(mode=NONE, wire=WIRE_NONE, off=OFF_OMIT)),
650
+ ),
651
+ "xai": (
652
+ # The current xAI reasoning guide documents the flat field for its SDK,
653
+ # while the legacy Chat Completions reference does not. Keep emission
654
+ # off until that transport surface is explicitly documented.
655
+ # Sources: https://docs.x.ai/developers/model-capabilities/text/reasoning
656
+ # https://docs.x.ai/developers/model-capabilities/legacy/chat-completions
657
+ (
658
+ "grok-4.6",
659
+ _C(
660
+ mode=ALWAYS_ON,
661
+ wire=WIRE_REASONING_EFFORT,
662
+ values=("low", "medium", "high", "xhigh"),
663
+ default="high",
664
+ off=OFF_IMPOSSIBLE,
665
+ notes="effort + stop/presence_penalty/frequency_penalty = documented error",
666
+ ),
667
+ ),
668
+ (
669
+ "grok-4.5",
670
+ _C(
671
+ mode=ALWAYS_ON,
672
+ wire=WIRE_REASONING_EFFORT,
673
+ values=("low", "medium", "high"),
674
+ default="high",
675
+ off=OFF_IMPOSSIBLE,
676
+ notes="effort + stop/presence_penalty/frequency_penalty = documented error",
677
+ ),
678
+ ),
679
+ (
680
+ "grok-build-0.1",
681
+ _C(mode=ALWAYS_ON, wire=WIRE_REASONING_EFFORT, off=OFF_IMPOSSIBLE),
682
+ ),
683
+ (
684
+ "grok-4.20-0309-reasoning",
685
+ _C(mode=ALWAYS_ON, wire=WIRE_NONE, off=OFF_IMPOSSIBLE, notes="mode-by-slug"),
686
+ ),
687
+ (
688
+ "grok-4.20-0309-non-reasoning",
689
+ _C(mode=NONE, wire=WIRE_NONE, off=OFF_OMIT, notes="mode-by-slug"),
690
+ ),
691
+ (
692
+ "grok-4.3",
693
+ _C(
694
+ mode=OPTIONAL,
695
+ wire=WIRE_REASONING_EFFORT,
696
+ default="low",
697
+ off=OFF_EXPLICIT,
698
+ notes="has an off switch; full value set unpublished — emit only "
699
+ "values confirmed by probe",
700
+ ),
701
+ ),
702
+ ),
703
+ "cohere": (
704
+ # Source: https://docs.cohere.com/v2/docs/compatibility-api
705
+ (
706
+ "command-a-reasoning-",
707
+ _C(
708
+ mode=OPTIONAL,
709
+ wire=WIRE_REASONING_EFFORT,
710
+ values=("none", "high"),
711
+ off=OFF_EXPLICIT,
712
+ emits_flat_reasoning_effort=True,
713
+ notes="medium/low documented not supported on the compat surface; native "
714
+ "thinking param likely does not pass through /compatibility/v1",
715
+ ),
716
+ ),
717
+ ("command-", _C(mode=NONE, wire=WIRE_NONE, off=OFF_OMIT)),
718
+ ),
719
+ "openrouter": (
720
+ (
721
+ "",
722
+ _C(
723
+ mode=UNKNOWN,
724
+ wire=WIRE_REASONING_OBJECT,
725
+ off=OFF_UNKNOWN,
726
+ notes="drive from live /api/v1/models: per-model supported_efforts + "
727
+ "default_effort; effort and max_tokens are mutually exclusive",
728
+ ),
729
+ ),
730
+ ),
731
+ "perplexity": (
732
+ (
733
+ "sonar",
734
+ _C(mode=NONE, wire=WIRE_NONE, off=OFF_OMIT, notes="sonar rejects tools anyway"),
735
+ ),
736
+ ),
737
+ "together": (
738
+ (
739
+ "deepseek-ai/DeepSeek-V4-Pro",
740
+ _C(
741
+ mode=OPTIONAL,
742
+ wire=WIRE_REASONING_EFFORT,
743
+ values=("high", "max"),
744
+ default="high",
745
+ off=OFF_EXPLICIT,
746
+ notes="reasoning_effort controls depth; reasoning:{enabled:false} is the "
747
+ "separate off wire shape; reasoning must be replayed after tool calls",
748
+ ),
749
+ ),
750
+ (
751
+ "moonshotai/Kimi-K2.7-Code",
752
+ _C(
753
+ mode=ALWAYS_ON,
754
+ wire=WIRE_REASONING_ENABLED,
755
+ off=OFF_IMPOSSIBLE,
756
+ notes="thinking cannot be disabled on Together",
757
+ ),
758
+ ),
759
+ (
760
+ "MiniMaxAI/MiniMax-M3",
761
+ _C(
762
+ mode=OPTIONAL,
763
+ wire=WIRE_REASONING_ENABLED,
764
+ off=OFF_EXPLICIT,
765
+ notes="toggleable per Together's model page; exact wire spelling "
766
+ "unconfirmed — probe before emitting",
767
+ ),
768
+ ),
769
+ (
770
+ "deepseek-ai/DeepSeek-V4-Flash",
771
+ UNKNOWN_CONTRACT,
772
+ ),
773
+ (
774
+ "deepseek-ai/",
775
+ _C(
776
+ mode=OPTIONAL,
777
+ wire=WIRE_REASONING_ENABLED,
778
+ off=OFF_EXPLICIT,
779
+ notes="effort low/medium coerce to high",
780
+ ),
781
+ ),
782
+ (
783
+ "openai/gpt-oss-",
784
+ _C(mode=OPTIONAL, wire=WIRE_REASONING_ENABLED, off=OFF_EXPLICIT),
785
+ ),
786
+ (
787
+ "zai-org/",
788
+ _C(
789
+ mode=UNKNOWN,
790
+ wire=WIRE_REASONING_ENABLED,
791
+ off=OFF_UNKNOWN,
792
+ notes="not classified by the capability report — probe before emitting",
793
+ ),
794
+ ),
795
+ ),
796
+ "fireworks": (
797
+ (
798
+ "accounts/fireworks/models/deepseek-v4-",
799
+ _C(
800
+ mode=OPTIONAL,
801
+ wire=WIRE_REASONING_EFFORT,
802
+ values=("none", "high", "max"),
803
+ default="high",
804
+ off=OFF_EXPLICIT,
805
+ emits_flat_reasoning_effort=True,
806
+ notes="low/medium normalize to high and xhigh normalizes to max; expose only "
807
+ "the three distinct wire behaviors",
808
+ ),
809
+ ),
810
+ # The endpoint accepts reasoning_effort, but the current reference does
811
+ # not define values for MiniMax M3. Leave this model probe-gated.
812
+ # Source: https://docs.fireworks.ai/api-reference/post-chatcompletions
813
+ (
814
+ "accounts/fireworks/models/minimax-m3",
815
+ _C(
816
+ mode=OPTIONAL,
817
+ wire=WIRE_REASONING_EFFORT,
818
+ off=OFF_UNKNOWN,
819
+ notes="doc-classified adaptive; thinking + reasoning_effort together = "
820
+ "documented validation error",
821
+ ),
822
+ ),
823
+ (
824
+ "accounts/fireworks/models/",
825
+ _C(
826
+ mode=UNKNOWN,
827
+ wire=WIRE_REASONING_EFFORT,
828
+ off=OFF_UNKNOWN,
829
+ notes="per-model acceptance probe-gated (5 of 6 unknown)",
830
+ ),
831
+ ),
832
+ ),
833
+ }
834
+
835
+
836
+ def reasoning_contract_for(
837
+ provider_key: str | None, model: str | None, *, preset_key: str | None = None
838
+ ) -> ReasoningContract:
839
+ """Resolve the reasoning contract for ``model`` on ``provider_key``.
840
+
841
+ ``preset_key`` takes precedence when it names a distinct surface — e.g. the
842
+ ``kimi-code`` preset carries ``provider_key="moonshot"``, but the membership
843
+ endpoint's contracts differ from the platform's (same vendor, different
844
+ rules; "off" even means different things). First matching prefix rule wins;
845
+ anything unmatched returns :data:`UNKNOWN_CONTRACT` — and per the allowlist
846
+ emission rule, unknown means *send no reasoning parameter at all*.
847
+ """
848
+ name = str(model or "").strip()
849
+ if not name:
850
+ return UNKNOWN_CONTRACT
851
+ folded = name.casefold()
852
+ for key in (preset_key, provider_key):
853
+ rules = _CONTRACTS.get(str(key or "").strip())
854
+ if not rules:
855
+ continue
856
+ for prefix, contract in rules:
857
+ if folded.startswith(prefix.casefold()):
858
+ return contract
859
+ return UNKNOWN_CONTRACT
860
+
861
+
862
+ def reasoning_off_is_safe(provider_key: str | None, model: str | None) -> bool:
863
+ """Whether a reasoning-off toggle can be honoured without error or deceit."""
864
+ return reasoning_contract_for(provider_key, model).toggleable
865
+
866
+
867
+ def reasoning_off_hazard(provider_key: str | None, model: str | None) -> str:
868
+ """Human-readable reason a reasoning-off toggle must not be emitted.
869
+
870
+ Empty string when off is safe. Drives the TUI warning (and the
871
+ ``doctor --live`` assertion mode, per implementation-order step 7).
872
+ """
873
+ contract = reasoning_contract_for(provider_key, model)
874
+ if contract.toggleable:
875
+ return ""
876
+ if contract.off == OFF_SWAPS_MODEL:
877
+ return f"disabling reasoning on {model} silently substitutes a different model"
878
+ if contract.off == OFF_IMPOSSIBLE:
879
+ return f"{model} cannot disable reasoning; lowest effort is the floor"
880
+ if contract.mode == NONE:
881
+ return f"{model} has no reasoning control; nothing to disable"
882
+ return f"reasoning contract for {model} is unknown; emitting a disable flag is unsafe"
883
+
884
+
885
+ __all__ = [
886
+ "ALWAYS_ON",
887
+ "NONE",
888
+ "OPTIONAL",
889
+ "UNKNOWN",
890
+ "UNKNOWN_CONTRACT",
891
+ "OFF_EXPLICIT",
892
+ "OFF_IMPOSSIBLE",
893
+ "OFF_OMIT",
894
+ "OFF_SWAPS_MODEL",
895
+ "OFF_UNKNOWN",
896
+ "ReasoningContract",
897
+ "WIRE_BUDGET_TOKENS",
898
+ "WIRE_ENABLE_THINKING",
899
+ "WIRE_CHAT_TEMPLATE_ENABLE_THINKING",
900
+ "WIRE_NONE",
901
+ "WIRE_REASONING_EFFORT",
902
+ "WIRE_REASONING_ENABLED",
903
+ "WIRE_REASONING_OBJECT",
904
+ "WIRE_THINKING_ADAPTIVE",
905
+ "WIRE_THINKING_LEVEL",
906
+ "WIRE_THINKING_TYPE",
907
+ "reasoning_contract_for",
908
+ "reasoning_labels_allowed_by_contract",
909
+ "reasoning_off_hazard",
910
+ "reasoning_off_is_safe",
911
+ ]