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,456 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import logging
5
+ import time
6
+ from dataclasses import asdict, dataclass
7
+ from typing import Any, Literal
8
+
9
+ from ..atomic_io import atomic_write_json
10
+ from ..config import (
11
+ AppConfig,
12
+ resolve_llm_enable_thinking,
13
+ resolve_model_access_api_key,
14
+ resolve_prompt_cache_key,
15
+ resolve_prompt_cache_retention,
16
+ resolve_role_temperature,
17
+ )
18
+ from ..forge import RunPaths, now_iso
19
+ from ..llm.factory import make_llm_client
20
+ from ..llm.types import LLMError, LLMResponse
21
+ from ..model_registry import ModelRegistry
22
+ from ..model_router import resolve_model_for_role
23
+ from ..token_budget import estimate_tokens
24
+ from .models import AssetError
25
+ from .worker_mirror import MirroredAssetEntry, TaskAssetMirror
26
+
27
+ LOGGER = logging.getLogger(__name__)
28
+
29
+ InclusionMode = Literal["full_inline", "focused_extract", "reference_only"]
30
+ _ALLOCATOR_TOOL_NAME = "record_task_asset_allocation"
31
+ _ALLOCATOR_TOOL_CHOICE = {
32
+ "type": "function",
33
+ "function": {"name": _ALLOCATOR_TOOL_NAME},
34
+ }
35
+ _ALLOCATOR_TOOL: dict[str, Any] = {
36
+ "type": "function",
37
+ "function": {
38
+ "name": _ALLOCATOR_TOOL_NAME,
39
+ "description": "Persist per-task asset inclusion decisions.",
40
+ "strict": True,
41
+ "parameters": {
42
+ "type": "object",
43
+ "additionalProperties": False,
44
+ "required": ["decisions"],
45
+ "properties": {
46
+ "decisions": {
47
+ "type": "array",
48
+ "items": {
49
+ "type": "object",
50
+ "additionalProperties": False,
51
+ "required": ["asset_id", "mode", "focus", "reason"],
52
+ "properties": {
53
+ "asset_id": {"type": "string"},
54
+ "mode": {
55
+ "type": "string",
56
+ "enum": ["full_inline", "focused_extract", "reference_only"],
57
+ },
58
+ "focus": {
59
+ "anyOf": [{"type": "string"}, {"type": "null"}],
60
+ },
61
+ "reason": {"type": "string"},
62
+ },
63
+ },
64
+ }
65
+ },
66
+ },
67
+ },
68
+ }
69
+
70
+
71
+ @dataclass(frozen=True)
72
+ class AssetInclusionDecision:
73
+ asset_id: str
74
+ mode: InclusionMode
75
+ focus: str | None
76
+ reason: str
77
+
78
+
79
+ @dataclass(frozen=True)
80
+ class TaskAssetAllocation:
81
+ task_id: str
82
+ decisions: list[AssetInclusionDecision]
83
+ elapsed_ms: int
84
+ model: str | None
85
+ tokens_used: dict[str, int]
86
+ fallback_used: bool
87
+ fallback_reason: str | None
88
+
89
+
90
+ def allocate_task_assets(
91
+ *,
92
+ task: dict[str, Any],
93
+ plan: dict[str, Any],
94
+ mirror: TaskAssetMirror,
95
+ cfg: AppConfig,
96
+ model_registry: ModelRegistry,
97
+ instruction_token_budget: int,
98
+ api_key: str | None = None,
99
+ ) -> TaskAssetAllocation:
100
+ _ = plan
101
+ started = time.monotonic()
102
+ task_id = str(task.get("id") or "").strip()
103
+ primary = list(mirror.primary)
104
+ if not primary:
105
+ return TaskAssetAllocation(
106
+ task_id=task_id,
107
+ decisions=[],
108
+ elapsed_ms=0,
109
+ model=None,
110
+ tokens_used={},
111
+ fallback_used=False,
112
+ fallback_reason=None,
113
+ )
114
+ role = str(cfg.assets.worker.allocator_role or "comprehension").strip().lower()
115
+ model = resolve_model_for_role(cfg=cfg, role=role, plan=plan)
116
+ try:
117
+ response = _call_allocator(
118
+ task=task,
119
+ primary=primary,
120
+ cfg=cfg,
121
+ role=role,
122
+ model=model,
123
+ api_key=api_key,
124
+ instruction_token_budget=instruction_token_budget,
125
+ retry_note=None,
126
+ )
127
+ decisions = _parse_decisions(
128
+ response, primary_asset_ids=[entry.asset_id for entry in primary]
129
+ )
130
+ except Exception as first_error: # noqa: BLE001
131
+ try:
132
+ response = _call_allocator(
133
+ task=task,
134
+ primary=primary,
135
+ cfg=cfg,
136
+ role=role,
137
+ model=model,
138
+ api_key=api_key,
139
+ instruction_token_budget=instruction_token_budget,
140
+ retry_note=str(first_error),
141
+ )
142
+ decisions = _parse_decisions(
143
+ response,
144
+ primary_asset_ids=[entry.asset_id for entry in primary],
145
+ )
146
+ except Exception as second_error: # noqa: BLE001
147
+ allocation = _fallback_allocation(
148
+ task_id=task_id,
149
+ primary=primary,
150
+ instruction_token_budget=instruction_token_budget,
151
+ reason=str(second_error) or str(first_error),
152
+ started=started,
153
+ )
154
+ _log_allocation(allocation)
155
+ return allocation
156
+ allocation = TaskAssetAllocation(
157
+ task_id=task_id,
158
+ decisions=decisions,
159
+ elapsed_ms=_elapsed_ms(started),
160
+ model=model,
161
+ tokens_used=_usage_payload(response),
162
+ fallback_used=False,
163
+ fallback_reason=None,
164
+ )
165
+ _log_allocation(allocation)
166
+ return allocation
167
+
168
+
169
+ def write_task_asset_allocation(
170
+ *,
171
+ run_paths: RunPaths,
172
+ allocation: TaskAssetAllocation,
173
+ started_at: str | None = None,
174
+ ) -> None:
175
+ path = run_paths.execution_asset_briefings_dir / f"{_safe_task(allocation.task_id)}.json"
176
+ try:
177
+ payload = json.loads(path.read_text(encoding="utf-8"))
178
+ except FileNotFoundError:
179
+ payload = {"schema_version": 1, "task_id": allocation.task_id, "attempts": []}
180
+ if not isinstance(payload, dict):
181
+ payload = {"schema_version": 1, "task_id": allocation.task_id, "attempts": []}
182
+ attempts = payload.setdefault("attempts", [])
183
+ if not isinstance(attempts, list):
184
+ attempts = []
185
+ payload["attempts"] = attempts
186
+ attempts.append(
187
+ {
188
+ "started_at": started_at or now_iso(),
189
+ "model": allocation.model,
190
+ "tokens_used": dict(allocation.tokens_used),
191
+ "elapsed_ms": allocation.elapsed_ms,
192
+ "fallback_used": allocation.fallback_used,
193
+ "fallback_reason": allocation.fallback_reason,
194
+ "decisions": [asdict(decision) for decision in allocation.decisions],
195
+ }
196
+ )
197
+ payload["schema_version"] = 1
198
+ payload["task_id"] = allocation.task_id
199
+ atomic_write_json(path, payload)
200
+
201
+
202
+ def _call_allocator(
203
+ *,
204
+ task: dict[str, Any],
205
+ primary: list[MirroredAssetEntry],
206
+ cfg: AppConfig,
207
+ role: str,
208
+ model: str,
209
+ api_key: str | None,
210
+ instruction_token_budget: int,
211
+ retry_note: str | None,
212
+ ) -> LLMResponse:
213
+ client = make_llm_client(
214
+ cfg=cfg,
215
+ api_key=resolve_model_access_api_key(cfg, override=api_key),
216
+ model=model,
217
+ timeout_s=float(cfg.assets.worker.allocator_timeout_seconds),
218
+ temperature=resolve_role_temperature(cfg, role=role),
219
+ prompt_cache_key=resolve_prompt_cache_key(cfg),
220
+ prompt_cache_retention=resolve_prompt_cache_retention(cfg),
221
+ enable_thinking=resolve_llm_enable_thinking(cfg),
222
+ )
223
+ prompt = _allocation_prompt(
224
+ task=task,
225
+ primary=primary,
226
+ instruction_token_budget=instruction_token_budget,
227
+ retry_note=retry_note,
228
+ )
229
+ try:
230
+ return client.chat(
231
+ messages=[
232
+ {"role": "system", "content": "Return the allocation tool call only."},
233
+ {"role": "user", "content": prompt},
234
+ ],
235
+ temperature=resolve_role_temperature(cfg, role=role),
236
+ tools=[_ALLOCATOR_TOOL],
237
+ tool_choice=_ALLOCATOR_TOOL_CHOICE,
238
+ )
239
+ except LLMError as exc:
240
+ if not _structured_tool_call_unsupported(exc):
241
+ raise
242
+ LOGGER.warning(
243
+ "Structured tool-call asset allocation was rejected by the provider; retrying with JSON mode."
244
+ )
245
+ return client.chat(
246
+ messages=[
247
+ {"role": "system", "content": "Return a JSON object with a decisions array only."},
248
+ {"role": "user", "content": prompt},
249
+ ],
250
+ temperature=resolve_role_temperature(cfg, role=role),
251
+ response_format={"type": "json_object"},
252
+ )
253
+
254
+
255
+ def _allocation_prompt(
256
+ *,
257
+ task: dict[str, Any],
258
+ primary: list[MirroredAssetEntry],
259
+ instruction_token_budget: int,
260
+ retry_note: str | None,
261
+ ) -> str:
262
+ lines = [
263
+ "Decide how each primary asset should be included in the worker task prompt.",
264
+ "Modes:",
265
+ "- full_inline: include full extracted text or image comprehension data.",
266
+ "- focused_extract: include an LLM-extracted subset focused on the task.",
267
+ "- reference_only: include only summary and let the worker call asset_read/asset_load.",
268
+ f"Instruction token budget: {max(0, int(instruction_token_budget))}",
269
+ "",
270
+ "Task:",
271
+ f"- id: {str(task.get('id') or '').strip()}",
272
+ f"- title: {str(task.get('title') or '').strip()}",
273
+ f"- description: {str(task.get('description') or '').strip()}",
274
+ "",
275
+ "Primary assets:",
276
+ ]
277
+ for entry in primary:
278
+ summary = (
279
+ entry.comprehension.data.semantic_summary
280
+ if entry.comprehension is not None
281
+ else "(no comprehension)"
282
+ )
283
+ lines.extend(
284
+ [
285
+ f"- asset_id: {entry.asset_id}",
286
+ f" kind: {entry.kind}",
287
+ f" status: {entry.status}",
288
+ f" size_bytes: {entry.size_bytes}",
289
+ f" rationale: {entry.rationale or ''}",
290
+ f" expected_use: {entry.expected_use or ''}",
291
+ f" summary: {summary}",
292
+ ]
293
+ )
294
+ if retry_note:
295
+ lines.extend(
296
+ [
297
+ "",
298
+ "Previous response was invalid. Return exactly one decision per primary asset id.",
299
+ f"Validation error: {retry_note}",
300
+ ]
301
+ )
302
+ return "\n".join(lines)
303
+
304
+
305
+ def _parse_decisions(
306
+ response: LLMResponse,
307
+ *,
308
+ primary_asset_ids: list[str],
309
+ ) -> list[AssetInclusionDecision]:
310
+ payload: dict[str, Any] | None = None
311
+ for tool_call in response.tool_calls or []:
312
+ if tool_call.name == _ALLOCATOR_TOOL_NAME:
313
+ payload = tool_call.arguments
314
+ break
315
+ if payload is None:
316
+ try:
317
+ payload = json.loads(response.content)
318
+ except json.JSONDecodeError as exc:
319
+ raise AssetError("Allocator did not return a JSON allocation.") from exc
320
+ raw_decisions = payload.get("decisions") if isinstance(payload, dict) else None
321
+ if not isinstance(raw_decisions, list):
322
+ raise AssetError("Allocator decisions must be an array.")
323
+ decisions: list[AssetInclusionDecision] = []
324
+ for index, raw in enumerate(raw_decisions):
325
+ if not isinstance(raw, dict):
326
+ raise AssetError(f"Allocator decision {index} must be an object.")
327
+ asset_id = str(raw.get("asset_id") or "").strip()
328
+ mode = str(raw.get("mode") or "").strip()
329
+ if mode not in {"full_inline", "focused_extract", "reference_only"}:
330
+ raise AssetError(f"Allocator returned invalid mode for {asset_id}.")
331
+ focus = raw.get("focus")
332
+ focus_text = str(focus).strip() if focus is not None else None
333
+ if mode == "focused_extract" and not focus_text:
334
+ raise AssetError(f"Allocator focused_extract for {asset_id} requires focus.")
335
+ if mode != "focused_extract":
336
+ focus_text = None
337
+ reason = str(raw.get("reason") or "").strip()
338
+ if not reason:
339
+ raise AssetError(f"Allocator decision for {asset_id} requires reason.")
340
+ decisions.append(
341
+ AssetInclusionDecision(
342
+ asset_id=asset_id,
343
+ mode=mode, # type: ignore[arg-type]
344
+ focus=focus_text,
345
+ reason=reason,
346
+ )
347
+ )
348
+ expected = set(primary_asset_ids)
349
+ actual = {decision.asset_id for decision in decisions}
350
+ if actual != expected or len(decisions) != len(expected):
351
+ missing = sorted(expected - actual)
352
+ extra = sorted(actual - expected)
353
+ raise AssetError(
354
+ "Allocator decisions must cover exactly the primary assets. "
355
+ f"missing={missing}; extra={extra}"
356
+ )
357
+ return decisions
358
+
359
+
360
+ def _fallback_allocation(
361
+ *,
362
+ task_id: str,
363
+ primary: list[MirroredAssetEntry],
364
+ instruction_token_budget: int,
365
+ reason: str,
366
+ started: float,
367
+ ) -> TaskAssetAllocation:
368
+ threshold = max(0, int(instruction_token_budget * 0.5))
369
+ decisions_by_id: dict[str, AssetInclusionDecision] = {}
370
+ used_tokens = 0
371
+ for entry in sorted(primary, key=lambda item: (max(0, item.size_bytes), item.asset_id)):
372
+ content_tokens = estimate_tokens(_inline_candidate_text(entry))
373
+ if entry.status == "mirrored" and used_tokens + content_tokens <= threshold:
374
+ used_tokens += content_tokens
375
+ mode: InclusionMode = "full_inline"
376
+ decision_reason = "Fallback selected small assets for inline context first."
377
+ else:
378
+ mode = "reference_only"
379
+ decision_reason = "Fallback kept this asset available by reference."
380
+ decisions_by_id[entry.asset_id] = AssetInclusionDecision(
381
+ asset_id=entry.asset_id,
382
+ mode=mode,
383
+ focus=None,
384
+ reason=decision_reason,
385
+ )
386
+ allocation = TaskAssetAllocation(
387
+ task_id=task_id,
388
+ decisions=[decisions_by_id[entry.asset_id] for entry in primary],
389
+ elapsed_ms=_elapsed_ms(started),
390
+ model=None,
391
+ tokens_used={},
392
+ fallback_used=True,
393
+ fallback_reason=reason[:500],
394
+ )
395
+ LOGGER.info("asset_allocator_fallback task_id=%s reason=%s", task_id, "allocator_failed")
396
+ return allocation
397
+
398
+
399
+ def _inline_candidate_text(entry: MirroredAssetEntry) -> str:
400
+ if entry.extracted_text_workspace_path is not None:
401
+ try:
402
+ return entry.extracted_text_workspace_path.read_text(
403
+ encoding="utf-8",
404
+ errors="replace",
405
+ )
406
+ except OSError:
407
+ return ""
408
+ if entry.comprehension is not None:
409
+ return entry.comprehension.data.semantic_summary
410
+ return ""
411
+
412
+
413
+ def _usage_payload(response: LLMResponse) -> dict[str, int]:
414
+ usage = response.usage
415
+ if usage is None:
416
+ return {}
417
+ payload: dict[str, int] = {}
418
+ if usage.prompt_tokens is not None:
419
+ payload["input"] = int(usage.prompt_tokens)
420
+ if usage.completion_tokens is not None:
421
+ payload["output"] = int(usage.completion_tokens)
422
+ return payload
423
+
424
+
425
+ def _log_allocation(allocation: TaskAssetAllocation) -> None:
426
+ counts = {"full_inline": 0, "focused_extract": 0, "reference_only": 0}
427
+ for decision in allocation.decisions:
428
+ counts[decision.mode] += 1
429
+ LOGGER.info(
430
+ "asset_allocator task_id=%s model=%s elapsed_ms=%s primary=%s full_inline=%s "
431
+ "focused=%s reference=%s fallback=%s",
432
+ allocation.task_id,
433
+ allocation.model,
434
+ allocation.elapsed_ms,
435
+ len(allocation.decisions),
436
+ counts["full_inline"],
437
+ counts["focused_extract"],
438
+ counts["reference_only"],
439
+ str(allocation.fallback_used).lower(),
440
+ )
441
+
442
+
443
+ def _elapsed_ms(started: float) -> int:
444
+ return max(0, int((time.monotonic() - started) * 1000))
445
+
446
+
447
+ def _safe_task(task_id: str) -> str:
448
+ safe = "".join(ch if ch.isalnum() or ch in {"-", "_"} else "_" for ch in task_id)
449
+ return safe or "task"
450
+
451
+
452
+ def _structured_tool_call_unsupported(error: LLMError) -> bool:
453
+ text = str(error).casefold()
454
+ if "tool" not in text and "function" not in text:
455
+ return False
456
+ return any(token in text for token in ("unsupported", "unknown", "invalid", "not allowed"))