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,597 @@
1
+ from __future__ import annotations
2
+
3
+ import json
4
+ import os
5
+ import re
6
+ from collections.abc import Mapping
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from pydantic import ValidationError
11
+
12
+ from ..branding import canonical_user_config_dir, env_get, env_get_from
13
+ from ..runtime_kind import RuntimeKind
14
+ from .errors import McpConfigError as ConfigError
15
+ from .models import (
16
+ ProjectMcpConfigFile,
17
+ PromptsMode,
18
+ ResolvedMcpConfig,
19
+ ResolvedMcpHttpOAuthConfig,
20
+ ResolvedMcpServer,
21
+ ResourcesMode,
22
+ RootsMode,
23
+ UserMcpConfigFile,
24
+ UserMcpHttpServer,
25
+ UserMcpStdioServer,
26
+ )
27
+
28
+ _FORBIDDEN_PROJECT_SERVER_FIELDS = {
29
+ "args",
30
+ "command",
31
+ "env",
32
+ "headers",
33
+ "oauth",
34
+ "tool_prefix",
35
+ "transport",
36
+ "trust",
37
+ "url",
38
+ }
39
+ _ENV_PATTERN = re.compile(r"\$\{([A-Za-z_][A-Za-z0-9_]*)\}")
40
+
41
+
42
+ def _mcp_config_dir() -> Path:
43
+ override = env_get("ALYSIS_CONFIG_DIR")
44
+ if override:
45
+ return Path(override)
46
+ return canonical_user_config_dir()
47
+
48
+
49
+ def user_mcp_config_path() -> Path:
50
+ return _mcp_config_dir() / "mcp.json"
51
+
52
+
53
+ def project_mcp_config_path(workspace_root: Path) -> Path:
54
+ return workspace_root.resolve() / ".alysis" / "mcp.json"
55
+
56
+
57
+ def redact_sensitive_mapping(values: Mapping[str, str] | None) -> dict[str, str]:
58
+ if not values:
59
+ return {}
60
+ return {str(key): "[redacted]" for key in values}
61
+
62
+
63
+ def expand_env_placeholders(
64
+ value: str,
65
+ *,
66
+ env: Mapping[str, str],
67
+ source_path: Path,
68
+ field_path: str,
69
+ ) -> str:
70
+ text = str(value)
71
+
72
+ def _replace(match: re.Match[str]) -> str:
73
+ var_name = match.group(1)
74
+ resolved = env_get_from(env, var_name)
75
+ if resolved is None:
76
+ raise ConfigError(
77
+ f"Invalid MCP config: {source_path}: {field_path}: missing environment variable "
78
+ f"${{{var_name}}}"
79
+ )
80
+ return resolved
81
+
82
+ return _ENV_PATTERN.sub(_replace, text)
83
+
84
+
85
+ def _read_json_object(path: Path) -> dict[str, Any]:
86
+ try:
87
+ raw = json.loads(path.read_text(encoding="utf-8"))
88
+ except Exception as e: # noqa: BLE001
89
+ raise ConfigError(f"Failed to read MCP config: {path}") from e
90
+ if not isinstance(raw, dict):
91
+ raise ConfigError(f"Invalid MCP config format (expected JSON object): {path}")
92
+ return raw
93
+
94
+
95
+ def _normalize_server_keys(path: Path, raw: dict[str, Any]) -> dict[str, Any]:
96
+ servers = raw.get("servers")
97
+ if servers is None:
98
+ raw["servers"] = {}
99
+ return raw
100
+ if not isinstance(servers, dict):
101
+ raise ConfigError(f"Invalid MCP config: {path}: servers must be an object.")
102
+ normalized_servers: dict[str, Any] = {}
103
+ for raw_id, payload in servers.items():
104
+ from .models import normalize_server_id
105
+
106
+ try:
107
+ server_id = normalize_server_id(raw_id)
108
+ except ConfigError as exc:
109
+ raise ConfigError(f"Invalid MCP config: {path}: {exc}") from exc
110
+ if server_id in normalized_servers:
111
+ raise ConfigError(
112
+ f"Invalid MCP config: {path}: duplicate server id after normalization: {server_id}"
113
+ )
114
+ normalized_servers[server_id] = payload
115
+ raw["servers"] = normalized_servers
116
+ return raw
117
+
118
+
119
+ def _raise_project_override_error(path: Path, *, server_id: str, field_name: str) -> None:
120
+ raise ConfigError(
121
+ f"Invalid MCP config: {path}: server '{server_id}': field '{field_name}': "
122
+ "project config cannot override this field."
123
+ )
124
+
125
+
126
+ def _raise_project_narrowing_error(
127
+ path: Path,
128
+ *,
129
+ server_id: str,
130
+ field_name: str,
131
+ reason: str,
132
+ ) -> None:
133
+ raise ConfigError(
134
+ f"Invalid MCP config: {path}: server '{server_id}': field '{field_name}': {reason}"
135
+ )
136
+
137
+
138
+ def _validate_project_override_boundary(path: Path, raw: dict[str, Any]) -> None:
139
+ servers = raw.get("servers")
140
+ if not isinstance(servers, dict):
141
+ return
142
+ for server_id, payload in servers.items():
143
+ if not isinstance(payload, dict):
144
+ continue
145
+ for field_name in sorted(_FORBIDDEN_PROJECT_SERVER_FIELDS):
146
+ if field_name in payload:
147
+ _raise_project_override_error(
148
+ path,
149
+ server_id=str(server_id),
150
+ field_name=field_name,
151
+ )
152
+
153
+
154
+ def _format_validation_error(path: Path, exc: ValidationError) -> ConfigError:
155
+ details: list[str] = []
156
+ for err in exc.errors(include_url=False):
157
+ loc = ".".join(str(part) for part in err.get("loc", ()))
158
+ msg = str(err.get("msg") or "invalid value")
159
+ detail = f"{path}: {msg}" if not loc else f"{path}: {loc}: {msg}"
160
+ details.append(detail)
161
+ joined = "\n".join(details) if details else f"{path}: invalid MCP config"
162
+ return ConfigError(f"Invalid MCP config:\n{joined}")
163
+
164
+
165
+ def _load_user_mcp_config(path: Path) -> UserMcpConfigFile:
166
+ raw = _normalize_server_keys(path, _read_json_object(path))
167
+ try:
168
+ return UserMcpConfigFile.model_validate(raw)
169
+ except ValidationError as exc:
170
+ raise _format_validation_error(path, exc) from exc
171
+
172
+
173
+ def _load_project_mcp_config(path: Path) -> ProjectMcpConfigFile:
174
+ raw = _normalize_server_keys(path, _read_json_object(path))
175
+ _validate_project_override_boundary(path, raw)
176
+ try:
177
+ return ProjectMcpConfigFile.model_validate(raw)
178
+ except ValidationError as exc:
179
+ raise _format_validation_error(path, exc) from exc
180
+
181
+
182
+ def _expand_user_secret_map(
183
+ values: Mapping[str, str] | None,
184
+ *,
185
+ env: Mapping[str, str],
186
+ source_path: Path,
187
+ field_name: str,
188
+ server_id: str,
189
+ ) -> dict[str, str]:
190
+ if not values:
191
+ return {}
192
+ expanded: dict[str, str] = {}
193
+ for key, raw_value in values.items():
194
+ expanded[key] = expand_env_placeholders(
195
+ raw_value,
196
+ env=env,
197
+ source_path=source_path,
198
+ field_path=f"servers.{server_id}.{field_name}.{key}",
199
+ )
200
+ return expanded
201
+
202
+
203
+ def _normalize_name_key(value: str) -> str:
204
+ return value.casefold()
205
+
206
+
207
+ def _merge_enabled(
208
+ *,
209
+ user_value: bool,
210
+ project_value: bool | None,
211
+ project_path: Path,
212
+ server_id: str,
213
+ ) -> bool:
214
+ if project_value is None:
215
+ return user_value
216
+ if not user_value and project_value:
217
+ _raise_project_narrowing_error(
218
+ project_path,
219
+ server_id=server_id,
220
+ field_name="enabled",
221
+ reason="project config cannot re-enable a server disabled in user config.",
222
+ )
223
+ return bool(project_value)
224
+
225
+
226
+ def _merge_enabled_in(
227
+ *,
228
+ user_value: tuple[RuntimeKind, ...] | None,
229
+ project_value: tuple[RuntimeKind, ...] | None,
230
+ project_path: Path,
231
+ server_id: str,
232
+ ) -> tuple[RuntimeKind, ...] | None:
233
+ if project_value is None:
234
+ return user_value
235
+ if user_value is None:
236
+ return project_value
237
+ user_set = set(user_value)
238
+ if any(kind not in user_set for kind in project_value):
239
+ _raise_project_narrowing_error(
240
+ project_path,
241
+ server_id=server_id,
242
+ field_name="enabled_in",
243
+ reason="project config cannot broaden enabled_in beyond user config.",
244
+ )
245
+ return project_value
246
+
247
+
248
+ def _merge_allowed_tools(
249
+ *,
250
+ user_value: tuple[str, ...],
251
+ project_value: tuple[str, ...] | None,
252
+ project_path: Path,
253
+ server_id: str,
254
+ ) -> tuple[str, ...]:
255
+ if project_value is None:
256
+ return user_value
257
+ if not user_value:
258
+ return project_value
259
+ user_set = {_normalize_name_key(name) for name in user_value}
260
+ if any(_normalize_name_key(name) not in user_set for name in project_value):
261
+ _raise_project_narrowing_error(
262
+ project_path,
263
+ server_id=server_id,
264
+ field_name="allowed_tools",
265
+ reason="project config cannot broaden allowed_tools beyond user config.",
266
+ )
267
+ return project_value
268
+
269
+
270
+ def _stable_union_names(
271
+ base: tuple[str, ...],
272
+ extra: tuple[str, ...],
273
+ ) -> tuple[str, ...]:
274
+ merged = list(base)
275
+ seen = {_normalize_name_key(name) for name in base}
276
+ for name in extra:
277
+ key = _normalize_name_key(name)
278
+ if key in seen:
279
+ continue
280
+ seen.add(key)
281
+ merged.append(name)
282
+ return tuple(merged)
283
+
284
+
285
+ def _merge_denied_tools(
286
+ *,
287
+ user_value: tuple[str, ...],
288
+ project_value: tuple[str, ...] | None,
289
+ ) -> tuple[str, ...]:
290
+ if project_value is None:
291
+ return user_value
292
+ return _stable_union_names(user_value, project_value)
293
+
294
+
295
+ def _merge_timeout(
296
+ *,
297
+ field_name: str,
298
+ user_value: float,
299
+ project_value: float | None,
300
+ project_path: Path,
301
+ server_id: str,
302
+ ) -> float:
303
+ if project_value is None:
304
+ return float(user_value)
305
+ if float(project_value) > float(user_value):
306
+ _raise_project_narrowing_error(
307
+ project_path,
308
+ server_id=server_id,
309
+ field_name=field_name,
310
+ reason=f"project config cannot increase {field_name} beyond user config.",
311
+ )
312
+ return float(project_value)
313
+
314
+
315
+ def _merge_roots_mode(
316
+ *,
317
+ user_value: RootsMode,
318
+ project_value: RootsMode | None,
319
+ project_path: Path,
320
+ server_id: str,
321
+ ) -> RootsMode:
322
+ if project_value is None:
323
+ return user_value
324
+ if user_value == "disabled" and project_value == "workspace":
325
+ _raise_project_narrowing_error(
326
+ project_path,
327
+ server_id=server_id,
328
+ field_name="roots_mode",
329
+ reason="project config cannot broaden roots exposure beyond user config.",
330
+ )
331
+ return project_value
332
+
333
+
334
+ def _merge_resources_mode(
335
+ *,
336
+ user_value: ResourcesMode,
337
+ project_value: ResourcesMode | None,
338
+ project_path: Path,
339
+ server_id: str,
340
+ ) -> ResourcesMode:
341
+ if project_value is None:
342
+ return user_value
343
+ if user_value == "disabled" and project_value == "listed_read_only":
344
+ _raise_project_narrowing_error(
345
+ project_path,
346
+ server_id=server_id,
347
+ field_name="resources_mode",
348
+ reason="project config cannot broaden resources exposure beyond user config.",
349
+ )
350
+ return project_value
351
+
352
+
353
+ def _merge_prompts_mode(
354
+ *,
355
+ user_value: PromptsMode,
356
+ project_value: PromptsMode | None,
357
+ project_path: Path,
358
+ server_id: str,
359
+ ) -> PromptsMode:
360
+ if project_value is None:
361
+ return user_value
362
+ if user_value == "disabled" and project_value == "listed_get_only":
363
+ _raise_project_narrowing_error(
364
+ project_path,
365
+ server_id=server_id,
366
+ field_name="prompts_mode",
367
+ reason="project config cannot broaden prompts exposure beyond user config.",
368
+ )
369
+ return project_value
370
+
371
+
372
+ def _validate_merged_tool_policy_overlap(
373
+ *,
374
+ source_path: Path,
375
+ server_id: str,
376
+ allowed_tools: tuple[str, ...],
377
+ denied_tools: tuple[str, ...],
378
+ ) -> None:
379
+ overlap = sorted(
380
+ name
381
+ for name in allowed_tools
382
+ if _normalize_name_key(name) in {_normalize_name_key(denied) for denied in denied_tools}
383
+ )
384
+ if not overlap:
385
+ return
386
+ joined = ", ".join(overlap)
387
+ _raise_project_narrowing_error(
388
+ source_path,
389
+ server_id=server_id,
390
+ field_name="allowed_tools/denied_tools",
391
+ reason=f"allowed_tools and denied_tools cannot overlap after merge: {joined}",
392
+ )
393
+
394
+
395
+ def _merge_server(
396
+ *,
397
+ server_id: str,
398
+ user_server: UserMcpStdioServer | UserMcpHttpServer,
399
+ project_override: Any | None,
400
+ env: Mapping[str, str],
401
+ user_path: Path,
402
+ project_path: Path,
403
+ ) -> ResolvedMcpServer:
404
+ enabled = _merge_enabled(
405
+ user_value=user_server.enabled,
406
+ project_value=getattr(project_override, "enabled", None),
407
+ project_path=project_path,
408
+ server_id=server_id,
409
+ )
410
+ enabled_in = _merge_enabled_in(
411
+ user_value=tuple(user_server.enabled_in) if user_server.enabled_in is not None else None,
412
+ project_value=(
413
+ tuple(project_override.enabled_in)
414
+ if getattr(project_override, "enabled_in", None) is not None
415
+ else None
416
+ ),
417
+ project_path=project_path,
418
+ server_id=server_id,
419
+ )
420
+ allowed_tools = _merge_allowed_tools(
421
+ user_value=tuple(user_server.allowed_tools),
422
+ project_value=(
423
+ tuple(project_override.allowed_tools)
424
+ if getattr(project_override, "allowed_tools", None) is not None
425
+ else None
426
+ ),
427
+ project_path=project_path,
428
+ server_id=server_id,
429
+ )
430
+ denied_tools = _merge_denied_tools(
431
+ user_value=tuple(user_server.denied_tools),
432
+ project_value=(
433
+ tuple(project_override.denied_tools)
434
+ if getattr(project_override, "denied_tools", None) is not None
435
+ else None
436
+ ),
437
+ )
438
+ overlap_source_path = user_path if project_override is None else project_path
439
+ _validate_merged_tool_policy_overlap(
440
+ source_path=overlap_source_path,
441
+ server_id=server_id,
442
+ allowed_tools=allowed_tools,
443
+ denied_tools=denied_tools,
444
+ )
445
+ startup_timeout_s = _merge_timeout(
446
+ field_name="startup_timeout_s",
447
+ user_value=float(user_server.startup_timeout_s),
448
+ project_value=getattr(project_override, "startup_timeout_s", None),
449
+ project_path=project_path,
450
+ server_id=server_id,
451
+ )
452
+ call_timeout_s = _merge_timeout(
453
+ field_name="call_timeout_s",
454
+ user_value=float(user_server.call_timeout_s),
455
+ project_value=getattr(project_override, "call_timeout_s", None),
456
+ project_path=project_path,
457
+ server_id=server_id,
458
+ )
459
+ roots_mode = _merge_roots_mode(
460
+ user_value=user_server.roots_mode,
461
+ project_value=getattr(project_override, "roots_mode", None),
462
+ project_path=project_path,
463
+ server_id=server_id,
464
+ )
465
+ resources_mode = _merge_resources_mode(
466
+ user_value=user_server.resources_mode,
467
+ project_value=getattr(project_override, "resources_mode", None),
468
+ project_path=project_path,
469
+ server_id=server_id,
470
+ )
471
+ prompts_mode = _merge_prompts_mode(
472
+ user_value=user_server.prompts_mode,
473
+ project_value=getattr(project_override, "prompts_mode", None),
474
+ project_path=project_path,
475
+ server_id=server_id,
476
+ )
477
+
478
+ if isinstance(user_server, UserMcpStdioServer):
479
+ return ResolvedMcpServer(
480
+ id=server_id,
481
+ transport="stdio",
482
+ enabled=enabled,
483
+ enabled_in=enabled_in,
484
+ trust=user_server.trust,
485
+ allowed_tools=allowed_tools,
486
+ denied_tools=denied_tools,
487
+ startup_timeout_s=startup_timeout_s,
488
+ call_timeout_s=call_timeout_s,
489
+ tool_prefix=user_server.tool_prefix,
490
+ roots_mode=roots_mode,
491
+ resources_mode=resources_mode,
492
+ prompts_mode=prompts_mode,
493
+ command=user_server.command,
494
+ args=tuple(user_server.args),
495
+ env=_expand_user_secret_map(
496
+ user_server.env,
497
+ env=env,
498
+ source_path=user_path,
499
+ field_name="env",
500
+ server_id=server_id,
501
+ ),
502
+ )
503
+ oauth = None
504
+ if user_server.oauth is not None:
505
+ oauth = ResolvedMcpHttpOAuthConfig(
506
+ client_id=user_server.oauth.client_id,
507
+ redirect_host=user_server.oauth.redirect_host,
508
+ redirect_port=user_server.oauth.redirect_port,
509
+ scopes=tuple(user_server.oauth.scopes or []),
510
+ authorization_server_url=user_server.oauth.authorization_server_url,
511
+ )
512
+ return ResolvedMcpServer(
513
+ id=server_id,
514
+ transport="http",
515
+ enabled=enabled,
516
+ enabled_in=enabled_in,
517
+ trust=user_server.trust,
518
+ allowed_tools=allowed_tools,
519
+ denied_tools=denied_tools,
520
+ startup_timeout_s=startup_timeout_s,
521
+ call_timeout_s=call_timeout_s,
522
+ tool_prefix=user_server.tool_prefix,
523
+ roots_mode=roots_mode,
524
+ resources_mode=resources_mode,
525
+ prompts_mode=prompts_mode,
526
+ url=user_server.url,
527
+ headers=_expand_user_secret_map(
528
+ user_server.headers,
529
+ env=env,
530
+ source_path=user_path,
531
+ field_name="headers",
532
+ server_id=server_id,
533
+ ),
534
+ oauth=oauth,
535
+ )
536
+
537
+
538
+ def load_resolved_mcp_config(
539
+ *,
540
+ workspace_root: Path,
541
+ env: Mapping[str, str] | None = None,
542
+ ) -> ResolvedMcpConfig:
543
+ resolved_workspace_root = workspace_root.resolve()
544
+ user_path = user_mcp_config_path()
545
+ project_path = project_mcp_config_path(resolved_workspace_root)
546
+ effective_env = os.environ if env is None else env
547
+
548
+ user_config = UserMcpConfigFile()
549
+ project_config = ProjectMcpConfigFile()
550
+ user_present = user_path.exists()
551
+ project_present = project_path.exists()
552
+
553
+ if user_present:
554
+ user_config = _load_user_mcp_config(user_path)
555
+ if project_present:
556
+ project_config = _load_project_mcp_config(project_path)
557
+
558
+ if not user_present and not project_present:
559
+ return ResolvedMcpConfig(
560
+ workspace_root=resolved_workspace_root,
561
+ user_config_path=user_path,
562
+ project_config_path=project_path,
563
+ user_config_present=False,
564
+ project_config_present=False,
565
+ servers=(),
566
+ )
567
+
568
+ user_servers = dict(user_config.servers)
569
+ project_servers = dict(project_config.servers)
570
+
571
+ for server_id in project_servers:
572
+ if server_id not in user_servers:
573
+ raise ConfigError(
574
+ f"Invalid MCP config: {project_path}: unknown server id '{server_id}'. "
575
+ "Project MCP config can only override user-defined servers."
576
+ )
577
+
578
+ resolved_servers = tuple(
579
+ _merge_server(
580
+ server_id=server_id,
581
+ user_server=user_server,
582
+ project_override=project_servers.get(server_id),
583
+ env=effective_env,
584
+ user_path=user_path,
585
+ project_path=project_path,
586
+ )
587
+ for server_id, user_server in user_servers.items()
588
+ )
589
+
590
+ return ResolvedMcpConfig(
591
+ workspace_root=resolved_workspace_root,
592
+ user_config_path=user_path,
593
+ project_config_path=project_path,
594
+ user_config_present=user_present,
595
+ project_config_present=project_present,
596
+ servers=resolved_servers,
597
+ )
@@ -0,0 +1,113 @@
1
+ from __future__ import annotations
2
+
3
+ from ..config import ConfigError as AppConfigError
4
+
5
+
6
+ class McpError(RuntimeError):
7
+ error_code = "mcp_error"
8
+ retryable = False
9
+
10
+ def __init__(
11
+ self,
12
+ message: str = "",
13
+ *,
14
+ server_id: str | None = None,
15
+ method: str | None = None,
16
+ cause: BaseException | None = None,
17
+ ) -> None:
18
+ super().__init__(message)
19
+ self.server_id = server_id
20
+ self.method = method
21
+ self.cause = cause
22
+
23
+
24
+ class McpConfigError(McpError, AppConfigError):
25
+ error_code = "mcp_config_error"
26
+
27
+
28
+ class McpPolicyError(McpError):
29
+ error_code = "mcp_policy_error"
30
+
31
+
32
+ class McpTransportError(McpError):
33
+ error_code = "mcp_transport_error"
34
+ retryable = True
35
+
36
+
37
+ class McpTimeoutError(McpTransportError):
38
+ error_code = "mcp_timeout_error"
39
+
40
+
41
+ class McpProcessError(McpTransportError):
42
+ error_code = "mcp_process_error"
43
+
44
+
45
+ class McpProtocolError(McpError):
46
+ error_code = "mcp_protocol_error"
47
+
48
+
49
+ class McpSerializationError(McpProtocolError):
50
+ error_code = "mcp_serialization_error"
51
+
52
+
53
+ class McpRemoteError(McpError):
54
+ error_code = "mcp_remote_error"
55
+
56
+
57
+ class McpAuthError(McpError):
58
+ error_code = "mcp_auth_error"
59
+
60
+
61
+ class McpTokenStoreError(McpAuthError):
62
+ error_code = "mcp_token_store_error"
63
+
64
+
65
+ class McpOAuthTokenStoreError(McpTokenStoreError):
66
+ error_code = "mcp_oauth_token_store_error"
67
+
68
+
69
+ class McpTokenStoreUnavailableError(McpOAuthTokenStoreError):
70
+ error_code = "mcp_token_store_unavailable"
71
+
72
+
73
+ class McpTokenStoreCorruptError(McpOAuthTokenStoreError):
74
+ error_code = "mcp_token_store_corrupt"
75
+
76
+
77
+ class McpTokenStoreMigrationError(McpOAuthTokenStoreError):
78
+ error_code = "mcp_token_store_migration"
79
+
80
+
81
+ class McpTokenStoreVersionError(McpOAuthTokenStoreError):
82
+ error_code = "mcp_token_store_version"
83
+
84
+
85
+ class McpCancellationError(McpError):
86
+ error_code = "mcp_cancellation_error"
87
+
88
+
89
+ class McpResourceLimitError(McpError):
90
+ error_code = "mcp_resource_limit_error"
91
+
92
+
93
+ class McpNotSupportedError(McpError):
94
+ error_code = "mcp_not_supported_error"
95
+
96
+
97
+ def is_retryable_mcp_error(error: BaseException, *, side_effect_free: bool) -> bool:
98
+ if not side_effect_free or not isinstance(error, McpError):
99
+ return False
100
+ if isinstance(
101
+ error,
102
+ (
103
+ McpConfigError,
104
+ McpPolicyError,
105
+ McpProtocolError,
106
+ McpAuthError,
107
+ McpCancellationError,
108
+ McpResourceLimitError,
109
+ McpNotSupportedError,
110
+ ),
111
+ ):
112
+ return False
113
+ return bool(getattr(error, "retryable", False))