aria-code 0.52.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 (478) hide show
  1. aria_code/__init__.py +42 -0
  2. aria_code/_version.py +20 -0
  3. aria_code/adk_apps/__init__.py +1 -0
  4. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  5. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  6. aria_code/adk_apps/aria_research/__init__.py +1 -0
  7. aria_code/adk_apps/aria_research/agent.py +31 -0
  8. aria_code/agents/__init__.py +34 -0
  9. aria_code/agents/base.py +318 -0
  10. aria_code/agents/code_review.py +200 -0
  11. aria_code/agents/deep/__init__.py +37 -0
  12. aria_code/agents/deep/calibration_loop.py +144 -0
  13. aria_code/agents/deep/critic.py +125 -0
  14. aria_code/agents/deep/deepen.py +193 -0
  15. aria_code/agents/deep/models.py +149 -0
  16. aria_code/agents/deep/pipeline.py +164 -0
  17. aria_code/agents/deep/quant_fusion.py +192 -0
  18. aria_code/agents/deep/themes.py +95 -0
  19. aria_code/agents/deep/tiers.py +106 -0
  20. aria_code/agents/engineering/__init__.py +14 -0
  21. aria_code/agents/engineering/coder.py +222 -0
  22. aria_code/agents/engineering/tester.py +145 -0
  23. aria_code/agents/financial/__init__.py +21 -0
  24. aria_code/agents/financial/cashflow_burn.py +119 -0
  25. aria_code/agents/financial/catalyst.py +279 -0
  26. aria_code/agents/financial/corporate_finance.py +175 -0
  27. aria_code/agents/financial/debate.py +224 -0
  28. aria_code/agents/financial/earnings.py +303 -0
  29. aria_code/agents/financial/fundamental.py +191 -0
  30. aria_code/agents/financial/macro.py +99 -0
  31. aria_code/agents/financial/news.py +207 -0
  32. aria_code/agents/financial/risk.py +165 -0
  33. aria_code/agents/financial/sector.py +279 -0
  34. aria_code/agents/financial/strategist.py +121 -0
  35. aria_code/agents/financial/stripe_revenue.py +127 -0
  36. aria_code/agents/financial/synthesis.py +274 -0
  37. aria_code/agents/financial/technical.py +258 -0
  38. aria_code/agents/orchestrator.py +44 -0
  39. aria_code/agents/portfolio_agent.py +363 -0
  40. aria_code/agents/realty/__init__.py +62 -0
  41. aria_code/agents/realty/asset_diagnosis.py +152 -0
  42. aria_code/agents/realty/business_match.py +166 -0
  43. aria_code/agents/realty/cashflow_verify.py +209 -0
  44. aria_code/agents/realty/contract_rules.py +210 -0
  45. aria_code/agents/realty/energy_anomaly.py +189 -0
  46. aria_code/agents/realty/exit_settlement.py +208 -0
  47. aria_code/agents/realty/fulfillment_risk.py +206 -0
  48. aria_code/agents/realty/ops_optimize.py +160 -0
  49. aria_code/agents/realty/revenue_share.py +215 -0
  50. aria_code/agents/registry.py +158 -0
  51. aria_code/agents/signal_scheme.py +111 -0
  52. aria_code/agents/sports/__init__.py +0 -0
  53. aria_code/agents/sports/football_agent.py +197 -0
  54. aria_code/agents/supervisor.py +113 -0
  55. aria_code/agents/team.py +492 -0
  56. aria_code/agents/warehouse/__init__.py +25 -0
  57. aria_code/agents/warehouse/contracts.py +26 -0
  58. aria_code/agents/warehouse/dashboard.py +155 -0
  59. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  60. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  61. aria_code/agents/warehouse/inventory_health.py +24 -0
  62. aria_code/agents/warehouse/local_run.py +71 -0
  63. aria_code/agents/warehouse/local_snapshot.py +132 -0
  64. aria_code/agents/warehouse/logistics_cost.py +84 -0
  65. aria_code/agents/warehouse/logistics_sync.py +21 -0
  66. aria_code/agents/warehouse/workflow.py +31 -0
  67. aria_code/aliyun_data_client.py +4 -0
  68. aria_code/apps/README.md +12 -0
  69. aria_code/apps/__init__.py +2 -0
  70. aria_code/apps/channels/README.md +15 -0
  71. aria_code/apps/channels/__init__.py +17 -0
  72. aria_code/apps/channels/intake.py +92 -0
  73. aria_code/apps/channels/registry.py +95 -0
  74. aria_code/apps/channels/tradingview.py +94 -0
  75. aria_code/apps/cli/README.md +13 -0
  76. aria_code/apps/cli/__init__.py +2 -0
  77. aria_code/apps/cli/_optional.py +120 -0
  78. aria_code/apps/cli/bootstrap.py +145 -0
  79. aria_code/apps/cli/broker_render.py +206 -0
  80. aria_code/apps/cli/codegen_paths.py +29 -0
  81. aria_code/apps/cli/commands/__init__.py +16 -0
  82. aria_code/apps/cli/commands/_business_render.py +78 -0
  83. aria_code/apps/cli/commands/_ui.py +71 -0
  84. aria_code/apps/cli/commands/analysis_cmds.py +369 -0
  85. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  86. aria_code/apps/cli/commands/auth_cmds.py +239 -0
  87. aria_code/apps/cli/commands/backtest_cmds.py +2543 -0
  88. aria_code/apps/cli/commands/broker_cmds.py +1234 -0
  89. aria_code/apps/cli/commands/business_workflow_cmds.py +319 -0
  90. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  91. aria_code/apps/cli/commands/catalog.py +87 -0
  92. aria_code/apps/cli/commands/core_cmds.py +2520 -0
  93. aria_code/apps/cli/commands/data_cmds.py +457 -0
  94. aria_code/apps/cli/commands/diagnostic_cmds.py +230 -0
  95. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  96. aria_code/apps/cli/commands/file_cmds.py +312 -0
  97. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  98. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  99. aria_code/apps/cli/commands/market.py +434 -0
  100. aria_code/apps/cli/commands/market_cmds.py +1381 -0
  101. aria_code/apps/cli/commands/market_context.py +425 -0
  102. aria_code/apps/cli/commands/market_render.py +7 -0
  103. aria_code/apps/cli/commands/model_cmds.py +2153 -0
  104. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  105. aria_code/apps/cli/commands/orchestrator_cmds.py +179 -0
  106. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  107. aria_code/apps/cli/commands/portfolio_cmds.py +1278 -0
  108. aria_code/apps/cli/commands/report.py +437 -0
  109. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  110. aria_code/apps/cli/commands/session_cmds.py +236 -0
  111. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  112. aria_code/apps/cli/commands/team.py +669 -0
  113. aria_code/apps/cli/commands/team_render.py +8 -0
  114. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  115. aria_code/apps/cli/commands/warehouse_cmds.py +79 -0
  116. aria_code/apps/cli/commands/workflow_cmds.py +480 -0
  117. aria_code/apps/cli/commands/workspace_cmds.py +1494 -0
  118. aria_code/apps/cli/config_paths.py +73 -0
  119. aria_code/apps/cli/config_store.py +78 -0
  120. aria_code/apps/cli/constants.py +88 -0
  121. aria_code/apps/cli/context.py +34 -0
  122. aria_code/apps/cli/deterministic.py +191 -0
  123. aria_code/apps/cli/direct.py +48 -0
  124. aria_code/apps/cli/finance_service_catalog.py +111 -0
  125. aria_code/apps/cli/football_reports.py +231 -0
  126. aria_code/apps/cli/github_app_auth.py +135 -0
  127. aria_code/apps/cli/google_login.py +196 -0
  128. aria_code/apps/cli/handlers/__init__.py +11 -0
  129. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  130. aria_code/apps/cli/handlers/chart_handlers.py +1309 -0
  131. aria_code/apps/cli/handlers/market_handlers.py +2513 -0
  132. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  133. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  134. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  135. aria_code/apps/cli/helpers.py +302 -0
  136. aria_code/apps/cli/hooks.py +180 -0
  137. aria_code/apps/cli/i18n.py +297 -0
  138. aria_code/apps/cli/intent.py +136 -0
  139. aria_code/apps/cli/intent_router.py +276 -0
  140. aria_code/apps/cli/intent_signals.py +344 -0
  141. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  142. aria_code/apps/cli/local_tool_schemas.py +536 -0
  143. aria_code/apps/cli/main.py +29 -0
  144. aria_code/apps/cli/market_metadata.py +135 -0
  145. aria_code/apps/cli/market_universe.py +487 -0
  146. aria_code/apps/cli/message_processing.py +310 -0
  147. aria_code/apps/cli/model_catalog.py +620 -0
  148. aria_code/apps/cli/pdf_report.py +977 -0
  149. aria_code/apps/cli/plan_mode.py +139 -0
  150. aria_code/apps/cli/plotly_html.py +15 -0
  151. aria_code/apps/cli/prediction_feedback.py +202 -0
  152. aria_code/apps/cli/preflight.py +502 -0
  153. aria_code/apps/cli/project_aria.py +60 -0
  154. aria_code/apps/cli/prompt_assembly.py +59 -0
  155. aria_code/apps/cli/prompts/__init__.py +0 -0
  156. aria_code/apps/cli/prompts/coding.py +53 -0
  157. aria_code/apps/cli/prompts/select.py +142 -0
  158. aria_code/apps/cli/prompts/system_prompts.py +552 -0
  159. aria_code/apps/cli/prompts/ui.py +436 -0
  160. aria_code/apps/cli/provider_endpoints.py +237 -0
  161. aria_code/apps/cli/provider_keys.py +73 -0
  162. aria_code/apps/cli/providers/__init__.py +1 -0
  163. aria_code/apps/cli/providers/base.py +521 -0
  164. aria_code/apps/cli/providers/chat_routing.py +128 -0
  165. aria_code/apps/cli/providers/collaboration.py +98 -0
  166. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  167. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  168. aria_code/apps/cli/providers/llm/ollama_stream.py +1594 -0
  169. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  170. aria_code/apps/cli/providers/llm/sse_stream.py +257 -0
  171. aria_code/apps/cli/providers/runtime_bridge.py +353 -0
  172. aria_code/apps/cli/providers/vertexai_stream.py +316 -0
  173. aria_code/apps/cli/runtime_consumer.py +657 -0
  174. aria_code/apps/cli/sandbox.py +32 -0
  175. aria_code/apps/cli/session_export.py +120 -0
  176. aria_code/apps/cli/session_jsonl.py +208 -0
  177. aria_code/apps/cli/session_store.py +112 -0
  178. aria_code/apps/cli/skills_catalog.py +273 -0
  179. aria_code/apps/cli/todo_tracker.py +190 -0
  180. aria_code/apps/cli/tool_executor.py +823 -0
  181. aria_code/apps/cli/tool_registry.py +34 -0
  182. aria_code/apps/cli/tool_scope.py +106 -0
  183. aria_code/apps/cli/tools/__init__.py +40 -0
  184. aria_code/apps/cli/tools/context.py +46 -0
  185. aria_code/apps/cli/tools/file_tools.py +180 -0
  186. aria_code/apps/cli/tools/market_tools.py +620 -0
  187. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  188. aria_code/apps/cli/tools/system_tools.py +726 -0
  189. aria_code/apps/cli/tools/write_tools.py +845 -0
  190. aria_code/apps/cli/tradingview_bridge.py +529 -0
  191. aria_code/apps/cli/turn_planning.py +137 -0
  192. aria_code/apps/cli/update_check.py +153 -0
  193. aria_code/apps/cli/utils/__init__.py +0 -0
  194. aria_code/apps/cli/utils/market_detect.py +1597 -0
  195. aria_code/apps/daemon/README.md +14 -0
  196. aria_code/apps/vscode/README.md +115 -0
  197. aria_code/apps/vscode/package.json +70 -0
  198. aria_code/aria_cli.py +6743 -0
  199. aria_code/aria_daemon.py +1398 -0
  200. aria_code/aria_feishu_bot.py +1423 -0
  201. aria_code/aria_mcp_server.py +7 -0
  202. aria_code/aria_relay_client.py +4 -0
  203. aria_code/aria_relay_server.py +490 -0
  204. aria_code/aria_telegram_bot.py +202 -0
  205. aria_code/ariarc.py +385 -0
  206. aria_code/artifacts.py +581 -0
  207. aria_code/backtest_engine.py +4 -0
  208. aria_code/backtest_report.py +4 -0
  209. aria_code/brokers/__init__.py +78 -0
  210. aria_code/brokers/automation.py +193 -0
  211. aria_code/brokers/base.py +231 -0
  212. aria_code/brokers/capabilities.py +264 -0
  213. aria_code/brokers/cn/__init__.py +10 -0
  214. aria_code/brokers/cn/easytrader_broker.py +193 -0
  215. aria_code/brokers/cn/futu_broker.py +194 -0
  216. aria_code/brokers/cn/longbridge_broker.py +190 -0
  217. aria_code/brokers/cn/tiger_broker.py +196 -0
  218. aria_code/brokers/cn/xtquant_broker.py +225 -0
  219. aria_code/brokers/config.py +414 -0
  220. aria_code/brokers/intl/__init__.py +5 -0
  221. aria_code/brokers/intl/alpaca_broker.py +183 -0
  222. aria_code/brokers/intl/ibkr_broker.py +215 -0
  223. aria_code/brokers/intl/trading212_broker.py +183 -0
  224. aria_code/brokers/intl/webull_broker.py +156 -0
  225. aria_code/brokers/paper_broker.py +266 -0
  226. aria_code/brokers/planning.py +296 -0
  227. aria_code/brokers/registry.py +213 -0
  228. aria_code/brokers/trading.py +267 -0
  229. aria_code/canva_client.py +4 -0
  230. aria_code/change_store.py +127 -0
  231. aria_code/clients/__init__.py +0 -0
  232. aria_code/clients/aliyun_data_client.py +660 -0
  233. aria_code/clients/aria_relay_client.py +182 -0
  234. aria_code/clients/canva_client.py +284 -0
  235. aria_code/clients/figma_client.py +100 -0
  236. aria_code/clients/football_data_client.py +1671 -0
  237. aria_code/clients/kling_video_client.py +182 -0
  238. aria_code/clients/market_data_client.py +1976 -0
  239. aria_code/clients/openai_image_client.py +242 -0
  240. aria_code/clients/runway_video_client.py +157 -0
  241. aria_code/clients/warehouse_erp_client.py +105 -0
  242. aria_code/cloud_memory.py +181 -0
  243. aria_code/command_safety.py +19 -0
  244. aria_code/computer_use_tools.py +4 -0
  245. aria_code/dashboard_generator.py +579 -0
  246. aria_code/data_analysis_tools.py +4 -0
  247. aria_code/data_cleaner.py +483 -0
  248. aria_code/data_service.py +513 -0
  249. aria_code/datasources/__init__.py +23 -0
  250. aria_code/datasources/base.py +166 -0
  251. aria_code/datasources/router.py +253 -0
  252. aria_code/datasources/sources/__init__.py +15 -0
  253. aria_code/datasources/sources/akshare_source.py +269 -0
  254. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  255. aria_code/datasources/sources/edgar_source.py +219 -0
  256. aria_code/datasources/sources/finnhub_source.py +198 -0
  257. aria_code/datasources/sources/fred_source.py +223 -0
  258. aria_code/datasources/sources/tushare_source.py +143 -0
  259. aria_code/datasources/sources/web_scraper_source.py +278 -0
  260. aria_code/datasources/sources/world_bank_source.py +206 -0
  261. aria_code/datasources/sources/yfinance_source.py +152 -0
  262. aria_code/demo_player.py +207 -0
  263. aria_code/doctor.py +813 -0
  264. aria_code/domain/__init__.py +0 -0
  265. aria_code/domain/backtest_engine.py +354 -0
  266. aria_code/domain/backtest_report.py +472 -0
  267. aria_code/domain/finance_formulas.py +389 -0
  268. aria_code/domain/portfolio_ledger.py +307 -0
  269. aria_code/domain/strategy_vault.py +421 -0
  270. aria_code/evals/__init__.py +37 -0
  271. aria_code/evals/harness.py +598 -0
  272. aria_code/evals/runner.py +238 -0
  273. aria_code/external_agent_runner.py +115 -0
  274. aria_code/figma_client.py +4 -0
  275. aria_code/file_analysis_tools.py +4 -0
  276. aria_code/finance_formulas.py +4 -0
  277. aria_code/football_data_client.py +4 -0
  278. aria_code/image_gen_tools.py +4 -0
  279. aria_code/intent_classifier.py +386 -0
  280. aria_code/kling_video_client.py +4 -0
  281. aria_code/licensing.py +151 -0
  282. aria_code/local_finance_tools.py +4 -0
  283. aria_code/local_image_provider.py +4 -0
  284. aria_code/local_llm_provider.py +4 -0
  285. aria_code/macro_tools.py +4 -0
  286. aria_code/markdown_pdf.py +289 -0
  287. aria_code/market_data_client.py +4 -0
  288. aria_code/mcp_client.py +639 -0
  289. aria_code/memory_manager.py +246 -0
  290. aria_code/model_capability.py +486 -0
  291. aria_code/notification_tools.py +4 -0
  292. aria_code/openai_image_client.py +4 -0
  293. aria_code/packages/__init__.py +23 -0
  294. aria_code/packages/adk_bridge/__init__.py +11 -0
  295. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  296. aria_code/packages/adk_bridge/market_tools.py +125 -0
  297. aria_code/packages/aria_agents/__init__.py +5 -0
  298. aria_code/packages/aria_agents/manifest.py +78 -0
  299. aria_code/packages/aria_core/__init__.py +34 -0
  300. aria_code/packages/aria_core/architecture.py +269 -0
  301. aria_code/packages/aria_core/export.py +147 -0
  302. aria_code/packages/aria_core/manifest.py +65 -0
  303. aria_code/packages/aria_core/paths.py +57 -0
  304. aria_code/packages/aria_core/secure_file.py +68 -0
  305. aria_code/packages/aria_infra/__init__.py +15 -0
  306. aria_code/packages/aria_infra/arthera.py +52 -0
  307. aria_code/packages/aria_infra/doctor.py +246 -0
  308. aria_code/packages/aria_infra/product.py +40 -0
  309. aria_code/packages/aria_mcp/__init__.py +25 -0
  310. aria_code/packages/aria_mcp/bridge.py +201 -0
  311. aria_code/packages/aria_mcp/circuit.py +82 -0
  312. aria_code/packages/aria_mcp/config.py +111 -0
  313. aria_code/packages/aria_mcp/installer.py +95 -0
  314. aria_code/packages/aria_mcp/server.py +1550 -0
  315. aria_code/packages/aria_mcp/tools.py +81 -0
  316. aria_code/packages/aria_sdk/__init__.py +19 -0
  317. aria_code/packages/aria_sdk/client.py +396 -0
  318. aria_code/packages/aria_sdk/providers.py +70 -0
  319. aria_code/packages/aria_sdk/streaming.py +79 -0
  320. aria_code/packages/aria_sdk/types.py +86 -0
  321. aria_code/packages/aria_services/__init__.py +92 -0
  322. aria_code/packages/aria_services/cache.py +60 -0
  323. aria_code/packages/aria_services/context.py +356 -0
  324. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  325. aria_code/packages/aria_services/data.py +11 -0
  326. aria_code/packages/aria_services/provider_health.py +268 -0
  327. aria_code/packages/aria_services/references.py +247 -0
  328. aria_code/packages/aria_services/registry.py +249 -0
  329. aria_code/packages/aria_services/research_protocol.py +57 -0
  330. aria_code/packages/aria_services/research_quality.py +186 -0
  331. aria_code/packages/aria_services/settings.py +157 -0
  332. aria_code/packages/aria_services/usage.py +151 -0
  333. aria_code/packages/aria_skills/__init__.py +46 -0
  334. aria_code/packages/aria_skills/catalog.py +149 -0
  335. aria_code/packages/aria_skills/loader.py +455 -0
  336. aria_code/packages/aria_skills/registry.py +84 -0
  337. aria_code/packages/aria_tools/__init__.py +5 -0
  338. aria_code/packages/aria_tools/financial/__init__.py +8 -0
  339. aria_code/packages/aria_tools/financial/_paths.py +130 -0
  340. aria_code/packages/aria_tools/financial/compliance.py +31 -0
  341. aria_code/packages/aria_tools/financial/factors.py +33 -0
  342. aria_code/packages/aria_tools/financial/risk_tools.py +33 -0
  343. aria_code/packages/aria_tools/financial/strategy.py +31 -0
  344. aria_code/packages/aria_tools/registry.py +128 -0
  345. aria_code/packages/quant_engine/__init__.py +34 -0
  346. aria_code/packages/quant_engine/_namespace.py +62 -0
  347. aria_code/packages/quant_engine/backtest/__init__.py +14 -0
  348. aria_code/packages/quant_engine/backtest/core.py +53 -0
  349. aria_code/packages/quant_engine/backtest/engine.py +96 -0
  350. aria_code/packages/quant_engine/portfolio/__init__.py +13 -0
  351. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  352. aria_code/packages/quant_engine/sports/__init__.py +80 -0
  353. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  354. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  355. aria_code/packages/quant_engine/sports/elo.py +300 -0
  356. aria_code/packages/quant_engine/sports/form.py +188 -0
  357. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  358. aria_code/packages/quant_engine/sports/ml_model.py +377 -0
  359. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  360. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  361. aria_code/packages/quant_engine/stochastic/__init__.py +35 -0
  362. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  363. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  364. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  365. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  366. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  367. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  368. aria_code/packs/__init__.py +85 -0
  369. aria_code/packs/base.py +168 -0
  370. aria_code/packs/finance/__init__.py +250 -0
  371. aria_code/packs/logistics/__init__.py +169 -0
  372. aria_code/packs/payments/__init__.py +139 -0
  373. aria_code/packs/realty/__init__.py +190 -0
  374. aria_code/packs/registry.py +202 -0
  375. aria_code/packs/rules.py +90 -0
  376. aria_code/plan_utils.py +194 -0
  377. aria_code/plugin_loader.py +328 -0
  378. aria_code/portfolio_ledger.py +4 -0
  379. aria_code/preview_server.py +449 -0
  380. aria_code/privacy/__init__.py +5 -0
  381. aria_code/privacy/feedback.py +123 -0
  382. aria_code/project_review.py +278 -0
  383. aria_code/project_review_api.py +235 -0
  384. aria_code/project_review_server.py +18 -0
  385. aria_code/project_tools.py +4 -0
  386. aria_code/providers/__init__.py +30 -0
  387. aria_code/providers/llm/__init__.py +19 -0
  388. aria_code/providers/llm/anthropic.py +184 -0
  389. aria_code/providers/llm/autoconfig.py +213 -0
  390. aria_code/providers/llm/base.py +139 -0
  391. aria_code/providers/llm/custom.py +158 -0
  392. aria_code/providers/llm/ollama.py +129 -0
  393. aria_code/providers/llm/openai_compat.py +313 -0
  394. aria_code/providers/llm/registry.py +444 -0
  395. aria_code/providers/local_image_provider.py +202 -0
  396. aria_code/providers/local_llm_provider.py +589 -0
  397. aria_code/realty_data_tools.py +4 -0
  398. aria_code/report_exporters.py +150 -0
  399. aria_code/report_generator.py +1764 -0
  400. aria_code/runtime/__init__.py +169 -0
  401. aria_code/runtime/acceptance.py +481 -0
  402. aria_code/runtime/agent_loop.py +1520 -0
  403. aria_code/runtime/approval.py +57 -0
  404. aria_code/runtime/budget.py +232 -0
  405. aria_code/runtime/checkpoints.py +432 -0
  406. aria_code/runtime/crash_report.py +174 -0
  407. aria_code/runtime/events.py +113 -0
  408. aria_code/runtime/gateway.py +150 -0
  409. aria_code/runtime/lsp.py +484 -0
  410. aria_code/runtime/orchestrator.py +55 -0
  411. aria_code/runtime/repo_map.py +700 -0
  412. aria_code/runtime/run_state.py +127 -0
  413. aria_code/runtime/run_store.py +450 -0
  414. aria_code/runtime/self_healing.py +422 -0
  415. aria_code/runtime/subagent.py +547 -0
  416. aria_code/runtime/task_ledger.py +86 -0
  417. aria_code/runtime/task_orchestrator.py +90 -0
  418. aria_code/runtime/tool_executor.py +217 -0
  419. aria_code/runtime/tool_policy.py +107 -0
  420. aria_code/runtime/worktrees.py +215 -0
  421. aria_code/runway_video_client.py +4 -0
  422. aria_code/safety/__init__.py +23 -0
  423. aria_code/safety/permissions.py +306 -0
  424. aria_code/safety/service.py +102 -0
  425. aria_code/setup_wizard.py +673 -0
  426. aria_code/spreadsheet_tools.py +4 -0
  427. aria_code/strategy_vault.py +4 -0
  428. aria_code/tools/__init__.py +0 -0
  429. aria_code/tools/artifact_tools.py +252 -0
  430. aria_code/tools/broker_tools.py +92 -0
  431. aria_code/tools/build_quant_engine.py +97 -0
  432. aria_code/tools/code_audit_tools.py +192 -0
  433. aria_code/tools/computer_use_tools.py +504 -0
  434. aria_code/tools/data_analysis_tools.py +809 -0
  435. aria_code/tools/enterprise_finance_tools.py +156 -0
  436. aria_code/tools/extended_tools.py +240 -0
  437. aria_code/tools/file_analysis_tools.py +885 -0
  438. aria_code/tools/image_gen_tools.py +114 -0
  439. aria_code/tools/local_finance_tools.py +3417 -0
  440. aria_code/tools/logistics_tools.py +135 -0
  441. aria_code/tools/macro_tools.py +369 -0
  442. aria_code/tools/notification_tools.py +249 -0
  443. aria_code/tools/project_tools.py +525 -0
  444. aria_code/tools/realty_data_tools.py +659 -0
  445. aria_code/tools/spreadsheet_tools.py +388 -0
  446. aria_code/tools/stripe_tools.py +115 -0
  447. aria_code/ui/__init__.py +105 -0
  448. aria_code/ui/banner.py +508 -0
  449. aria_code/ui/completer.py +501 -0
  450. aria_code/ui/console.py +281 -0
  451. aria_code/ui/image_render.py +243 -0
  452. aria_code/ui/input_box.py +488 -0
  453. aria_code/ui/picker.py +201 -0
  454. aria_code/ui/render/__init__.py +11 -0
  455. aria_code/ui/render/finance.py +1705 -0
  456. aria_code/ui/render/market.py +225 -0
  457. aria_code/ui/render/output.py +775 -0
  458. aria_code/ui/render/responsive.py +75 -0
  459. aria_code/ui/render/team.py +354 -0
  460. aria_code/ui/robot.py +235 -0
  461. aria_code/ui/startup_dashboard.py +109 -0
  462. aria_code/utils/vscode_uri.py +37 -0
  463. aria_code/video_analysis.py +147 -0
  464. aria_code/video_editor.py +271 -0
  465. aria_code/workspace/__init__.py +6 -0
  466. aria_code/workspace/files.py +193 -0
  467. aria_code/workspace/verify.py +113 -0
  468. aria_code/workspace_changes.py +199 -0
  469. aria_code/workspace_context.py +233 -0
  470. aria_code/workspace_service.py +196 -0
  471. aria_code/workspace_tools.py +150 -0
  472. aria_code-0.52.0.dist-info/METADATA +237 -0
  473. aria_code-0.52.0.dist-info/RECORD +478 -0
  474. aria_code-0.52.0.dist-info/WHEEL +5 -0
  475. aria_code-0.52.0.dist-info/entry_points.txt +3 -0
  476. aria_code-0.52.0.dist-info/licenses/LICENSE +201 -0
  477. aria_code-0.52.0.dist-info/licenses/NOTICE +49 -0
  478. aria_code-0.52.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,169 @@
