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,11 @@
1
+ from __future__ import annotations
2
+
3
+ from .mcp_sanitize import MAX_MCP_TOOL_DESCRIPTION_CHARS, sanitize_mcp_tool_description
4
+ from .safe_http import SafeHttpError, safe_http_request
5
+
6
+ __all__ = [
7
+ "MAX_MCP_TOOL_DESCRIPTION_CHARS",
8
+ "SafeHttpError",
9
+ "safe_http_request",
10
+ "sanitize_mcp_tool_description",
11
+ ]
@@ -0,0 +1,29 @@
1
+ from __future__ import annotations
2
+
3
+ MAX_MCP_TOOL_DESCRIPTION_CHARS = 2000
4
+
5
+ _STRIPPED_MCP_TOOL_DESCRIPTION_PREFIXES = (
6
+ "ignore previous",
7
+ "system:",
8
+ "user:",
9
+ "assistant:",
10
+ "you are now",
11
+ "new instructions:",
12
+ "<system>",
13
+ "</system>",
14
+ "[inst]",
15
+ "[/inst]",
16
+ )
17
+
18
+
19
+ def sanitize_mcp_tool_description(description: str) -> str:
20
+ kept_lines: list[str] = []
21
+ for line in str(description or "").splitlines():
22
+ normalized = line.lstrip().casefold()
23
+ if any(normalized.startswith(prefix) for prefix in _STRIPPED_MCP_TOOL_DESCRIPTION_PREFIXES):
24
+ continue
25
+ kept_lines.append(line)
26
+ body = "\n".join(kept_lines).strip()
27
+ if len(body) > MAX_MCP_TOOL_DESCRIPTION_CHARS:
28
+ body = body[:MAX_MCP_TOOL_DESCRIPTION_CHARS].rstrip()
29
+ return f"[mcp-tool-description begin]\n{body}\n[mcp-tool-description end]"
@@ -0,0 +1,297 @@
1
+ from __future__ import annotations
2
+
3
+ import ipaddress
4
+ import socket
5
+ from collections.abc import Callable
6
+ from dataclasses import dataclass
7
+ from typing import Any
8
+ from urllib.parse import urljoin, urlsplit, urlunsplit
9
+
10
+ import httpx
11
+
12
+
13
+ class SafeHttpError(Exception):
14
+ """``recoverable=True`` marks failures specific to the requested URL or host
15
+ (a different target can succeed: oversized body, redirect problems, blocked
16
+ or invalid host). The default ``False`` marks environment-level failures
17
+ (network/DNS outage, invalid caller arguments)."""
18
+
19
+ def __init__(self, message: str, *, recoverable: bool = False) -> None:
20
+ super().__init__(message)
21
+ self.recoverable = recoverable
22
+
23
+
24
+ _ALLOWED_SCHEMES = {"http", "https"}
25
+ _REDIRECT_STATUS_CODES = {301, 302, 303, 307, 308}
26
+ _DEFAULT_ACCEPT_ENCODING = "identity"
27
+ Resolver = Callable[[str, int], list[str]]
28
+ _DENIED_NETWORKS = tuple(
29
+ ipaddress.ip_network(network)
30
+ for network in (
31
+ "127.0.0.0/8",
32
+ "::1/128",
33
+ "169.254.0.0/16",
34
+ "fe80::/10",
35
+ "10.0.0.0/8",
36
+ "172.16.0.0/12",
37
+ "192.168.0.0/16",
38
+ "fc00::/7",
39
+ "0.0.0.0/32",
40
+ "::/128",
41
+ "224.0.0.0/4",
42
+ "ff00::/8",
43
+ )
44
+ )
45
+
46
+
47
+ @dataclass(frozen=True)
48
+ class _SafeTarget:
49
+ request_url: str
50
+ connect_url: str
51
+ host_header: str
52
+
53
+
54
+ async def safe_http_request(
55
+ method: str,
56
+ url: str,
57
+ *,
58
+ timeout: float = 30.0,
59
+ max_bytes: int = 10 * 1024 * 1024,
60
+ allow_redirects: bool = True,
61
+ max_redirects: int = 5,
62
+ headers: dict[str, str] | None = None,
63
+ json: Any = None,
64
+ content: bytes | None = None,
65
+ _transport: httpx.AsyncBaseTransport | None = None,
66
+ _resolver: Resolver | None = None,
67
+ ) -> httpx.Response:
68
+ if max_bytes < 0:
69
+ raise SafeHttpError("max_bytes must be non-negative.")
70
+ if max_redirects < 0:
71
+ raise SafeHttpError("max_redirects must be non-negative.")
72
+
73
+ current_url = str(url or "").strip()
74
+ if not current_url:
75
+ raise SafeHttpError("url must be a non-empty string.")
76
+ current_method = str(method or "").strip().upper() or "GET"
77
+ request_headers = _headers_with_default_accept_encoding(headers)
78
+ redirects_seen = 0
79
+ request_json = json
80
+ request_content = content
81
+
82
+ async with httpx.AsyncClient(
83
+ follow_redirects=False,
84
+ timeout=httpx.Timeout(timeout),
85
+ transport=_transport,
86
+ ) as client:
87
+ while True:
88
+ target = _validate_and_resolve_target(
89
+ current_url,
90
+ resolver=_resolver or _resolve_host_addresses,
91
+ use_resolved_connect_url=_transport is None,
92
+ )
93
+ try:
94
+ response = await _single_request(
95
+ client=client,
96
+ method=current_method,
97
+ target=target,
98
+ headers=request_headers,
99
+ json=request_json,
100
+ content=request_content,
101
+ max_bytes=max_bytes,
102
+ )
103
+ except httpx.DecodingError as exc:
104
+ raise SafeHttpError(
105
+ "Response decompression failed: server or proxy returned invalid "
106
+ "Content-Encoding bytes. safe_http_request sends "
107
+ "Accept-Encoding: identity by default; check upstream compression handling.",
108
+ recoverable=True,
109
+ ) from exc
110
+ if not allow_redirects or response.status_code not in _REDIRECT_STATUS_CODES:
111
+ return response
112
+
113
+ location = str(response.headers.get("location") or "").strip()
114
+ if not location:
115
+ raise SafeHttpError(
116
+ f"Redirect response {response.status_code} missing Location header.",
117
+ recoverable=True,
118
+ )
119
+ if redirects_seen >= max_redirects:
120
+ raise SafeHttpError(f"Too many redirects (>{max_redirects}).", recoverable=True)
121
+ redirects_seen += 1
122
+ current_url = urljoin(current_url, location)
123
+ if response.status_code == 303 or (
124
+ response.status_code in {301, 302} and current_method not in {"GET", "HEAD"}
125
+ ):
126
+ current_method = "GET"
127
+ request_json = None
128
+ request_content = None
129
+
130
+
131
+ def _headers_with_default_accept_encoding(headers: dict[str, str] | None) -> dict[str, str]:
132
+ request_headers = dict(headers or {})
133
+ if not any(key.lower() == "accept-encoding" for key in request_headers):
134
+ request_headers["Accept-Encoding"] = _DEFAULT_ACCEPT_ENCODING
135
+ return request_headers
136
+
137
+
138
+ async def _single_request(
139
+ *,
140
+ client: httpx.AsyncClient,
141
+ method: str,
142
+ target: _SafeTarget,
143
+ headers: dict[str, str],
144
+ json: Any,
145
+ content: bytes | None,
146
+ max_bytes: int,
147
+ ) -> httpx.Response:
148
+ outbound_headers = dict(headers)
149
+ outbound_headers["Host"] = target.host_header
150
+ request = httpx.Request(method, target.request_url, headers=outbound_headers)
151
+ body = bytearray()
152
+ response_headers: httpx.Headers | None = None
153
+ status_code = 0
154
+ extensions: dict[str, Any] = {}
155
+ async with client.stream(
156
+ method,
157
+ target.connect_url,
158
+ headers=outbound_headers,
159
+ json=json,
160
+ content=content,
161
+ ) as response:
162
+ status_code = int(response.status_code)
163
+ response_headers = httpx.Headers(response.headers)
164
+ extensions = dict(response.extensions)
165
+ async for chunk in response.aiter_bytes():
166
+ if not chunk:
167
+ continue
168
+ if len(body) + len(chunk) > max_bytes:
169
+ raise SafeHttpError(
170
+ f"Response body exceeded max_bytes={max_bytes}.", recoverable=True
171
+ )
172
+ body.extend(chunk)
173
+ return httpx.Response(
174
+ status_code=status_code,
175
+ headers=response_headers,
176
+ content=bytes(body),
177
+ request=request,
178
+ extensions=extensions,
179
+ )
180
+
181
+
182
+ def _validate_and_resolve_target(
183
+ url: str,
184
+ *,
185
+ resolver: Resolver,
186
+ use_resolved_connect_url: bool,
187
+ ) -> _SafeTarget:
188
+ split = urlsplit(str(url or "").strip())
189
+ scheme = split.scheme.lower()
190
+ if scheme not in _ALLOWED_SCHEMES:
191
+ raise SafeHttpError(
192
+ f"Unsupported URL scheme: {split.scheme or '(missing)'}", recoverable=True
193
+ )
194
+ if split.hostname is None:
195
+ raise SafeHttpError("URL must include a hostname.", recoverable=True)
196
+ if split.username is not None or split.password is not None:
197
+ raise SafeHttpError("Embedded URL credentials are not allowed.", recoverable=True)
198
+ try:
199
+ port = split.port
200
+ except ValueError as exc:
201
+ raise SafeHttpError("URL has an invalid port value.", recoverable=True) from exc
202
+ if port is None:
203
+ port = 443 if scheme == "https" else 80
204
+
205
+ host = split.hostname
206
+ try:
207
+ literal_address = ipaddress.ip_address(host)
208
+ except ValueError:
209
+ literal_address = None
210
+ if literal_address is not None:
211
+ reason = _blocked_ip_reason(literal_address)
212
+ if reason is not None:
213
+ raise SafeHttpError(
214
+ f"Blocked URL host '{host}': resolved to denied address(es): "
215
+ f"{literal_address.compressed} ({reason}).",
216
+ recoverable=True,
217
+ )
218
+
219
+ addresses = resolver(host, port)
220
+ parsed_addresses = [_parse_ip_address(address, host=host) for address in addresses]
221
+ blocked = [
222
+ f"{address.compressed} ({_blocked_ip_reason(address)})"
223
+ for address in parsed_addresses
224
+ if _blocked_ip_reason(address) is not None
225
+ ]
226
+ if blocked:
227
+ raise SafeHttpError(
228
+ f"Blocked URL host '{host}': resolved to denied address(es): {', '.join(blocked)}.",
229
+ recoverable=True,
230
+ )
231
+
232
+ host_header = _host_header(host=host, port=split.port, scheme=scheme)
233
+ request_url = urlunsplit((split.scheme, split.netloc, split.path, split.query, ""))
234
+ if use_resolved_connect_url and scheme != "https":
235
+ connect_ip = parsed_addresses[0].compressed
236
+ connect_host = f"[{connect_ip}]" if ":" in connect_ip else connect_ip
237
+ connect_netloc = _host_header(host=connect_host, port=port, scheme=scheme, force_port=True)
238
+ connect_url = urlunsplit((split.scheme, connect_netloc, split.path, split.query, ""))
239
+ else:
240
+ # HTTPS must keep the original hostname for SNI and certificate validation.
241
+ # The DNS result is still preflight-validated above for SSRF-denied ranges.
242
+ connect_url = request_url
243
+ return _SafeTarget(
244
+ request_url=request_url,
245
+ connect_url=connect_url,
246
+ host_header=host_header,
247
+ )
248
+
249
+
250
+ def _resolve_host_addresses(host: str, port: int) -> list[str]:
251
+ try:
252
+ infos = socket.getaddrinfo(host, port, type=socket.SOCK_STREAM)
253
+ except OSError as exc:
254
+ raise SafeHttpError(f"Failed to resolve host '{host}': {exc}") from exc
255
+ addresses: list[str] = []
256
+ seen: set[str] = set()
257
+ for _family, _socktype, _proto, _canonname, sockaddr in infos:
258
+ if not sockaddr:
259
+ continue
260
+ address = str(sockaddr[0] or "").strip()
261
+ if address and address not in seen:
262
+ seen.add(address)
263
+ addresses.append(address)
264
+ if not addresses:
265
+ raise SafeHttpError(f"Host '{host}' did not resolve to any address.", recoverable=True)
266
+ return addresses
267
+
268
+
269
+ def _parse_ip_address(value: str, *, host: str) -> ipaddress._BaseAddress:
270
+ try:
271
+ return ipaddress.ip_address(value)
272
+ except ValueError as exc:
273
+ raise SafeHttpError(
274
+ f"Resolver returned a non-IP address for host '{host}': {value!r}",
275
+ recoverable=True,
276
+ ) from exc
277
+
278
+
279
+ def _blocked_ip_reason(address: ipaddress._BaseAddress) -> str | None:
280
+ for network in _DENIED_NETWORKS:
281
+ if address.version == network.version and address in network:
282
+ return str(network)
283
+ return None
284
+
285
+
286
+ def _host_header(
287
+ *,
288
+ host: str,
289
+ port: int | None,
290
+ scheme: str,
291
+ force_port: bool = False,
292
+ ) -> str:
293
+ bracketed_host = f"[{host}]" if ":" in host and not host.startswith("[") else host
294
+ default_port = 443 if scheme == "https" else 80
295
+ if port is None or (port == default_port and not force_port):
296
+ return bracketed_host
297
+ return f"{bracketed_host}:{port}"
@@ -0,0 +1,184 @@
1
+ from __future__ import annotations
2
+
3
+ import re
4
+ from dataclasses import dataclass
5
+ from typing import Any
6
+
7
+ _MAX_REPORTED_TAGS = 16
8
+ _MAX_REPORTED_TAG_CHARS = 64
9
+
10
+ _ROLE_TAGS = frozenset({"assistant", "developer", "function", "system", "tool", "user"})
11
+ _HARNESS_TAGS = frozenset(
12
+ {
13
+ "asset_content",
14
+ "asset_summary",
15
+ "asset_text",
16
+ "assistant_summary",
17
+ "available_tool_catalog",
18
+ "environment_context",
19
+ "explicit_skill_context",
20
+ "matched_skill_context",
21
+ "ocr_text",
22
+ "repo_conventions",
23
+ "resume_context",
24
+ "scoped_prompt_prelude",
25
+ "skill_context",
26
+ "skill_instructions",
27
+ "subagent_context",
28
+ "subagent_turn_context",
29
+ "task_brief",
30
+ "tool_call",
31
+ "workspace_binding_context",
32
+ }
33
+ )
34
+
35
+ _TAG_RE = re.compile(
36
+ r"<\s*/?\s*(?P<name>[A-Za-z][A-Za-z0-9:_-]*)\b[^<>]*>",
37
+ flags=re.IGNORECASE,
38
+ )
39
+ _ESCAPED_TAG_RE = re.compile(
40
+ r"&lt;\s*/?\s*(?P<name>[A-Za-z][A-Za-z0-9:_-]*)\b.*?&gt;",
41
+ flags=re.IGNORECASE,
42
+ )
43
+ _BLOCKED_INSTRUCTION_MARKER_RE = re.compile(
44
+ r"\[blocked untrusted subagent "
45
+ r"(?:instruction_override|permission_override|tool_demand)\]",
46
+ flags=re.IGNORECASE,
47
+ )
48
+
49
+ _HIGH_RISK_PATTERNS: tuple[tuple[str, tuple[re.Pattern[str], ...]], ...] = (
50
+ (
51
+ "instruction_override",
52
+ (
53
+ re.compile(
54
+ r"\b(?:ignore|disregard|forget|override|bypass)\s+"
55
+ r"(?:(?:all|any|the|your)\s+)?"
56
+ r"(?:previous|prior|above|earlier|system|developer|parent)\s+"
57
+ r"(?:instructions?|rules?|directives?|prompts?)\b",
58
+ flags=re.IGNORECASE,
59
+ ),
60
+ re.compile(
61
+ r"\b(?:follow|obey|use)\s+(?:these|the\s+following|my|new)\s+"
62
+ r"instructions?\s+(?:instead|now)\b",
63
+ flags=re.IGNORECASE,
64
+ ),
65
+ ),
66
+ ),
67
+ (
68
+ "permission_override",
69
+ (
70
+ re.compile(
71
+ r"\b(?:grant|escalate|elevate|change|set|switch|override|enable|disable|bypass)\s+"
72
+ r"(?:(?:the|your|parent|session)\s+)?"
73
+ r"(?:permissions?|permission\s+mode|sandbox(?:\s+mode)?|approval(?:\s+mode)?)"
74
+ r"(?:\s+(?:to|as)\s+[A-Za-z0-9_-]+)?\b",
75
+ flags=re.IGNORECASE,
76
+ ),
77
+ re.compile(
78
+ r"\b(?:you|the\s+parent|parent|assistant|main\s+agent)\s+(?:now\s+)?"
79
+ r"(?:have|has)\s+(?:full|unrestricted|elevated)\s+"
80
+ r"(?:access|permissions?)\b",
81
+ flags=re.IGNORECASE,
82
+ ),
83
+ ),
84
+ ),
85
+ (
86
+ "tool_demand",
87
+ (
88
+ re.compile(
89
+ r"\b(?:you|the\s+parent|parent|assistant|main\s+agent)\s+"
90
+ r"(?:must|should|shall|need(?:s)?\s+to)\s+(?:now\s+)?"
91
+ r"(?:call|run|invoke|use|execute)\s+(?:the\s+)?"
92
+ r"(?:[A-Za-z][A-Za-z0-9.-]*_[A-Za-z0-9_.-]+|"
93
+ r"[A-Za-z][A-Za-z0-9_.-]*\s+tool)\b",
94
+ flags=re.IGNORECASE,
95
+ ),
96
+ re.compile(
97
+ r"\b(?:call|run|invoke|use|execute)\s+(?:the\s+)?"
98
+ r"(?:[A-Za-z][A-Za-z0-9.-]*_[A-Za-z0-9_.-]+|"
99
+ r"[A-Za-z][A-Za-z0-9_.-]*\s+tool)\s+(?:now|instead)\b",
100
+ flags=re.IGNORECASE,
101
+ ),
102
+ ),
103
+ ),
104
+ )
105
+
106
+
107
+ @dataclass(frozen=True, slots=True)
108
+ class SanitizedSubagentReport:
109
+ text: str
110
+ sanitized: bool
111
+ detected_categories: tuple[str, ...] = ()
112
+ detected_tags: tuple[str, ...] = ()
113
+
114
+ def metadata(self) -> dict[str, Any]:
115
+ return {
116
+ "sanitized": self.sanitized,
117
+ "detected_categories": list(self.detected_categories),
118
+ "detected_tags": list(self.detected_tags),
119
+ }
120
+
121
+
122
+ def _tag_category(tag_name: str) -> str | None:
123
+ normalized = tag_name.casefold()
124
+ if normalized in _ROLE_TAGS:
125
+ return "role_tag"
126
+ if (
127
+ normalized in _HARNESS_TAGS
128
+ or normalized.endswith("_context")
129
+ or normalized.startswith("alysis_")
130
+ ):
131
+ return "harness_tag"
132
+ return None
133
+
134
+
135
+ def sanitize_subagent_report(text: str) -> SanitizedSubagentReport:
136
+ original = str(text or "")
137
+ categories: set[str] = set()
138
+ tags: set[str] = set()
139
+
140
+ def _escape_tag(match: re.Match[str]) -> str:
141
+ tag_name = str(match.group("name") or "")
142
+ category = _tag_category(tag_name)
143
+ if category is None:
144
+ return match.group(0)
145
+ categories.add(category)
146
+ tags.add(tag_name.casefold()[:_MAX_REPORTED_TAG_CHARS])
147
+ return match.group(0).replace("<", "&lt;").replace(">", "&gt;")
148
+
149
+ sanitized_text = _TAG_RE.sub(_escape_tag, original)
150
+ for category, patterns in _HIGH_RISK_PATTERNS:
151
+ for pattern in patterns:
152
+ sanitized_text, replacements = pattern.subn(
153
+ f"[blocked untrusted subagent {category}]",
154
+ sanitized_text,
155
+ )
156
+ if replacements:
157
+ categories.add(category)
158
+
159
+ ordered_categories = tuple(
160
+ category
161
+ for category in (
162
+ "role_tag",
163
+ "harness_tag",
164
+ "instruction_override",
165
+ "permission_override",
166
+ "tool_demand",
167
+ )
168
+ if category in categories
169
+ )
170
+ ordered_tags = tuple(sorted(tags)[:_MAX_REPORTED_TAGS])
171
+ return SanitizedSubagentReport(
172
+ text=sanitized_text,
173
+ sanitized=sanitized_text != original,
174
+ detected_categories=ordered_categories,
175
+ detected_tags=ordered_tags,
176
+ )
177
+
178
+
179
+ def subagent_report_evidence_text(text: str) -> str:
180
+ def _remove_protected_wrapper(match: re.Match[str]) -> str:
181
+ return " " if _tag_category(str(match.group("name") or "")) is not None else match.group(0)
182
+
183
+ without_wrappers = _ESCAPED_TAG_RE.sub(_remove_protected_wrapper, str(text or ""))
184
+ return _BLOCKED_INSTRUCTION_MARKER_RE.sub(" ", without_wrappers)