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,450 @@
1
+ from __future__ import annotations
2
+
3
+ import copy
4
+ import hashlib
5
+ import hmac
6
+ import json
7
+ import re
8
+ from collections.abc import Mapping
9
+ from dataclasses import dataclass, replace
10
+ from typing import Any
11
+ from urllib.parse import parse_qsl, urlencode, urlsplit, urlunsplit
12
+
13
+ from ..internal_artifacts import INTERNAL_ARTIFACT_MESSAGE_KEY
14
+
15
+ PROVIDER_METADATA_KEY = "_alysis_provider_metadata"
16
+ ROUTE_IDENTITY_PROVIDER_METADATA_KEY = "_route_identity"
17
+ OPENAI_RESPONSES_PROVIDER_METADATA_KEY = "openai_responses"
18
+ ANTHROPIC_MESSAGES_PROVIDER_METADATA_KEY = "anthropic_messages"
19
+ GEMINI_GENERATE_CONTENT_PROVIDER_METADATA_KEY = "gemini_generate_content"
20
+ GEMINI_INTERACTIONS_PROVIDER_METADATA_KEY = "gemini_interactions"
21
+ MISTRAL_PROVIDER_METADATA_KEY = "mistral"
22
+ MOONSHOT_PROVIDER_METADATA_KEY = "moonshot"
23
+ QWEN_PROVIDER_METADATA_KEY = "qwen"
24
+ MISTRAL_CONTENT_CHUNKS_KEY = "content_chunks"
25
+ TOOL_CALL_PROVIDER_METADATA_KEY = "_tool_calls"
26
+ DEEPSEEK_REASONING_CONTENT_KEY = "reasoning_content"
27
+ DEEPSEEK_PROVIDER_METADATA_KEY = "deepseek"
28
+ OPENROUTER_REASONING_KEY = "reasoning"
29
+ OPENROUTER_REASONING_DETAILS_KEY = "reasoning_details"
30
+
31
+ STATEFUL_PROVIDER_METADATA_KEYS = frozenset(
32
+ {
33
+ OPENAI_RESPONSES_PROVIDER_METADATA_KEY,
34
+ ANTHROPIC_MESSAGES_PROVIDER_METADATA_KEY,
35
+ GEMINI_GENERATE_CONTENT_PROVIDER_METADATA_KEY,
36
+ GEMINI_INTERACTIONS_PROVIDER_METADATA_KEY,
37
+ # DeepSeek requires opaque reasoning_content to survive every
38
+ # tools-enabled assistant turn, even when that turn called no tool.
39
+ DEEPSEEK_PROVIDER_METADATA_KEY,
40
+ # Mistral requires the full assistant content, including opaque
41
+ # ThinkChunk/signature state, to be replayed on later same-route turns.
42
+ MISTRAL_PROVIDER_METADATA_KEY,
43
+ # Kimi requires reasoning_content to be replayed for same-route
44
+ # multi-turn and tool continuations, including ordinary K3 replies.
45
+ MOONSHOT_PROVIDER_METADATA_KEY,
46
+ # Qwen reasoning is retained privately so model contracts that require
47
+ # preserved thinking can replay it on later same-route assistant turns.
48
+ QWEN_PROVIDER_METADATA_KEY,
49
+ }
50
+ )
51
+ _HIERARCHICAL_URL_RE = re.compile(
52
+ r"\b[A-Za-z][A-Za-z0-9+.-]*://\S+",
53
+ )
54
+
55
+
56
+ @dataclass(frozen=True)
57
+ class ProviderRouteIdentity:
58
+ """Immutable identity for provider-owned continuation state.
59
+
60
+ Provider reasoning payloads, signatures, and server-side continuation IDs
61
+ are only valid on the exact route that produced them. Factory-created
62
+ clients include the configured profile and auth adapter; directly-created
63
+ clients use deterministic empty values for those two fields.
64
+ """
65
+
66
+ protocol: str
67
+ base_url: str
68
+ provider_key: str
69
+ model: str
70
+ profile_name: str
71
+ auth_provider: str
72
+ credential_scope: str
73
+ routing_headers: tuple[tuple[str, str], ...]
74
+ routing_fields: tuple[tuple[str, str], ...]
75
+ reasoning_state_adapter: str
76
+ protocol_revision: str
77
+ session_scope: str
78
+ fingerprint: str
79
+
80
+ def as_metadata(self) -> dict[str, Any]:
81
+ # Persist only the versioned digest. Route inputs can contain API-key
82
+ # query parameters or account-routing identifiers and must not enter
83
+ # transcripts/support bundles even in normalized form.
84
+ return {
85
+ "version": 1,
86
+ "fingerprint": self.fingerprint,
87
+ }
88
+
89
+
90
+ def _normalize_route_base_url(value: str) -> str:
91
+ raw = str(value or "").strip().rstrip("/")
92
+ if not raw:
93
+ return ""
94
+ try:
95
+ parsed = urlsplit(raw)
96
+ except ValueError:
97
+ return raw
98
+ if not parsed.scheme or not parsed.netloc:
99
+ return raw
100
+ path = parsed.path.rstrip("/")
101
+ query = urlencode(sorted(parse_qsl(parsed.query, keep_blank_values=True)))
102
+ userinfo, separator, authority = parsed.netloc.rpartition("@")
103
+ normalized_netloc = (
104
+ f"{userinfo}@{authority.casefold()}" if separator else parsed.netloc.casefold()
105
+ )
106
+ return urlunsplit(
107
+ (
108
+ parsed.scheme.casefold(),
109
+ normalized_netloc,
110
+ path,
111
+ query,
112
+ "",
113
+ )
114
+ )
115
+
116
+
117
+ def endpoint_descriptor(value: str) -> dict[str, Any]:
118
+ """Return a persistence-safe identity for a configured provider endpoint.
119
+
120
+ Provider URLs can contain credentials in userinfo, signed path segments, or
121
+ API keys in query parameters. Session logs need an exact-match signal for
122
+ safe resume, but must not retain those route inputs. The versioned digest
123
+ covers the same normalized endpoint material used by route identities while
124
+ the optional host is derived through ``urlsplit().hostname`` so userinfo,
125
+ ports, paths, queries, and fragments cannot enter persisted metadata.
126
+ """
127
+
128
+ normalized = _normalize_route_base_url(value)
129
+ fingerprint = hashlib.sha256(f"alysis-endpoint:v1:{normalized}".encode()).hexdigest()
130
+ descriptor: dict[str, Any] = {
131
+ "version": 1,
132
+ "fingerprint": fingerprint,
133
+ }
134
+ try:
135
+ hostname = str(urlsplit(str(value or "").strip()).hostname or "").rstrip(".")
136
+ safe_host = hostname.encode("idna").decode("ascii").casefold()
137
+ except (UnicodeError, ValueError):
138
+ safe_host = ""
139
+ if safe_host and not any(char.isspace() or char in "/@?#" for char in safe_host):
140
+ descriptor["host"] = safe_host
141
+ return descriptor
142
+
143
+
144
+ def endpoint_descriptor_matches(value: str, descriptor: Any) -> bool:
145
+ """Return whether ``value`` exactly matches a persisted endpoint digest."""
146
+
147
+ if not isinstance(descriptor, dict) or descriptor.get("version") != 1:
148
+ return False
149
+ fingerprint = descriptor.get("fingerprint")
150
+ if not isinstance(fingerprint, str) or len(fingerprint) != 64:
151
+ return False
152
+ try:
153
+ int(fingerprint, 16)
154
+ except ValueError:
155
+ return False
156
+ expected = endpoint_descriptor(value)["fingerprint"]
157
+ return hmac.compare_digest(fingerprint.casefold(), expected)
158
+
159
+
160
+ def endpoint_label(value: str) -> str:
161
+ """Return a human-readable endpoint label that is safe for logs/errors."""
162
+
163
+ descriptor = endpoint_descriptor(value)
164
+ fingerprint = str(descriptor["fingerprint"])
165
+ host = str(descriptor.get("host") or "").strip()
166
+ if host:
167
+ authority = f"[{host}]" if ":" in host else host
168
+ try:
169
+ port = urlsplit(str(value or "").strip()).port
170
+ except ValueError:
171
+ port = None
172
+ if port is not None:
173
+ authority = f"{authority}:{port}"
174
+ return f"{authority} (endpoint {fingerprint[:12]})"
175
+ return f"endpoint {fingerprint[:12]}"
176
+
177
+
178
+ def sanitize_urls_for_output(value: Any) -> str:
179
+ """Remove URL credentials and route material from persisted/displayed text.
180
+
181
+ Provider/network exceptions can embed the full request URL. Replace every
182
+ hierarchical URL with the same safe host+digest label used by transport
183
+ errors, retaining enough correlation for diagnostics without userinfo,
184
+ path, query, or fragment data.
185
+ """
186
+
187
+ text = str(value or "")
188
+
189
+ def replace_url(match: re.Match[str]) -> str:
190
+ raw = match.group(0)
191
+ trailing = ""
192
+ while raw and raw[-1] in ".,;:!?)]":
193
+ trailing = raw[-1] + trailing
194
+ raw = raw[:-1]
195
+ return endpoint_label(raw) + trailing
196
+
197
+ return _HIERARCHICAL_URL_RE.sub(replace_url, text)
198
+
199
+
200
+ def build_provider_route_identity(
201
+ *,
202
+ protocol: str,
203
+ base_url: str,
204
+ provider_key: str | None,
205
+ model: str,
206
+ profile_name: str | None = None,
207
+ auth_provider: str | None = None,
208
+ credential_scope: str | None = None,
209
+ routing_headers: Mapping[str, Any] | None = None,
210
+ routing_fields: dict[str, str] | None = None,
211
+ reasoning_state_adapter: str | None = None,
212
+ protocol_revision: str | None = None,
213
+ session_scope: str | None = None,
214
+ ) -> ProviderRouteIdentity:
215
+ normalized_routing_headers = canonicalize_extra_headers(routing_headers)
216
+ canonical_routing_headers = tuple(
217
+ sorted(
218
+ (
219
+ str(name).strip().casefold(),
220
+ credential_scope_fingerprint(value),
221
+ )
222
+ for name, value in normalized_routing_headers.items()
223
+ )
224
+ )
225
+ canonical_routing_fields = tuple(
226
+ sorted(
227
+ (
228
+ str(name).strip(),
229
+ credential_scope_fingerprint(value),
230
+ )
231
+ for name, value in (routing_fields or {}).items()
232
+ if str(name).strip() and str(value or "").strip()
233
+ )
234
+ )
235
+ fields = {
236
+ "protocol": str(protocol or "").strip().casefold(),
237
+ "base_url": _normalize_route_base_url(base_url),
238
+ "provider_key": str(provider_key or "").strip().casefold(),
239
+ "model": str(model or "").strip(),
240
+ "profile_name": str(profile_name or "").strip(),
241
+ "auth_provider": str(auth_provider or "").strip().casefold(),
242
+ "credential_scope": str(credential_scope or "").strip().casefold(),
243
+ "routing_headers": canonical_routing_headers,
244
+ "routing_fields": canonical_routing_fields,
245
+ "reasoning_state_adapter": str(reasoning_state_adapter or "").strip().casefold(),
246
+ "protocol_revision": str(protocol_revision or "").strip(),
247
+ "session_scope": str(session_scope or "").strip().casefold(),
248
+ }
249
+ canonical = json.dumps(fields, ensure_ascii=True, separators=(",", ":"), sort_keys=True)
250
+ fingerprint = hashlib.sha256(canonical.encode("utf-8")).hexdigest()
251
+ return ProviderRouteIdentity(**fields, fingerprint=fingerprint)
252
+
253
+
254
+ def credential_scope_fingerprint(value: Any) -> str:
255
+ material = str(value or "").strip()
256
+ if not material:
257
+ return ""
258
+ return hashlib.sha256(f"alysis-route-scope:v1:{material}".encode()).hexdigest()
259
+
260
+
261
+ def canonicalize_extra_headers(headers: Mapping[str, Any] | None) -> dict[str, str]:
262
+ """Normalize configured headers once for both transport and route identity.
263
+
264
+ HTTP header names are case-insensitive, so case-variant duplicates are
265
+ ambiguous and rejected. Optional whitespace is removed from names and
266
+ values before either sending or hashing, preventing wire/fingerprint drift.
267
+ """
268
+
269
+ canonical: dict[str, str] = {}
270
+ for raw_name, raw_value in (headers or {}).items():
271
+ name = str(raw_name or "").strip().casefold()
272
+ value = str(raw_value or "").strip()
273
+ if not name or not value:
274
+ continue
275
+ if name in canonical:
276
+ raise ValueError(f"Duplicate extra header name (case-insensitive): {name}")
277
+ canonical[name] = value
278
+ return canonical
279
+
280
+
281
+ def merge_canonical_headers(*header_maps: Mapping[str, Any] | None) -> dict[str, str]:
282
+ """Case-insensitively merge headers, with each later map overriding earlier maps."""
283
+
284
+ merged: dict[str, str] = {}
285
+ for header_map in header_maps:
286
+ merged.update(canonicalize_extra_headers(header_map))
287
+ return merged
288
+
289
+
290
+ def stamp_provider_metadata_for_route(
291
+ provider_metadata: dict[str, Any] | None,
292
+ route_identity: ProviderRouteIdentity,
293
+ ) -> dict[str, Any]:
294
+ stamped = copy.deepcopy(provider_metadata) if isinstance(provider_metadata, dict) else {}
295
+ stamped[ROUTE_IDENTITY_PROVIDER_METADATA_KEY] = route_identity.as_metadata()
296
+ return stamped
297
+
298
+
299
+ def stamp_response_for_route(response: Any, route_identity: ProviderRouteIdentity) -> Any:
300
+ """Return a response whose provider metadata is bound to ``route_identity``."""
301
+
302
+ provider_metadata = getattr(response, "provider_metadata", None)
303
+ tool_calls = getattr(response, "tool_calls", None)
304
+ has_tool_metadata = isinstance(tool_calls, list) and any(
305
+ isinstance(getattr(tool_call, "provider_metadata", None), dict)
306
+ and bool(getattr(tool_call, "provider_metadata", None))
307
+ for tool_call in tool_calls
308
+ )
309
+ if not (isinstance(provider_metadata, dict) and provider_metadata) and not has_tool_metadata:
310
+ return response
311
+ return replace(
312
+ response,
313
+ provider_metadata=stamp_provider_metadata_for_route(
314
+ provider_metadata,
315
+ route_identity,
316
+ ),
317
+ )
318
+
319
+
320
+ def provider_metadata_matches_route(
321
+ provider_metadata: Any,
322
+ route_identity: ProviderRouteIdentity,
323
+ ) -> bool:
324
+ if not isinstance(provider_metadata, dict):
325
+ return False
326
+ stamped = provider_metadata.get(ROUTE_IDENTITY_PROVIDER_METADATA_KEY)
327
+ return isinstance(stamped, dict) and stamped == route_identity.as_metadata()
328
+
329
+
330
+ def gate_messages_for_provider_route(
331
+ messages: list[dict[str, Any]],
332
+ route_identity: ProviderRouteIdentity,
333
+ ) -> list[dict[str, Any]]:
334
+ """Keep provider state only when its exact route stamp matches.
335
+
336
+ Legacy unstamped state and mismatched state fail closed. Public message
337
+ content and tool calls remain available for ordinary full-history replay.
338
+ """
339
+
340
+ gated: list[dict[str, Any]] = []
341
+ for message in messages:
342
+ if not isinstance(message, dict):
343
+ continue
344
+ metadata = message.get(PROVIDER_METADATA_KEY)
345
+ copied = strip_provider_metadata_from_message(copy.deepcopy(message))
346
+ if provider_metadata_matches_route(metadata, route_identity):
347
+ copied[PROVIDER_METADATA_KEY] = copy.deepcopy(metadata)
348
+ gated.append(copied)
349
+ return gated
350
+
351
+
352
+ def strip_provider_metadata_from_messages(
353
+ messages: list[dict[str, Any]],
354
+ ) -> list[dict[str, Any]]:
355
+ return [
356
+ strip_provider_metadata_from_message(copy.deepcopy(message))
357
+ for message in messages
358
+ if isinstance(message, dict)
359
+ ]
360
+
361
+
362
+ def merge_provider_metadata(*items: dict[str, Any] | None) -> dict[str, Any] | None:
363
+ merged: dict[str, Any] = {}
364
+ for item in items:
365
+ if not isinstance(item, dict):
366
+ continue
367
+ for key, value in item.items():
368
+ if isinstance(value, dict) and value:
369
+ merged[str(key)] = copy.deepcopy(value)
370
+ return merged or None
371
+
372
+
373
+ def tool_call_metadata_entries(response: Any) -> list[dict[str, Any]]:
374
+ tool_calls = getattr(response, "tool_calls", None)
375
+ if not isinstance(tool_calls, list):
376
+ return []
377
+ entries: list[dict[str, Any]] = []
378
+ for index, tool_call in enumerate(tool_calls):
379
+ metadata = getattr(tool_call, "provider_metadata", None)
380
+ merged = merge_provider_metadata(metadata)
381
+ if not merged:
382
+ continue
383
+ entry: dict[str, Any] = {
384
+ "index": index,
385
+ "metadata": merged,
386
+ }
387
+ tool_call_id = str(getattr(tool_call, "id", "") or "")
388
+ if tool_call_id:
389
+ entry["id"] = tool_call_id
390
+ entries.append(entry)
391
+ return entries
392
+
393
+
394
+ def strip_provider_metadata_from_message(message: dict[str, Any]) -> dict[str, Any]:
395
+ copied = dict(message)
396
+ copied.pop(PROVIDER_METADATA_KEY, None)
397
+ copied.pop(INTERNAL_ARTIFACT_MESSAGE_KEY, None)
398
+ copied.pop(DEEPSEEK_REASONING_CONTENT_KEY, None)
399
+ copied.pop(OPENROUTER_REASONING_KEY, None)
400
+ copied.pop(OPENROUTER_REASONING_DETAILS_KEY, None)
401
+ return copied
402
+
403
+
404
+ def attach_provider_metadata_to_assistant_message(
405
+ message: dict[str, Any],
406
+ response: Any,
407
+ ) -> dict[str, Any]:
408
+ if str(message.get("role") or "") != "assistant":
409
+ return message
410
+ message_metadata = merge_provider_metadata(getattr(response, "provider_metadata", None))
411
+ if not message.get("tool_calls") and not (
412
+ isinstance(message_metadata, dict)
413
+ and any(key in message_metadata for key in STATEFUL_PROVIDER_METADATA_KEYS)
414
+ ):
415
+ return message
416
+ tool_call_metadata = tool_call_metadata_entries(response)
417
+ if not message_metadata and not tool_call_metadata:
418
+ return message
419
+ copied = dict(message)
420
+ merged = dict(message_metadata or {})
421
+ if tool_call_metadata:
422
+ merged[TOOL_CALL_PROVIDER_METADATA_KEY] = tool_call_metadata
423
+ copied[PROVIDER_METADATA_KEY] = merged
424
+ return copied
425
+
426
+
427
+ def assistant_message_from_response(
428
+ response: Any,
429
+ *,
430
+ content: str | None = None,
431
+ ) -> dict[str, Any]:
432
+ message_content = getattr(response, "content", "") if content is None else content
433
+ message: dict[str, Any] = {
434
+ "role": "assistant",
435
+ "content": str(message_content or ""),
436
+ }
437
+ tool_calls = getattr(response, "tool_calls", None)
438
+ if isinstance(tool_calls, list) and tool_calls:
439
+ message["tool_calls"] = [
440
+ {
441
+ "id": str(getattr(tool_call, "id", "") or ""),
442
+ "type": "function",
443
+ "function": {
444
+ "name": str(getattr(tool_call, "name", "") or ""),
445
+ "arguments": json.dumps(getattr(tool_call, "arguments", {}) or {}),
446
+ },
447
+ }
448
+ for tool_call in tool_calls
449
+ ]
450
+ return attach_provider_metadata_to_assistant_message(message, response)