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,525 @@
1
+ from __future__ import annotations
2
+
3
+ import errno
4
+ import logging
5
+ import random
6
+ import socket
7
+ import ssl
8
+ import threading
9
+ import time
10
+ from collections.abc import Callable, Mapping
11
+ from dataclasses import dataclass
12
+ from typing import TypeVar
13
+ from urllib.parse import urlsplit
14
+
15
+ from ..failure_category import is_provider_throttling_error, provider_unavailable_retry_reason
16
+ from ..provider_url import known_provider_key_from_base_url
17
+ from .types import LLMStreamNoProgressError
18
+
19
+ T = TypeVar("T")
20
+
21
+ DEFAULT_PROVIDER_CONCURRENCY_CAPS: dict[str, int] = {"qwen": 4}
22
+ DEFAULT_PROVIDER_RETRY_MAX_RETRIES = 1
23
+ DEFAULT_PROVIDER_RETRY_BASE_DELAY_SECONDS = 10.0
24
+ DEFAULT_PROVIDER_RETRY_MAX_DELAY_SECONDS = 120.0
25
+
26
+ _LOGGER = logging.getLogger(__name__)
27
+ _JITTER_RATIO = 0.25
28
+ _QWEN_CANONICAL_KEY = "qwen"
29
+ _QWEN_PROVIDER_ALIASES = frozenset(
30
+ {
31
+ "aliyun",
32
+ "aliyuncs",
33
+ "dashscope",
34
+ "dashscopechat",
35
+ "qwen",
36
+ "qwen2",
37
+ "qwen25",
38
+ "qwen3",
39
+ "qwen35",
40
+ "qwenmax",
41
+ "tongyi",
42
+ }
43
+ )
44
+ _GENERIC_HOST_PARTS = frozenset(
45
+ {
46
+ "api",
47
+ "ai",
48
+ "chat",
49
+ "coding",
50
+ "compatible",
51
+ "com",
52
+ "cn",
53
+ "intl",
54
+ "mode",
55
+ "v1",
56
+ "v1beta",
57
+ "www",
58
+ }
59
+ )
60
+ _KNOWN_TRANSPORT_PROVIDER_KEYS = frozenset(
61
+ {
62
+ "qwen",
63
+ "openrouter",
64
+ "openai",
65
+ "azure",
66
+ "deepseek",
67
+ "gemini",
68
+ "mistral",
69
+ "nvidia",
70
+ "zai_coding_plan",
71
+ "xai",
72
+ }
73
+ )
74
+ _SEMAPHORE_LOCK = threading.Lock()
75
+ _SEMAPHORES: dict[tuple[str, int], threading.Semaphore] = {}
76
+
77
+
78
+ @dataclass(frozen=True)
79
+ class ProviderRetrySettings:
80
+ max_retries: int = DEFAULT_PROVIDER_RETRY_MAX_RETRIES
81
+ base_delay_seconds: float = DEFAULT_PROVIDER_RETRY_BASE_DELAY_SECONDS
82
+ max_delay_seconds: float = DEFAULT_PROVIDER_RETRY_MAX_DELAY_SECONDS
83
+
84
+
85
+ def canonical_provider_key(provider_key: str | None) -> str | None:
86
+ normalized = _normalize_provider_key(provider_key)
87
+ if not normalized:
88
+ return None
89
+ tokens = _provider_tokens(normalized)
90
+ if tokens & _QWEN_PROVIDER_ALIASES:
91
+ return _QWEN_CANONICAL_KEY
92
+ return normalized
93
+
94
+
95
+ def best_effort_provider_key(*, base_url: str | None, model: str | None) -> str | None:
96
+ url_key = _provider_key_from_base_url(base_url)
97
+ if url_key in _KNOWN_TRANSPORT_PROVIDER_KEYS:
98
+ return url_key
99
+ model_key = _provider_key_from_model(model)
100
+ if model_key is not None:
101
+ return model_key
102
+ return url_key
103
+
104
+
105
+ def resolve_provider_retry_settings(cfg: object | None) -> ProviderRetrySettings:
106
+ return ProviderRetrySettings(
107
+ max_retries=_coerce_non_negative_int(
108
+ getattr(cfg, "provider_retry_max_retries", DEFAULT_PROVIDER_RETRY_MAX_RETRIES),
109
+ default=DEFAULT_PROVIDER_RETRY_MAX_RETRIES,
110
+ ),
111
+ base_delay_seconds=_coerce_positive_float(
112
+ getattr(
113
+ cfg,
114
+ "provider_retry_base_delay_seconds",
115
+ DEFAULT_PROVIDER_RETRY_BASE_DELAY_SECONDS,
116
+ ),
117
+ default=DEFAULT_PROVIDER_RETRY_BASE_DELAY_SECONDS,
118
+ ),
119
+ max_delay_seconds=_coerce_positive_float(
120
+ getattr(
121
+ cfg,
122
+ "provider_retry_max_delay_seconds",
123
+ DEFAULT_PROVIDER_RETRY_MAX_DELAY_SECONDS,
124
+ ),
125
+ default=DEFAULT_PROVIDER_RETRY_MAX_DELAY_SECONDS,
126
+ ),
127
+ )
128
+
129
+
130
+ def resolve_provider_concurrency_cap(
131
+ provider_concurrency_caps: Mapping[str, int | None] | None,
132
+ provider_key: str | None,
133
+ ) -> int | None:
134
+ canonical_key = canonical_provider_key(provider_key)
135
+ if canonical_key is None:
136
+ return None
137
+ caps = provider_concurrency_caps or DEFAULT_PROVIDER_CONCURRENCY_CAPS
138
+ exact_found, exact_cap = _lookup_cap(caps, canonical_key)
139
+ if exact_found:
140
+ return exact_cap
141
+
142
+ alias_caps: list[int] = []
143
+ for raw_key, raw_cap in caps.items():
144
+ if canonical_provider_key(str(raw_key)) != canonical_key:
145
+ continue
146
+ cap = _coerce_cap(raw_cap)
147
+ if cap is not None:
148
+ alias_caps.append(cap)
149
+ if alias_caps:
150
+ return min(alias_caps)
151
+ return None
152
+
153
+
154
+ def run_provider_limited_call(
155
+ *,
156
+ call: Callable[[], T],
157
+ provider_key: str | None,
158
+ provider_concurrency_caps: Mapping[str, int | None] | None = None,
159
+ retry_settings: ProviderRetrySettings | None = None,
160
+ operation: str,
161
+ sleep_fn: Callable[[float], None] | None = None,
162
+ random_fn: Callable[[], float] | None = None,
163
+ on_retry: Callable[[int, str, float], None] | None = None,
164
+ on_retry_event: Callable[[dict[str, object]], None] | None = None,
165
+ retry_deadline_allows: Callable[[float], bool] | None = None,
166
+ retry_wall_clock_cap_seconds: float | None = None,
167
+ clock_fn: Callable[[], float] | None = None,
168
+ ) -> T:
169
+ settings = retry_settings or ProviderRetrySettings()
170
+ canonical_key = canonical_provider_key(provider_key)
171
+ cap = resolve_provider_concurrency_cap(provider_concurrency_caps, canonical_key)
172
+ semaphore = _semaphore_for(canonical_key, cap) if canonical_key and cap else None
173
+ sleep = sleep_fn or time.sleep
174
+ jitter = random_fn or random.random
175
+ clock = clock_fn or time.monotonic
176
+ retry_started_at = clock()
177
+ retries_used = 0
178
+
179
+ while True:
180
+ if semaphore is not None:
181
+ semaphore.acquire()
182
+ try:
183
+ return call()
184
+ except Exception as exc:
185
+ retry_reason = _provider_retry_reason(exc)
186
+ if (
187
+ retry_reason is None
188
+ or _is_provider_call_non_retryable(exc)
189
+ or retries_used >= settings.max_retries
190
+ ):
191
+ raise
192
+ wait_seconds = _retry_delay_seconds(settings, retries_used, jitter)
193
+ if _retry_wall_clock_cap_blocks(
194
+ retry_reason=retry_reason,
195
+ retry_started_at=retry_started_at,
196
+ wait_seconds=wait_seconds,
197
+ cap_seconds=retry_wall_clock_cap_seconds,
198
+ clock=clock,
199
+ ):
200
+ _LOGGER.info(
201
+ "provider_retry_wall_clock_cap_blocked",
202
+ extra={
203
+ "operation": operation,
204
+ "provider_key": canonical_key,
205
+ "retry_attempt": retries_used + 1,
206
+ "wait_seconds": wait_seconds,
207
+ "retry_reason": retry_reason,
208
+ },
209
+ )
210
+ raise
211
+ if retry_deadline_allows is not None and not retry_deadline_allows(wait_seconds):
212
+ _LOGGER.info(
213
+ "provider_retry_deadline_blocked",
214
+ extra={
215
+ "operation": operation,
216
+ "provider_key": canonical_key,
217
+ "retry_attempt": retries_used + 1,
218
+ "wait_seconds": wait_seconds,
219
+ "retry_reason": retry_reason,
220
+ },
221
+ )
222
+ raise
223
+ if on_retry is not None:
224
+ try:
225
+ on_retry(retries_used + 1, retry_reason, wait_seconds)
226
+ except Exception: # noqa: BLE001 - observers must not change retry behavior.
227
+ _LOGGER.debug("provider_retry_observer_failed", exc_info=True)
228
+ if on_retry_event is not None:
229
+ try:
230
+ on_retry_event(
231
+ {
232
+ "provider": canonical_key or "unknown",
233
+ "attempt": retries_used + 2,
234
+ "reason": retry_reason,
235
+ "elapsed_ms": max(
236
+ 0,
237
+ int((clock() - retry_started_at) * 1000),
238
+ ),
239
+ }
240
+ )
241
+ except Exception: # noqa: BLE001 - observers cannot change retries.
242
+ _LOGGER.debug("provider_retry_event_observer_failed", exc_info=True)
243
+ _LOGGER.info(
244
+ f"{retry_reason}, retrying",
245
+ extra={
246
+ "operation": operation,
247
+ "provider_key": canonical_key,
248
+ "retry_attempt": retries_used + 1,
249
+ "wait_seconds": wait_seconds,
250
+ },
251
+ )
252
+ finally:
253
+ if semaphore is not None:
254
+ semaphore.release()
255
+ retries_used += 1
256
+ sleep(wait_seconds)
257
+
258
+
259
+ def _retry_wall_clock_cap_blocks(
260
+ *,
261
+ retry_reason: str,
262
+ retry_started_at: float,
263
+ wait_seconds: float,
264
+ cap_seconds: float | None,
265
+ clock: Callable[[], float],
266
+ ) -> bool:
267
+ if (
268
+ retry_reason not in {"provider_unavailable", "provider_stream_truncated"}
269
+ or cap_seconds is None
270
+ ):
271
+ return False
272
+ try:
273
+ cap = float(cap_seconds)
274
+ except (TypeError, ValueError):
275
+ return False
276
+ if cap <= 0:
277
+ return True
278
+ elapsed = max(0.0, float(clock()) - float(retry_started_at))
279
+ return elapsed + max(0.0, float(wait_seconds)) > cap
280
+
281
+
282
+ def _provider_retry_reason(exc: Exception) -> str | None:
283
+ if isinstance(exc, LLMStreamNoProgressError):
284
+ return "provider_no_progress"
285
+ if is_provider_throttling_error(exc):
286
+ return "provider_throttled"
287
+ transport_reason = _transient_transport_retry_reason(exc)
288
+ if transport_reason is not None:
289
+ return transport_reason
290
+ return provider_unavailable_retry_reason(exc)
291
+
292
+
293
+ _TRANSIENT_TRANSPORT_ERRNOS = {
294
+ value
295
+ for value in (
296
+ getattr(socket, "EAI_AGAIN", None),
297
+ getattr(socket, "EAI_FAIL", None),
298
+ getattr(socket, "EAI_NONAME", None),
299
+ getattr(errno, "ECONNABORTED", None),
300
+ getattr(errno, "ECONNREFUSED", None),
301
+ getattr(errno, "ECONNRESET", None),
302
+ getattr(errno, "EHOSTUNREACH", None),
303
+ getattr(errno, "ENETUNREACH", None),
304
+ getattr(errno, "ETIMEDOUT", None),
305
+ )
306
+ if value is not None
307
+ }
308
+ _TRANSIENT_TRANSPORT_MARKERS = (
309
+ "connection aborted",
310
+ "connection refused",
311
+ "connection reset",
312
+ "getaddrinfo failed",
313
+ "handshake operation timed out",
314
+ "name or service not known",
315
+ "name resolution",
316
+ "network is unreachable",
317
+ "nodename nor servname",
318
+ "temporary failure in name resolution",
319
+ "timed out",
320
+ )
321
+
322
+
323
+ def _transient_transport_retry_reason(exc: BaseException) -> str | None:
324
+ for item in _iter_exception_chain(exc):
325
+ if isinstance(item, (socket.timeout, TimeoutError)):
326
+ return "provider_unavailable"
327
+ if isinstance(item, ssl.SSLError) and _has_transient_transport_marker(item):
328
+ return "provider_unavailable"
329
+ if isinstance(
330
+ item,
331
+ (ConnectionAbortedError, ConnectionRefusedError, ConnectionResetError),
332
+ ):
333
+ return "provider_unavailable"
334
+ if isinstance(item, OSError):
335
+ if getattr(item, "errno", None) in _TRANSIENT_TRANSPORT_ERRNOS:
336
+ return "provider_unavailable"
337
+ if _has_transient_transport_marker(item):
338
+ return "provider_unavailable"
339
+ return None
340
+
341
+
342
+ def _iter_exception_chain(exc: BaseException) -> list[BaseException]:
343
+ chain: list[BaseException] = []
344
+ seen: set[int] = set()
345
+ current: BaseException | None = exc
346
+ while current is not None and id(current) not in seen:
347
+ seen.add(id(current))
348
+ chain.append(current)
349
+ cause = current.__cause__
350
+ context = current.__context__
351
+ current = cause if cause is not None else context
352
+ return chain
353
+
354
+
355
+ def _has_transient_transport_marker(exc: BaseException) -> bool:
356
+ message = str(exc or "").casefold()
357
+ return any(marker in message for marker in _TRANSIENT_TRANSPORT_MARKERS)
358
+
359
+
360
+ PROVIDER_NON_RETRYABLE_ATTR = "_provider_call_non_retryable"
361
+
362
+
363
+ def mark_provider_call_non_retryable(exc: BaseException) -> None:
364
+ """Tag an exception so :func:`run_provider_limited_call` will not retry it.
365
+
366
+ Some failures match the retryable heuristics by message (e.g. an httpx read
367
+ timeout contains "timed out") yet an immediate retry with the same budget is
368
+ pointless — it will fail the same way and only double the dead air. A caller
369
+ that knows a failure is not worth retrying marks it here so the retry loop
370
+ skips it while still retrying genuinely transient failures.
371
+ """
372
+ try:
373
+ setattr(exc, PROVIDER_NON_RETRYABLE_ATTR, True)
374
+ except Exception: # noqa: BLE001 - never let tagging crash the caller
375
+ pass
376
+
377
+
378
+ def _is_provider_call_non_retryable(exc: BaseException) -> bool:
379
+ return bool(getattr(exc, PROVIDER_NON_RETRYABLE_ATTR, False))
380
+
381
+
382
+ def single_retry_is_worthwhile(exc: BaseException) -> bool:
383
+ """Whether one more attempt at the same call could plausibly succeed.
384
+
385
+ Only transport-class failures qualify: timeouts, resets, refused
386
+ connections, and transient SSL/OS errors. A rejection — bad credentials,
387
+ exhausted quota, a malformed request — fails identically on the second
388
+ attempt and only spends another round trip. Failures the provider layer has
389
+ already given up on are never retried again on top of that.
390
+
391
+ Exposed for callers outside this module that run their own bounded retry,
392
+ so the transient classification stays defined in exactly one place.
393
+ """
394
+
395
+ if _is_provider_call_non_retryable(exc):
396
+ return False
397
+ return _transient_transport_retry_reason(exc) is not None
398
+
399
+
400
+ def reset_provider_limit_state_for_tests() -> None:
401
+ with _SEMAPHORE_LOCK:
402
+ _SEMAPHORES.clear()
403
+
404
+
405
+ def qwen_provider_aliases() -> tuple[str, ...]:
406
+ return tuple(sorted(_QWEN_PROVIDER_ALIASES))
407
+
408
+
409
+ def _semaphore_for(provider_key: str, cap: int) -> threading.Semaphore:
410
+ key = (provider_key, cap)
411
+ with _SEMAPHORE_LOCK:
412
+ semaphore = _SEMAPHORES.get(key)
413
+ if semaphore is None:
414
+ semaphore = threading.Semaphore(cap)
415
+ _SEMAPHORES[key] = semaphore
416
+ return semaphore
417
+
418
+
419
+ def _retry_delay_seconds(
420
+ settings: ProviderRetrySettings,
421
+ retry_index: int,
422
+ random_fn: Callable[[], float],
423
+ ) -> float:
424
+ base = max(settings.base_delay_seconds, 0.0)
425
+ max_delay = max(settings.max_delay_seconds, base)
426
+ raw_delay = min(max_delay, base * (2**retry_index))
427
+ jitter_sample = min(max(float(random_fn()), 0.0), 1.0)
428
+ jitter_offset = (jitter_sample - 0.5) * 2.0 * _JITTER_RATIO * raw_delay
429
+ return max(0.0, min(max_delay, raw_delay + jitter_offset))
430
+
431
+
432
+ def _lookup_cap(
433
+ caps: Mapping[str, int | None],
434
+ key: str,
435
+ ) -> tuple[bool, int | None]:
436
+ for raw_key, raw_cap in caps.items():
437
+ if _normalize_provider_key(str(raw_key)) == key:
438
+ return True, _coerce_cap(raw_cap)
439
+ return False, None
440
+
441
+
442
+ def _coerce_cap(raw: int | None) -> int | None:
443
+ try:
444
+ cap = int(raw if raw is not None else 0)
445
+ except (TypeError, ValueError):
446
+ return None
447
+ return cap if cap > 0 else None
448
+
449
+
450
+ def _coerce_non_negative_int(raw: object, *, default: int) -> int:
451
+ try:
452
+ value = int(raw)
453
+ except (TypeError, ValueError):
454
+ return default
455
+ return value if value >= 0 else default
456
+
457
+
458
+ def _coerce_positive_float(raw: object, *, default: float) -> float:
459
+ try:
460
+ value = float(raw)
461
+ except (TypeError, ValueError):
462
+ return default
463
+ return value if value > 0 else default
464
+
465
+
466
+ def _provider_key_from_model(model: str | None) -> str | None:
467
+ raw_model = str(model or "").strip()
468
+ normalized = _normalize_provider_key(raw_model)
469
+ if not normalized:
470
+ return None
471
+ canonical = canonical_provider_key(normalized)
472
+ if canonical is not None and canonical != normalized:
473
+ return canonical
474
+ if "/" in raw_model:
475
+ prefix = raw_model.split("/", 1)[0]
476
+ return canonical_provider_key(prefix) or _normalize_provider_key(prefix)
477
+ return None
478
+
479
+
480
+ def _provider_key_from_base_url(base_url: str | None) -> str | None:
481
+ raw = str(base_url or "").strip()
482
+ if not raw:
483
+ return None
484
+ try:
485
+ parts = urlsplit(raw)
486
+ hostname = (parts.hostname or "").rstrip(".").casefold()
487
+ except ValueError:
488
+ return None
489
+ if not hostname:
490
+ return None
491
+ known_provider = known_provider_key_from_base_url(raw)
492
+ if known_provider:
493
+ return known_provider
494
+ canonical = canonical_provider_key(hostname)
495
+ if canonical is not None and canonical != hostname:
496
+ return canonical
497
+ host_parts = [part for part in hostname.replace("-", ".").split(".") if part]
498
+ for part in host_parts:
499
+ canonical_part = canonical_provider_key(part)
500
+ if canonical_part is not None and canonical_part != part:
501
+ return canonical_part
502
+ for part in reversed(host_parts):
503
+ if part not in _GENERIC_HOST_PARTS:
504
+ return canonical_provider_key(part) or part
505
+ return hostname
506
+
507
+
508
+ def _normalize_provider_key(provider_key: str | None) -> str | None:
509
+ normalized = str(provider_key or "").strip().casefold()
510
+ if not normalized:
511
+ return None
512
+ return "".join(char if char.isalnum() else "_" for char in normalized).strip("_") or None
513
+
514
+
515
+ def _provider_tokens(provider_key: str) -> set[str]:
516
+ ordered_tokens = [
517
+ token
518
+ for token in provider_key.replace("-", "_").replace("/", "_").replace(".", "_").split("_")
519
+ if token
520
+ ]
521
+ tokens = set(ordered_tokens)
522
+ compact = "".join(ordered_tokens)
523
+ if compact:
524
+ tokens.add(compact)
525
+ return tokens