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,403 @@
1
+ """Bounded handling for a model endpoint that stops answering.
2
+
3
+ Some endpoints, under some contexts, start returning responses with no text and
4
+ no tool calls. Retrying the identical request does not help: the input that
5
+ produced the empty response is re-sent unchanged, so the endpoint produces
6
+ another empty response. Without a wall-clock bound the loop can run for hours
7
+ and then terminate as a failure even though the working tree already holds
8
+ finished work.
9
+
10
+ This module supplies the facts that bound that situation. It is pure and side
11
+ effect free; the clock is injectable so the time-based rules are testable
12
+ without sleeping.
13
+
14
+ * ``response_is_contentless`` — a response carrying neither text nor tool calls.
15
+ A response with tool calls and no text is ordinary and is never counted.
16
+ * ``EmptyResponseStallTracker`` — consecutive contentless responses, how long
17
+ the current contentless streak has lasted, how many recovery cycles the
18
+ session has spent, and the total time attributed to empty-response handling.
19
+ A stall is declared on *either* the count rule or the time rule, so a provider
20
+ that hangs for minutes per empty call is caught without waiting for the count.
21
+ * ``compact_recent_tool_output`` — drops the most recent tool-output block from
22
+ a message list so one recovery attempt re-issues against a different context
23
+ instead of the same one. Roles and ``tool_call_id`` values are preserved, so
24
+ the assistant/tool pairing every provider requires stays intact.
25
+
26
+ Nothing here inspects the task, the repository, or the model: the trigger is the
27
+ observed shape of provider responses.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import time
33
+ from collections.abc import Callable, Iterable, Mapping
34
+ from dataclasses import dataclass, field
35
+ from typing import Any
36
+
37
+ from ..branding import env_get
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # Policy (kill-switch mirrors the route-arbitration / evidence-v2 idiom)
41
+ # ---------------------------------------------------------------------------
42
+
43
+ DEFAULT_STALL_THRESHOLD = 3
44
+ DEFAULT_STALL_SECONDS = 300.0
45
+ DEFAULT_MAX_RECOVERY_CYCLES = 2
46
+ DEFAULT_HANDLING_BUDGET_SECONDS = 600.0
47
+
48
+ # Backoff before a recovery re-issue. Small and bounded: the point is to let a
49
+ # transient provider condition clear, not to wait out an outage.
50
+ _BACKOFF_BASE_SECONDS = 2.0
51
+ _BACKOFF_MAX_SECONDS = 30.0
52
+
53
+ # Characters of each recent tool result kept when the block is compacted. Short
54
+ # results are below this and survive verbatim, so compaction only removes bulk.
55
+ DEFAULT_TOOL_OUTPUT_KEEP_CHARS = 200
56
+
57
+
58
+ def empty_response_stall_guard_enabled(cfg: Any | None) -> bool:
59
+ """Kill-switch for bounded empty-response handling.
60
+
61
+ ``ALYSIS_EMPTY_RESPONSE_STALL`` (off/0/false/no/disabled) wins over the
62
+ config value; default is on. When off, no stall is ever declared and the
63
+ legacy retry-then-terminate behaviour applies.
64
+ """
65
+ env_value = env_get("ALYSIS_EMPTY_RESPONSE_STALL")
66
+ if env_value is not None:
67
+ normalized = str(env_value).strip().lower()
68
+ if normalized in {"off", "0", "false", "no", "disabled"}:
69
+ return False
70
+ if normalized in {"on", "1", "true", "yes", "enabled"}:
71
+ return True
72
+ return bool(getattr(cfg, "empty_response_stall_guard_enabled", True))
73
+
74
+
75
+ def _positive_int(value: Any, fallback: int) -> int:
76
+ try:
77
+ parsed = int(value)
78
+ except (TypeError, ValueError):
79
+ return fallback
80
+ return parsed if parsed > 0 else fallback
81
+
82
+
83
+ def _positive_float(value: Any, fallback: float) -> float:
84
+ try:
85
+ parsed = float(value)
86
+ except (TypeError, ValueError):
87
+ return fallback
88
+ if parsed <= 0 or parsed != parsed or parsed in {float("inf"), float("-inf")}:
89
+ return fallback
90
+ return parsed
91
+
92
+
93
+ @dataclass(frozen=True)
94
+ class EmptyResponseStallPolicy:
95
+ """Resolved thresholds for one session."""
96
+
97
+ enabled: bool = True
98
+ consecutive_threshold: int = DEFAULT_STALL_THRESHOLD
99
+ stall_seconds: float = DEFAULT_STALL_SECONDS
100
+ max_recovery_cycles: int = DEFAULT_MAX_RECOVERY_CYCLES
101
+ handling_budget_seconds: float = DEFAULT_HANDLING_BUDGET_SECONDS
102
+
103
+ def as_payload(self) -> dict[str, Any]:
104
+ return {
105
+ "enabled": self.enabled,
106
+ "consecutive_threshold": self.consecutive_threshold,
107
+ "stall_seconds": self.stall_seconds,
108
+ "max_recovery_cycles": self.max_recovery_cycles,
109
+ "handling_budget_seconds": self.handling_budget_seconds,
110
+ }
111
+
112
+
113
+ def resolve_empty_response_stall_policy(cfg: Any | None) -> EmptyResponseStallPolicy:
114
+ """Build the policy from config, falling back to defaults on bad values."""
115
+ return EmptyResponseStallPolicy(
116
+ enabled=empty_response_stall_guard_enabled(cfg),
117
+ consecutive_threshold=_positive_int(
118
+ getattr(cfg, "empty_response_stall_threshold", None),
119
+ DEFAULT_STALL_THRESHOLD,
120
+ ),
121
+ stall_seconds=_positive_float(
122
+ getattr(cfg, "empty_response_stall_seconds", None),
123
+ DEFAULT_STALL_SECONDS,
124
+ ),
125
+ max_recovery_cycles=_positive_int(
126
+ getattr(cfg, "empty_response_max_recovery_cycles", None),
127
+ DEFAULT_MAX_RECOVERY_CYCLES,
128
+ ),
129
+ handling_budget_seconds=_positive_float(
130
+ getattr(cfg, "empty_response_handling_budget_seconds", None),
131
+ DEFAULT_HANDLING_BUDGET_SECONDS,
132
+ ),
133
+ )
134
+
135
+
136
+ # ---------------------------------------------------------------------------
137
+ # Response classification
138
+ # ---------------------------------------------------------------------------
139
+
140
+
141
+ def response_is_contentless(response: Any) -> bool:
142
+ """True when a model response carries neither text nor a tool call.
143
+
144
+ A response with tool calls but no text is the ordinary shape of a working
145
+ step and is never contentless. Reasoning traces are not content: a response
146
+ that only thought is still one the runtime cannot act on.
147
+ """
148
+ if response is None:
149
+ return True
150
+ if getattr(response, "tool_calls", None):
151
+ return False
152
+ return not str(getattr(response, "content", "") or "").strip()
153
+
154
+
155
+ # ---------------------------------------------------------------------------
156
+ # Stall tracking
157
+ # ---------------------------------------------------------------------------
158
+
159
+
160
+ @dataclass(frozen=True)
161
+ class StallSignal:
162
+ """Outcome of observing one model response."""
163
+
164
+ stalled: bool
165
+ trigger: str
166
+ consecutive_contentless: int
167
+ streak_seconds: float
168
+
169
+ def as_payload(self) -> dict[str, Any]:
170
+ return {
171
+ "stalled": self.stalled,
172
+ "trigger": self.trigger,
173
+ "consecutive_contentless": self.consecutive_contentless,
174
+ "streak_seconds": round(self.streak_seconds, 3),
175
+ }
176
+
177
+
178
+ @dataclass(frozen=True)
179
+ class RecoveryPlan:
180
+ """Whether one more recovery cycle may be spent, and how long to back off."""
181
+
182
+ allowed: bool
183
+ reason: str
184
+ cycle: int
185
+ backoff_seconds: float
186
+
187
+ def as_payload(self) -> dict[str, Any]:
188
+ return {
189
+ "allowed": self.allowed,
190
+ "reason": self.reason,
191
+ "cycle": self.cycle,
192
+ "backoff_seconds": round(self.backoff_seconds, 3),
193
+ }
194
+
195
+
196
+ @dataclass
197
+ class EmptyResponseStallTracker:
198
+ """Per-session state for empty-response detection and its time budget."""
199
+
200
+ policy: EmptyResponseStallPolicy = field(default_factory=EmptyResponseStallPolicy)
201
+ clock: Callable[[], float] = time.monotonic
202
+ consecutive_contentless: int = 0
203
+ recovery_cycles_used: int = 0
204
+ handling_seconds: float = 0.0
205
+ total_contentless_responses: int = 0
206
+ _streak_started_at: float | None = None
207
+ _billed_through: float | None = None
208
+
209
+ def observe(self, *, contentless: bool) -> StallSignal:
210
+ """Record one model response and report whether the turn has stalled."""
211
+ now = float(self.clock())
212
+ if not contentless:
213
+ self._bill(now)
214
+ self.consecutive_contentless = 0
215
+ self._streak_started_at = None
216
+ self._billed_through = None
217
+ return StallSignal(
218
+ stalled=False,
219
+ trigger="",
220
+ consecutive_contentless=0,
221
+ streak_seconds=0.0,
222
+ )
223
+
224
+ self.total_contentless_responses += 1
225
+ if self._streak_started_at is None:
226
+ # Time before the first contentless response belongs to ordinary
227
+ # work (a long tool run, a slow but answering model), so billing
228
+ # for empty-response handling starts here, not earlier.
229
+ self._streak_started_at = now
230
+ self._billed_through = now
231
+ self.consecutive_contentless += 1
232
+ streak_seconds = max(0.0, now - self._streak_started_at)
233
+
234
+ if not self.policy.enabled:
235
+ return StallSignal(
236
+ stalled=False,
237
+ trigger="",
238
+ consecutive_contentless=self.consecutive_contentless,
239
+ streak_seconds=streak_seconds,
240
+ )
241
+ if self.consecutive_contentless >= self.policy.consecutive_threshold:
242
+ trigger = "consecutive_contentless_responses"
243
+ elif streak_seconds >= self.policy.stall_seconds:
244
+ trigger = "contentless_streak_duration"
245
+ else:
246
+ trigger = ""
247
+ return StallSignal(
248
+ stalled=bool(trigger),
249
+ trigger=trigger,
250
+ consecutive_contentless=self.consecutive_contentless,
251
+ streak_seconds=streak_seconds,
252
+ )
253
+
254
+ def plan_recovery(self) -> RecoveryPlan:
255
+ """Decide whether one more compaction-recovery cycle may be spent."""
256
+ self._bill(float(self.clock()))
257
+ cycle = self.recovery_cycles_used + 1
258
+ if not self.policy.enabled:
259
+ return RecoveryPlan(
260
+ allowed=False,
261
+ reason="stall_guard_disabled",
262
+ cycle=cycle,
263
+ backoff_seconds=0.0,
264
+ )
265
+ if self.recovery_cycles_used >= self.policy.max_recovery_cycles:
266
+ return RecoveryPlan(
267
+ allowed=False,
268
+ reason="recovery_cycles_exhausted",
269
+ cycle=cycle,
270
+ backoff_seconds=0.0,
271
+ )
272
+ if self.handling_seconds >= self.policy.handling_budget_seconds:
273
+ return RecoveryPlan(
274
+ allowed=False,
275
+ reason="handling_budget_exhausted",
276
+ cycle=cycle,
277
+ backoff_seconds=0.0,
278
+ )
279
+ return RecoveryPlan(
280
+ allowed=True,
281
+ reason="recovery_available",
282
+ cycle=cycle,
283
+ backoff_seconds=self._backoff_seconds(cycle),
284
+ )
285
+
286
+ def note_recovery_started(self, *, backoff_seconds: float = 0.0) -> None:
287
+ """Charge one recovery cycle (plus the backoff actually slept)."""
288
+ self.recovery_cycles_used += 1
289
+ self.handling_seconds += max(0.0, float(backoff_seconds))
290
+ self.consecutive_contentless = 0
291
+ self._streak_started_at = None
292
+ self._billed_through = None
293
+
294
+ def remaining_budget_seconds(self) -> float:
295
+ return max(0.0, self.policy.handling_budget_seconds - self.handling_seconds)
296
+
297
+ def as_payload(self) -> dict[str, Any]:
298
+ return {
299
+ "consecutive_contentless": self.consecutive_contentless,
300
+ "contentless_responses": self.total_contentless_responses,
301
+ "recovery_cycles_used": self.recovery_cycles_used,
302
+ "max_recovery_cycles": self.policy.max_recovery_cycles,
303
+ "handling_seconds": round(self.handling_seconds, 3),
304
+ "handling_budget_seconds": self.policy.handling_budget_seconds,
305
+ }
306
+
307
+ def _bill(self, now: float) -> None:
308
+ if self._billed_through is None:
309
+ return
310
+ self.handling_seconds += max(0.0, now - self._billed_through)
311
+ self._billed_through = now
312
+
313
+ def _backoff_seconds(self, cycle: int) -> float:
314
+ exponent = max(0, cycle - 1)
315
+ return min(_BACKOFF_MAX_SECONDS, _BACKOFF_BASE_SECONDS * (2**exponent))
316
+
317
+
318
+ # ---------------------------------------------------------------------------
319
+ # Context compaction for the recovery re-issue
320
+ # ---------------------------------------------------------------------------
321
+
322
+
323
+ @dataclass(frozen=True)
324
+ class ToolOutputCompaction:
325
+ """What ``compact_recent_tool_output`` actually removed."""
326
+
327
+ applied: bool
328
+ compacted_messages: int
329
+ removed_characters: int
330
+ block_size: int
331
+
332
+ def as_payload(self) -> dict[str, Any]:
333
+ return {
334
+ "applied": self.applied,
335
+ "compacted_messages": self.compacted_messages,
336
+ "removed_characters": self.removed_characters,
337
+ "block_size": self.block_size,
338
+ }
339
+
340
+
341
+ def _is_tool_message(message: Any) -> bool:
342
+ return isinstance(message, Mapping) and str(message.get("role") or "") == "tool"
343
+
344
+
345
+ def _elision_note(removed: int) -> str:
346
+ return (
347
+ f"\n[Tool output elided by the runtime after repeated empty model responses: "
348
+ f"{removed} characters removed. Re-run the tool if you still need this output.]"
349
+ )
350
+
351
+
352
+ def compact_recent_tool_output(
353
+ messages: Iterable[Mapping[str, Any]],
354
+ *,
355
+ keep_chars: int = DEFAULT_TOOL_OUTPUT_KEEP_CHARS,
356
+ ) -> tuple[list[Any], ToolOutputCompaction]:
357
+ """Drop the most recent tool-output block, keeping the message protocol valid.
358
+
359
+ The block is the trailing run of ``role="tool"`` messages, located by
360
+ scanning back from the end past any controller messages appended after it.
361
+ Each oversized result keeps its first ``keep_chars`` characters plus an
362
+ explicit note that the rest was removed, so the model is never told a
363
+ truncated result is complete. Roles and ``tool_call_id`` values are
364
+ untouched, so every assistant tool call keeps its matching result.
365
+ """
366
+ original = [dict(message) if isinstance(message, Mapping) else message for message in messages]
367
+ keep = max(0, int(keep_chars))
368
+
369
+ last_tool_index: int | None = None
370
+ for index in range(len(original) - 1, -1, -1):
371
+ if _is_tool_message(original[index]):
372
+ last_tool_index = index
373
+ break
374
+ if last_tool_index is None:
375
+ return original, ToolOutputCompaction(
376
+ applied=False,
377
+ compacted_messages=0,
378
+ removed_characters=0,
379
+ block_size=0,
380
+ )
381
+
382
+ start = last_tool_index
383
+ while start - 1 >= 0 and _is_tool_message(original[start - 1]):
384
+ start -= 1
385
+
386
+ compacted_messages = 0
387
+ removed_characters = 0
388
+ for index in range(start, last_tool_index + 1):
389
+ message = original[index]
390
+ content = str(message.get("content") or "")
391
+ if len(content) <= keep:
392
+ continue
393
+ removed = len(content) - keep
394
+ message["content"] = content[:keep] + _elision_note(removed)
395
+ compacted_messages += 1
396
+ removed_characters += removed
397
+
398
+ return original, ToolOutputCompaction(
399
+ applied=compacted_messages > 0,
400
+ compacted_messages=compacted_messages,
401
+ removed_characters=removed_characters,
402
+ block_size=last_tool_index - start + 1,
403
+ )
@@ -0,0 +1,28 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any
4
+
5
+
6
+ class AgentRuntimeError(RuntimeError):
7
+ def __init__(
8
+ self,
9
+ *args: object,
10
+ result_payload: dict[str, Any] | None = None,
11
+ ) -> None:
12
+ self.result_payload = result_payload
13
+ super().__init__(*args)
14
+
15
+
16
+ class ApprovalDeclinedError(AgentRuntimeError):
17
+ def __init__(
18
+ self,
19
+ approval_kind: str,
20
+ *,
21
+ message: str | None = None,
22
+ ) -> None:
23
+ self.approval_kind = str(approval_kind or "approval").strip() or "approval"
24
+ super().__init__(message or f"User declined: {self.approval_kind}")
25
+
26
+
27
+ class SessionWorkdirError(AgentRuntimeError):
28
+ pass