1
+ """Runtime primitives for Aria Code agent execution."""
2
+
3
+ from .agent_loop import (
4
+ AgentErrorPresentation,
5
+ AgentEvent,
6
+ AgentEventCancelled,
7
+ AgentEventComplete,
8
+ AgentEventError,
9
+ AgentEventStatus,
10
+ AgentEventThinking,
11
+ AgentEventToken,
12
+ AgentEventToolCall,
13
+ AgentEventToolResult,
14
+ AgentOptions,
15
+ AgentTurnMetadata,
16
+ AgentTurnEnvelope,
17
+ AgentTurnResult,
18
+ AgentTurnState,
19
+ ToolBatchState,
20
+ ToolExecutionActivity,
21
+ ToolExecutionTurnResult,
22
+ ToolCallTask,
23
+ ToolTurnPlan,
24
+ build_next_turn_messages,
25
+ build_tool_followup,
26
+ collect_parallel_done,
27
+ detect_task_complete,
28
+ execute_tool_turn,
29
+ LoopGuard,
30
+ record_tool_result,
31
+ run_agent,
32
+ run_parallel_tools,
33
+ run_serial_tool,
34
+ split_tool_calls,
35
+ )
36
+ from .acceptance import (
37
+ AcceptanceGate,
38
+ AcceptanceReport,
39
+ CheckResult,
40
+ DEFAULT_MUTATING_TOOLS,
41
+ extract_mutated_paths,
42
+ )
43
+ from .repo_map import (
44
+ REPO_MAP_SCHEMAS,
45
+ REPO_MAP_TOOLS,
46
+ RepoMap,
47
+ Symbol,
48
+ extract_symbols,
49
+ )
50
+ from .gateway import TurnResult, run_turn
51
+ from .approval import ApprovalDecision, apply_approval_decision
52
+ from .events import RuntimeEvent, RuntimeTrace, ToolCallRecord, TurnResultRecord
53
+ from .run_state import (
54
+ ACTIVE_RUN_STATUSES,
55
+ TERMINAL_RUN_STATUSES,
56
+ InvalidRunTransition,
57
+ RunStatus,
58
+ can_transition,
59
+ is_terminal,
60
+ require_transition,
61
+ )
62
+ from .run_store import RunEventRecord, RunNotFoundError, RunRecord, RunStore
63
+ from .checkpoints import (
64
+ CheckpointConflictError,
65
+ CheckpointFile,
66
+ CheckpointNotFoundError,
67
+ CheckpointRecord,
68
+ CheckpointStore,
69
+ RestoreResult,
70
+ )
71
+ from .worktrees import WorktreeApplyResult, WorktreeError, WorktreeManager, WorktreeSpec
72
+ from .tool_executor import ToolExecutor
73
+ from .tool_policy import check_tool_policy, add_to_policy, remove_from_policy, load_tool_policy
74
+ from .subagent import (
75
+ SUBAGENT_TOOLS,
76
+ SUBAGENT_SCHEMAS,
77
+ tool_spawn_task,
78
+ tool_task_status,
79
+ tool_task_result,
80
+ tool_task_list,
81
+ tool_task_cancel,
82
+ register_runner,
83
+ restore_tasks,
84
+ )
85
+
86
+ __all__ = [
87
+ "REPO_MAP_SCHEMAS",
88
+ "REPO_MAP_TOOLS",
89
+ "RepoMap",
90
+ "Symbol",
91
+ "extract_symbols",
92
+ "AcceptanceGate",
93
+ "AcceptanceReport",
94
+ "CheckResult",
95
+ "DEFAULT_MUTATING_TOOLS",
96
+ "extract_mutated_paths",
97
+ "AgentErrorPresentation",
98
+ "AgentEvent",
99
+ "AgentEventCancelled",
100
+ "AgentEventComplete",
101
+ "AgentEventError",
102
+ "AgentEventStatus",
103
+ "AgentEventThinking",
104
+ "AgentEventToken",
105
+ "AgentEventToolCall",
106
+ "AgentEventToolResult",
107
+ "AgentOptions",
108
+ "AgentTurnMetadata",
109
+ "AgentTurnEnvelope",
110
+ "AgentTurnResult",
111
+ "AgentTurnState",
112
+ "ApprovalDecision",
113
+ "RuntimeEvent",
114
+ "RuntimeTrace",
115
+ "RunEventRecord",
116
+ "RunNotFoundError",
117
+ "RunRecord",
118
+ "RunStatus",
119
+ "RunStore",
120
+ "InvalidRunTransition",
121
+ "CheckpointConflictError",
122
+ "CheckpointFile",
123
+ "CheckpointNotFoundError",
124
+ "CheckpointRecord",
125
+ "CheckpointStore",
126
+ "RestoreResult",
127
+ "WorktreeError",
128
+ "WorktreeApplyResult",
129
+ "WorktreeManager",
130
+ "WorktreeSpec",
131
+ "ACTIVE_RUN_STATUSES",
132
+ "TERMINAL_RUN_STATUSES",
133
+ "ToolCallRecord",
134
+ "TurnResultRecord",
135
+ "ToolBatchState",
136
+ "ToolExecutionActivity",
137
+ "ToolExecutionTurnResult",
138
+ "ToolCallTask",
139
+ "ToolExecutor",
140
+ "ToolTurnPlan",
141
+ "apply_approval_decision",
142
+ "build_next_turn_messages",
143
+ "build_tool_followup",
144
+ "collect_parallel_done",
145
+ "detect_task_complete",
146
+ "execute_tool_turn",
147
+ "LoopGuard",
148
+ "record_tool_result",
149
+ "run_agent",
150
+ "run_parallel_tools",
151
+ "run_serial_tool",
152
+ "split_tool_calls",
153
+ "can_transition",
154
+ "is_terminal",
155
+ "require_transition",
156
+ "check_tool_policy",
157
+ "add_to_policy",
158
+ "remove_from_policy",
159
+ "load_tool_policy",
160
+ "SUBAGENT_TOOLS",
161
+ "SUBAGENT_SCHEMAS",
162
+ "tool_spawn_task",
163
+ "tool_task_status",
164
+ "tool_task_result",
165
+ "tool_task_list",
166
+ "tool_task_cancel",
167
+ "register_runner",
168
+ "restore_tasks",
169
+ ]
@@ -0,0 +1,481 @@
1
+ """The acceptance gate — making "done" a claim that was executed.
2
+
3
+ Why this exists
4
+ ---------------
5
+ Aria could already *suggest* verification. ``VerificationPlanner`` infers the
6
+ right checks from what changed, ``write_file`` attaches the inferred command to
7
+ its result as ``suggested_verification``, and ``/verify`` runs the plan when a
8
+ human asks for it. Every one of those paths ends with a suggestion. Nothing
9
+ in the loop ever ran the check, read the failure, or refused to let the turn
10
+ finish; the model announced "已完成" and the turn ended, verified or not.
11
+
12
+ That is the whole distance between an agent that writes code and an agent you
13
+ can hand a repository to. The strong coding agents are not better at emitting
14
+ a patch — they are structurally unable to stop while the check is red. The
15
+ loop, not the model, is what holds that line, which is exactly why this belongs
16
+ in the runtime and works the same whether the turn is driven by a 7B local
17
+ model or a frontier one.
18
+
19
+ What it does
20
+ ------------
21
+ The gate arms itself when a turn actually mutates a file on disk, and fires at
22
+ the moment the model stops requesting tools — the point where the loop would
23
+ otherwise ``break`` and report success. It runs the inferred checks; if they
24
+ pass, the turn ends as it always did, now carrying evidence. If they fail, the
25
+ loop gets a repair directive built from the *failing* output — trimmed, with
26
+ ``file:line`` anchors pulled out — and continues, so the model repairs against
27
+ a real signal instead of its own recollection of what it wrote.
28
+
29
+ Three constraints keep it from becoming a tax:
30
+
31
+ - **Read-only turns never pay it.** Arming requires an applied write. A
32
+ market question, a code review, a staged-but-unapplied edit: no checks run.
33
+ - **It is bounded.** After ``max_attempts`` failed rounds the gate stops
34
+ re-arming and the turn ends *honestly* — ``verified: false`` with the
35
+ failing command in the result — rather than looping until the budget dies.
36
+ An unverified answer that says so is useful; an infinite loop is not.
37
+ - **It never invents a sandbox.** Commands go through the caller's own
38
+ runner (the CLI passes ``run_command``, with its policy, permission mode
39
+ and approval path intact), so the gate cannot execute anything the user's
40
+ current mode would not already allow.
41
+ """
42
+
43
+ from __future__ import annotations
44
+
45
+ import inspect
46
+ import re
47
+ import time
48
+ from dataclasses import dataclass, field
49
+ from pathlib import Path
50
+ from typing import Any, Awaitable, Callable, Dict, Iterable, List, Optional, Sequence, Union
51
+
52
+ __all__ = [
53
+ "AcceptanceGate",
54
+ "AcceptanceReport",
55
+ "CheckResult",
56
+ "DEFAULT_MUTATING_TOOLS",
57
+ "extract_mutated_paths",
58
+ ]
59
+
60
+
61
+ # Tools whose success means bytes changed on disk. ``run_command`` is
62
+ # deliberately absent: it can mutate, but gating on it would re-arm the gate
63
+ # with every check the gate itself runs — the checks would never terminate.
64
+ DEFAULT_MUTATING_TOOLS = frozenset({
65
+ "write_file",
66
+ "edit_file",
67
+ "multi_edit",
68
+ "apply_patch",
69
+ "notebook_edit",
70
+ "str_replace",
71
+ "create_file",
72
+ })
73
+
74
+ # A failing test log is the single most context-hungry payload in a coding
75
+ # turn — a full pytest run can exceed the window on its own. Head carries the
76
+ # collection error, tail carries the assertion and the summary line; the middle
77
+ # is almost always repeated passing output.
78
+ _HEAD_LINES = 20
79
+ _TAIL_LINES = 45
80
+ _MAX_CHARS = 6000
81
+
82
+ # ``File "x.py", line 12`` (Python), ``x.ts:12:5`` (tsc/eslint/jest).
83
+ _PY_ANCHOR = re.compile(r'File "([^"]+)", line (\d+)')
84
+ _COLON_ANCHOR = re.compile(r"(?m)^\s*([\w./\\-]+\.[A-Za-z]{1,4}):(\d+)(?::\d+)?")
85
+
86
+
87
+ def _as_dict(value: Any) -> Dict[str, Any]:
88
+ return value if isinstance(value, dict) else {}
89
+
90
+
91
+ def extract_mutated_paths(
92
+ tool: str,
93
+ result: Any,
94
+ *,
95
+ mutating_tools: Iterable[str] = DEFAULT_MUTATING_TOOLS,
96
+ ) -> tuple[str, ...]:
97
+ """Paths this tool result actually wrote to disk.
98
+
99
+ Returns nothing for a failed call and nothing for a *staged* change. The
100
+ distinction matters: ``write_file`` in review mode returns success with
101
+ ``applied: False`` and the file on disk is untouched, so running the test
102
+ suite would verify the previous state and report a green that means
103
+ nothing.
104
+ """
105
+ canonical = str(tool or "").rsplit("__", 1)[-1]
106
+ if canonical not in set(mutating_tools):
107
+ return ()
108
+
109
+ payload = _as_dict(result)
110
+ if payload.get("success") is False or payload.get("error"):
111
+ return ()
112
+
113
+ data = _as_dict(payload.get("data")) or payload
114
+ if data.get("applied") is False or data.get("staged") is True:
115
+ return ()
116
+
117
+ found: List[str] = []
118
+
119
+ def _add(value: Any) -> None:
120
+ text = str(value or "").strip()
121
+ if text and text not in found:
122
+ found.append(text)
123
+
124
+ _add(data.get("path"))
125
+ _add(data.get("file_path"))
126
+ for entry in data.get("paths", ()) or ():
127
+ _add(entry)
128
+ # multi_edit reports per-edit records rather than a single path.
129
+ for entry in data.get("edits", ()) or ():
130
+ _add(_as_dict(entry).get("path"))
131
+
132
+ return tuple(found)
133
+
134
+
135
+ def _trim(text: str) -> str:
136
+ """Head + tail of command output, with the uninformative middle dropped."""
137
+ body = (text or "").strip()
138
+ if not body:
139
+ return ""
140
+ lines = body.splitlines()
141
+ if len(lines) > _HEAD_LINES + _TAIL_LINES:
142
+ omitted = len(lines) - _HEAD_LINES - _TAIL_LINES
143
+ lines = (
144
+ lines[:_HEAD_LINES]
145
+ + [f"… [{omitted} lines omitted] …"]
146
+ + lines[-_TAIL_LINES:]
147
+ )
148
+ joined = "\n".join(lines)
149
+ if len(joined) > _MAX_CHARS:
150
+ keep = _MAX_CHARS // 2
151
+ joined = f"{joined[:keep]}\n… [truncated] …\n{joined[-keep:]}"
152
+ return joined
153
+
154
+
155
+ def _anchors(text: str, limit: int = 5) -> tuple[str, ...]:
156
+ """``file:line`` locations named in a failure, most recent frame last.
157
+
158
+ Handing the model the anchors separately is what turns a wall of log into
159
+ an edit: without them a weaker model tends to rewrite the whole file
160
+ instead of patching the line the traceback already identified.
161
+ """
162
+ out: List[str] = []
163
+ for match in _PY_ANCHOR.finditer(text or ""):
164
+ entry = f"{match.group(1)}:{match.group(2)}"
165
+ if entry not in out:
166
+ out.append(entry)
167
+ for match in _COLON_ANCHOR.finditer(text or ""):
168
+ entry = f"{match.group(1)}:{match.group(2)}"
169
+ if entry not in out:
170
+ out.append(entry)
171
+ # The last frames are the ones inside the code that just changed; the first
172
+ # are usually the runner's own stack.
173
+ return tuple(out[-limit:])
174
+
175
+
176
+ @dataclass(frozen=True)
177
+ class CheckResult:
178
+ """One verification command, run."""
179
+
180
+ command: str
181
+ exit_code: int
182
+ passed: bool
183
+ output: str = ""
184
+ duration: float = 0.0
185
+ error: str = ""
186
+
187
+ @property
188
+ def anchors(self) -> tuple[str, ...]:
189
+ return () if self.passed else _anchors(self.output)
190
+
191
+ def summary(self) -> dict:
192
+ return {
193
+ "command": self.command,
194
+ "exit_code": self.exit_code,
195
+ "passed": self.passed,
196
+ "duration": round(self.duration, 3),
197
+ "error": self.error,
198
+ }
199
+
200
+
201
+ @dataclass(frozen=True)
202
+ class AcceptanceReport:
203
+ """The outcome of one pass of the gate."""
204
+
205
+ attempt: int
206
+ checks: tuple[CheckResult, ...] = ()
207
+ reason: str = ""
208
+ paths: tuple[str, ...] = ()
209
+ attempts_remaining: int = 0
210
+
211
+ @property
212
+ def ran(self) -> bool:
213
+ return bool(self.checks)
214
+
215
+ @property
216
+ def passed(self) -> bool:
217
+ """True only when at least one check ran and every check passed.
218
+
219
+ A gate that reports "verified" without running anything is worse than
220
+ no gate: it launders an unchecked claim into a checked-looking one.
221
+ """
222
+ return self.ran and all(check.passed for check in self.checks)
223
+
224
+ @property
225
+ def failures(self) -> tuple[CheckResult, ...]:
226
+ return tuple(check for check in self.checks if not check.passed)
227
+
228
+ def headline(self) -> str:
229
+ if not self.ran:
230
+ return "验收未执行:未推断出检查命令"
231
+ if self.passed:
232
+ names = ", ".join(check.command for check in self.checks)
233
+ return f"验收通过 ({names})"
234
+ first = self.failures[0]
235
+ return f"验收失败:`{first.command}` exit {first.exit_code}"
236
+
237
+ def repair_directive(self) -> str:
238
+ """What the model is told when the check came back red.
239
+
240
+ Written as evidence plus one instruction. The failing output is the
241
+ evidence; the instruction exists because the observed failure mode is
242
+ not that the model cannot fix the bug — it is that it re-announces
243
+ completion without having looked at why the command failed.
244
+ """
245
+ blocks: List[str] = [
246
+ "## 验收未通过 (Acceptance check failed)",
247
+ "",
248
+ "你声明任务已完成,但改动过的文件没有通过自动验收检查。"
249
+ "以下是**实际执行**的命令和真实输出:",
250
+ "",
251
+ ]
252
+ for check in self.failures:
253
+ blocks.append(f"### `{check.command}` → exit {check.exit_code}")
254
+ if check.error:
255
+ blocks.append(f"(执行错误:{check.error})")
256
+ body = _trim(check.output)
257
+ if body:
258
+ blocks.append(f"```\n{body}\n```")
259
+ if check.anchors:
260
+ blocks.append("定位:" + ", ".join(f"`{a}`" for a in check.anchors))
261
+ blocks.append("")
262
+
263
+ blocks.append(
264
+ "请依次:(1) 读出错位置的代码确认原因,(2) 用 `edit_file` 定点修补"
265
+ "——不要整文件重写,(3) 修好后直接说明修了什么。"
266
+ "修改后验收会自动重跑,你**不需要**自己调用验收命令。"
267
+ )
268
+ if self.attempts_remaining <= 0:
269
+ blocks.append(
270
+ "注意:这是最后一次修复机会。若仍未通过,请如实说明失败原因,"
271
+ "不要声称任务已完成。"
272
+ )
273
+ else:
274
+ blocks.append(f"剩余自动重试次数:{self.attempts_remaining}。")
275
+ return "\n".join(blocks)
276
+
277
+ def summary(self) -> dict:
278
+ return {
279
+ "attempt": self.attempt,
280
+ "passed": self.passed,
281
+ "ran": self.ran,
282
+ "reason": self.reason,
283
+ "paths": list(self.paths),
284
+ "checks": [check.summary() for check in self.checks],
285
+ }
286
+
287
+
288
+ # ``(command) -> result dict`` — sync or async. The result is read loosely
289
+ # because every surface in this codebase wraps command output slightly
290
+ # differently; see ``_read_run_result``.
291
+ CommandRunner = Callable[[str], Union[dict, Awaitable[dict]]]
292
+
293
+
294
+ def _read_run_result(raw: Any) -> tuple[int, str, str]:
295
+ """Normalise a runner result into ``(exit_code, output, error)``.
296
+
297
+ Deliberately permissive. ``tool_run_command`` returns
298
+ ``{"success": True, "data": {"exit_code": .., "stdout": .., "stderr": ..}}``
299
+ on a completed command and ``{"success": False, "error": "…timed out"}``
300
+ when it never completed — and a timeout is a *failed* check, not a passing
301
+ one, so an unreadable result must never be read as exit 0.
302
+ """
303
+ payload = _as_dict(raw)
304
+ if not payload:
305
+ return 1, "", "runner returned no result"
306
+
307
+ if payload.get("success") is False:
308
+ return 1, str(payload.get("output") or ""), str(payload.get("error") or "command failed")
309
+
310
+ data = _as_dict(payload.get("data")) or payload
311
+ for key in ("exit_code", "returncode", "code", "status"):
312
+ if key in data:
313
+ try:
314
+ exit_code = int(data[key])
315
+ break
316
+ except (TypeError, ValueError):
317
+ continue
318
+ else:
319
+ exit_code = 0
320
+
321
+ stdout = str(data.get("stdout") or data.get("output") or "")
322
+ stderr = str(data.get("stderr") or "")
323
+ output = "\n".join(part for part in (stdout, stderr) if part.strip())
324
+ return exit_code, output, ""
325
+
326
+
327
+ class AcceptanceGate:
328
+ """Arms on real writes, fires when the model says it is done."""
329
+
330
+ def __init__(
331
+ self,
332
+ *,
333
+ runner: CommandRunner,
334
+ root: Union[str, Path] = ".",
335
+ planner: Any = None,
336
+ max_attempts: int = 2,
337
+ mutating_tools: Iterable[str] = DEFAULT_MUTATING_TOOLS,
338
+ enabled: bool = True,
339
+ commands: Optional[Sequence[str]] = None,
340
+ ) -> None:
341
+ self.root = Path(root).expanduser()
342
+ self.runner = runner
343
+ self.max_attempts = max(0, int(max_attempts))
344
+ self.mutating_tools = frozenset(mutating_tools)
345
+ self.enabled = bool(enabled)
346
+ # An explicit command list overrides inference — this is the hook a
347
+ # domain pack or a repo's own config uses to say what "green" means
348
+ # for it (``make check``, ``bazel test //...``), which inference can
349
+ # never guess.
350
+ self.commands = tuple(commands or ())
351
+ self._planner = planner
352
+ self.attempts = 0
353
+ self.reports: List[AcceptanceReport] = []
354
+ self._pending: List[str] = []
355
+ self._all_paths: List[str] = []
356
+
357
+ # ── arming ────────────────────────────────────────────────────────────
358
+
359
+ def record_tool(self, tool: str, result: Any) -> tuple[str, ...]:
360
+ """Note one tool result; arms the gate if it wrote to disk."""
361
+ if not self.enabled:
362
+ return ()
363
+ paths = extract_mutated_paths(tool, result, mutating_tools=self.mutating_tools)
364
+ for path in paths:
365
+ if path not in self._pending:
366
+ self._pending.append(path)
367
+ if path not in self._all_paths:
368
+ self._all_paths.append(path)
369
+ return paths
370
+
371
+ @property
372
+ def armed(self) -> bool:
373
+ return bool(self._pending)
374
+
375
+ @property
376
+ def exhausted(self) -> bool:
377
+ return self.attempts >= self.max_attempts
378
+
379
+ def should_run(self) -> bool:
380
+ return self.enabled and self.armed and not self.exhausted
381
+
382
+ # ── firing ────────────────────────────────────────────────────────────
383
+
384
+ def _plan(self, paths: Sequence[str]) -> tuple[tuple[str, ...], str]:
385
+ if self.commands:
386
+ return self.commands, "configured acceptance commands"
387
+ planner = self._planner
388
+ if planner is None:
389
+ try:
390
+ from aria_code.workspace.verify import VerificationPlanner
391
+ except Exception:
392
+ return (), "verification planner unavailable"
393
+ planner = VerificationPlanner(root=self.root)
394
+ self._planner = planner
395
+ try:
396
+ plan = planner.infer(list(paths))
397
+ except Exception as exc: # a planner fault must not fail the turn
398
+ return (), f"verification planning failed: {exc}"
399
+ return tuple(plan.commands or ()), str(plan.reason or "")
400
+
401
+ async def _run_one(self, command: str) -> CheckResult:
402
+ started = time.time()
403
+ try:
404
+ raw = self.runner(command)
405
+ if inspect.isawaitable(raw):
406
+ raw = await raw
407
+ except Exception as exc:
408
+ return CheckResult(
409
+ command=command,
410
+ exit_code=1,
411
+ passed=False,
412
+ error=str(exc),
413
+ duration=time.time() - started,
414
+ )
415
+ exit_code, output, error = _read_run_result(raw)
416
+ return CheckResult(
417
+ command=command,
418
+ exit_code=exit_code,
419
+ passed=exit_code == 0 and not error,
420
+ output=output,
421
+ error=error,
422
+ duration=time.time() - started,
423
+ )
424
+
425
+ async def run(self) -> Optional[AcceptanceReport]:
426
+ """Run the inferred checks once. ``None`` when the gate is not armed.
427
+
428
+ Disarms before running: the paths this pass covers are consumed, so a
429
+ turn that fixes nothing cannot re-fire the same checks forever, while
430
+ a repair that writes a file re-arms the gate for the next pass.
431
+ """
432
+ if not self.should_run():
433
+ return None
434
+
435
+ paths = tuple(self._pending)
436
+ self._pending.clear()
437
+ self.attempts += 1
438
+
439
+ commands, reason = self._plan(paths)
440
+ checks: List[CheckResult] = []
441
+ for command in commands:
442
+ check = await self._run_one(command)
443
+ checks.append(check)
444
+ # Stop at the first red. Later checks usually fail for the same
445
+ # cause, and each extra failing log costs context the repair needs.
446
+ if not check.passed:
447
+ break
448
+
449
+ report = AcceptanceReport(
450
+ attempt=self.attempts,
451
+ checks=tuple(checks),
452
+ reason=reason,
453
+ paths=paths,
454
+ attempts_remaining=max(0, self.max_attempts - self.attempts),
455
+ )
456
+ self.reports.append(report)
457
+ return report
458
+
459
+ # ── reporting ─────────────────────────────────────────────────────────
460
+
461
+ def summary(self) -> dict:
462
+ """What the turn result carries so a caller can prove the claim.
463
+
464
+ ``verified`` is tri-state on purpose. ``True`` means checks ran green,
465
+ ``False`` means they ran red, and ``None`` means nothing was verified —
466
+ a read-only turn, or a workspace with no inferable check. Collapsing
467
+ the last two into ``False`` would make every analysis turn look failed;
468
+ collapsing it into ``True`` would be the exact lie this module exists
469
+ to prevent.
470
+ """
471
+ last = self.reports[-1] if self.reports else None
472
+ verified: Optional[bool] = None
473
+ if last is not None and last.ran:
474
+ verified = last.passed
475
+ return {
476
+ "verified": verified,
477
+ "attempts": self.attempts,
478
+ "paths": list(self._all_paths),
479
+ "reports": [report.summary() for report in self.reports],
480
+ "headline": last.headline() if last is not None else "",
481
+ }