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,726 @@
1
+ """System-level tools: run_command, web_fetch, github.
2
+
3
+ All functions are pure (no module-level globals). Console output is
4
+ injected via keyword args so aria_cli.py thin wrappers supply the
5
+ Rich console and global state defaults.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import os
11
+ import pathlib
12
+ import re
13
+ import shlex
14
+ import subprocess
15
+ import sys
16
+ from datetime import datetime
17
+ from pathlib import Path
18
+
19
+ _ROOT = Path(__file__).parent.parent.parent.parent # aria-code/
20
+ if str(_ROOT) not in sys.path:
21
+ sys.path.insert(0, str(_ROOT))
22
+
23
+ from aria_code.safety import evaluate_command_policy # noqa: E402
24
+
25
+
26
+ def _persist_command_output(command: str, stdout: str, stderr: str, returncode: int) -> dict:
27
+ """Persist full command output when it is too large for inline tool context."""
28
+ stdout_lines = stdout.splitlines()
29
+ stderr_lines = stderr.splitlines()
30
+ if (
31
+ len(stdout) <= 2500
32
+ and len(stderr) <= 1200
33
+ and len(stdout_lines) <= 18
34
+ and len(stderr_lines) <= 8
35
+ ):
36
+ return {}
37
+ try:
38
+ from aria_code.artifacts import create_artifact, write_artifact_metadata
39
+
40
+ record = create_artifact(
41
+ "command-output",
42
+ "shell",
43
+ "command_output",
44
+ ".txt",
45
+ timestamp=datetime.now(),
46
+ )
47
+ text = (
48
+ f"$ {command}\n"
49
+ f"exit_code={returncode}\n\n"
50
+ "===== STDOUT =====\n"
51
+ f"{stdout}"
52
+ "\n\n===== STDERR =====\n"
53
+ f"{stderr}"
54
+ )
55
+ record.path.write_text(text, encoding="utf-8", errors="replace")
56
+ write_artifact_metadata(record, {
57
+ "kind": "command_output",
58
+ "status": "complete" if returncode == 0 else "failed",
59
+ "created_at": datetime.now().isoformat(timespec="seconds"),
60
+ "command": command,
61
+ "exit_code": returncode,
62
+ "stdout_chars": len(stdout),
63
+ "stderr_chars": len(stderr),
64
+ "stdout_truncated_inline": len(stdout) > 5000,
65
+ "stderr_truncated_inline": len(stderr) > 2000,
66
+ })
67
+ return {"full_output_path": str(record.path)}
68
+ except Exception:
69
+ return {}
70
+
71
+
72
+ def _cprint(msg: str, *, console, has_rich: bool) -> None:
73
+ if has_rich and console is not None:
74
+ console.print(msg)
75
+ else:
76
+ plain = re.sub(r"\[/?[^\]]+\]", "", msg)
77
+ print(plain)
78
+
79
+
80
+ def tool_run_command(
81
+ params: dict,
82
+ *,
83
+ console=None,
84
+ has_rich: bool = True,
85
+ ) -> dict:
86
+ """Run a shell command and return output.
87
+
88
+ ``params`` should contain ``permission_mode`` and ``network_enabled``
89
+ (filled in by the aria_cli.py wrapper from the active globals).
90
+ """
91
+ command = params.get("command", "")
92
+ # LLMs sometimes send command as a list e.g. ['bash', '-lc', '...'] — normalize to string
93
+ if isinstance(command, list):
94
+ import shlex as _shlex
95
+ command = _shlex.join(str(c) for c in command)
96
+ params["command"] = command
97
+ if not command:
98
+ return {"success": False, "error": "Missing 'command' parameter"}
99
+
100
+ effective_policy = params.get("policy", "safe")
101
+ if params.get("user_approved") and effective_policy == "safe":
102
+ effective_policy = "balanced"
103
+
104
+ decision = evaluate_command_policy(
105
+ command,
106
+ effective_policy,
107
+ # "safe" is a *policy* name, not a mode — it never matched
108
+ # PermissionMode and silently resolved to workspace-write. Both
109
+ # executors supply a real mode, so this default is only reached
110
+ # by a caller that bypasses them; read-only is the right answer
111
+ # there.
112
+ mode=params.get("permission_mode", "read-only"),
113
+ network_enabled=bool(params.get("network_enabled", True)),
114
+ )
115
+ command = decision.normalized_command
116
+
117
+ dangerous = ["rm -rf /", "mkfs", "dd if=", "> /dev/", ":(){ :", "fork bomb"]
118
+ for d in dangerous:
119
+ if d in command:
120
+ return {"success": False, "error": f"Blocked dangerous command: {command}"}
121
+
122
+ # Prevent executing text/doc files as Python — they are analysis reports, not scripts
123
+ import re as _re_cmd
124
+ _py3_file = _re_cmd.search(r'\bpython3?\s+["\']?(\S+\.(?:txt|md|docx|csv|json|log))', command)
125
+ if _py3_file:
126
+ _bad_file = _py3_file.group(1)
127
+ return {
128
+ "success": False,
129
+ "error": (
130
+ f"拒绝执行: '{_bad_file}' 是文本/分析文件,不是 Python 脚本。\n"
131
+ "如需展示分析结果,请直接输出文字,或将分析结论写入 .py 文件后执行。"
132
+ ),
133
+ }
134
+
135
+ if params.get("dry_run"):
136
+ return {"success": True, "data": {
137
+ "command": command,
138
+ "risk": decision.risk,
139
+ "policy": decision.policy,
140
+ "requires_approval": getattr(decision, "requires_approval", False),
141
+ "network": getattr(decision, "network", False),
142
+ "dry_run": True,
143
+ }}
144
+ if not decision.allowed:
145
+ return {"success": False, "error": decision.reason}
146
+ try:
147
+ cwd = params.get("cwd", None)
148
+ timeout = min(params.get("timeout", 120), 300)
149
+ use_shell = True
150
+ argv = None
151
+ if decision.risk == "low":
152
+ has_shell_meta = any(ch in command for ch in ["|", "&", ";", "<", ">", "$", "`", "\n"])
153
+ if not has_shell_meta:
154
+ try:
155
+ argv = shlex.split(command)
156
+ if argv:
157
+ use_shell = False
158
+ except ValueError:
159
+ use_shell = True
160
+ argv = None
161
+
162
+ sandbox = params.get("sandbox", False)
163
+ if sandbox:
164
+ try:
165
+ from aria_code.apps.cli.sandbox import run_in_docker_sandbox
166
+ result = run_in_docker_sandbox(
167
+ command=command,
168
+ cwd=cwd,
169
+ timeout=timeout,
170
+ image="python:3.11-slim"
171
+ )
172
+ except Exception as e:
173
+ return {"success": False, "error": f"Sandbox execution failed: {e}"}
174
+ else:
175
+ result = subprocess.run(
176
+ argv if (argv and not use_shell) else command,
177
+ shell=use_shell,
178
+ capture_output=True,
179
+ text=True,
180
+ timeout=timeout,
181
+ cwd=cwd,
182
+ )
183
+ full_stdout = result.stdout
184
+ full_stderr = result.stderr
185
+ output = full_stdout[-5000:] if len(full_stdout) > 5000 else full_stdout
186
+ stderr = full_stderr[-2000:] if len(full_stderr) > 2000 else full_stderr
187
+ output_artifact = _persist_command_output(command, full_stdout, full_stderr, result.returncode)
188
+
189
+ # ── Auto-fix loop (up to 3 rounds for python3 scripts) ──────────────
190
+ MAX_AUTO_FIX_ROUNDS = 3
191
+ _cmd_tail = (
192
+ command.strip().split("python3 ", 1)[-1].strip().split()
193
+ if command.strip().startswith("python3 ") else []
194
+ )
195
+ if result.returncode != 0 and _cmd_tail:
196
+ script_path = _cmd_tail[0]
197
+ script_p = pathlib.Path(script_path).expanduser().resolve()
198
+
199
+ for _fix_round in range(MAX_AUTO_FIX_ROUNDS):
200
+ combined_err = (output + " " + stderr).strip()
201
+ auto_fixed = False
202
+
203
+ if not (script_p.exists() and script_p.suffix == ".py"):
204
+ break
205
+ script_content = script_p.read_text(errors="replace")
206
+
207
+ name_match = re.search(r"NameError: name ['\"](\w+)['\"] is not defined", combined_err)
208
+ if name_match and not auto_fixed:
209
+ missing = name_match.group(1)
210
+ import_map = {
211
+ "os": "import os", "sys": "import sys", "re": "import re",
212
+ "json": "import json", "math": "import math", "time": "import time",
213
+ "np": "import numpy as np", "pd": "import pandas as pd",
214
+ "yf": "import yfinance as yf", "plt": "import matplotlib.pyplot as plt",
215
+ "mpf": "import mplfinance as mpf",
216
+ "datetime": "from datetime import datetime, timedelta",
217
+ "Path": "from pathlib import Path",
218
+ "timedelta": "from datetime import datetime, timedelta",
219
+ "go": "import plotly.graph_objects as go",
220
+ "px": "import plotly.express as px",
221
+ "ta": "import pandas_ta as ta", "warnings": "import warnings",
222
+ "make_subplots": "from plotly.subplots import make_subplots",
223
+ "bt": "import backtrader as bt", "vbt": "import vectorbt as vbt",
224
+ "ccxt": "import ccxt", "requests": "import requests",
225
+ "BeautifulSoup": "from bs4 import BeautifulSoup",
226
+ "tqdm": "from tqdm import tqdm",
227
+ "xgb": "import xgboost as xgb",
228
+ "Prophet": "from prophet import Prophet",
229
+ "arch": "from arch import arch_model",
230
+ "statsmodels": "import statsmodels.api as sm",
231
+ "sm": "import statsmodels.api as sm",
232
+ }
233
+ fix_import = import_map.get(missing)
234
+ if fix_import and fix_import not in script_content:
235
+ lines = script_content.split("\n")
236
+ insert_at = 0
237
+ for i, l in enumerate(lines):
238
+ if l.strip().startswith("#!") or l.strip().startswith("# -*-"):
239
+ insert_at = i + 1
240
+ else:
241
+ break
242
+ lines.insert(insert_at, fix_import)
243
+ if missing == "plt" and "matplotlib.use" not in script_content:
244
+ lines.insert(insert_at, "import matplotlib; matplotlib.use('Agg')")
245
+ script_p.write_text("\n".join(lines))
246
+ auto_fixed = True
247
+ _cprint(
248
+ f" [#C08050]Auto-fix[{_fix_round+1}/{MAX_AUTO_FIX_ROUNDS}]:"
249
+ f"[/#C08050] [dim]added '{fix_import}'[/dim]",
250
+ console=console, has_rich=has_rich,
251
+ )
252
+
253
+ if not auto_fixed and (
254
+ "cannot be resolved at runtime" in combined_err.lower()
255
+ or ("matplotlib" in combined_err and "backend" in combined_err.lower())
256
+ ):
257
+ if "matplotlib.use" not in script_content and "matplotlib.pyplot" in script_content:
258
+ script_content = script_content.replace(
259
+ "import matplotlib.pyplot as plt",
260
+ "import matplotlib; matplotlib.use('Agg')\nimport matplotlib.pyplot as plt",
261
+ )
262
+ script_p.write_text(script_content)
263
+ auto_fixed = True
264
+ _cprint(
265
+ f" [#C08050]Auto-fix[{_fix_round+1}]:[/#C08050]"
266
+ " [dim]added matplotlib.use('Agg')[/dim]",
267
+ console=console, has_rich=has_rich,
268
+ )
269
+
270
+ key_match = re.search(
271
+ r"KeyError: ['\"]?(Close|Open|High|Low|Volume|Adj Close)", combined_err
272
+ )
273
+ if key_match and not auto_fixed and "yfinance" in script_content:
274
+ if "columns.droplevel" not in script_content:
275
+ fix_line = (
276
+ "\n# Fix yfinance MultiIndex columns\n"
277
+ "if isinstance(df.columns, pd.MultiIndex):\n"
278
+ " df.columns = df.columns.droplevel(1)\n"
279
+ )
280
+ dl_match = re.search(r"(.*=\s*yf\.download\([^)]+\))", script_content)
281
+ if dl_match:
282
+ script_content = script_content.replace(
283
+ dl_match.group(0), dl_match.group(0) + fix_line
284
+ )
285
+ script_p.write_text(script_content)
286
+ auto_fixed = True
287
+ _cprint(
288
+ f" [#C08050]Auto-fix[{_fix_round+1}]:[/#C08050]"
289
+ " [dim]MultiIndex column fix[/dim]",
290
+ console=console, has_rich=has_rich,
291
+ )
292
+
293
+ attr_match = re.search(
294
+ r"AttributeError: '(\w+)' object has no attribute '(\w+)'", combined_err
295
+ )
296
+ if attr_match and not auto_fixed:
297
+ obj_type, attr_name = attr_match.group(1), attr_match.group(2)
298
+ if obj_type == "DataFrame" and attr_name == "append":
299
+ script_content = re.sub(
300
+ r"(\w+)\.append\(([^)]+)\)",
301
+ r"pd.concat([\1, \2], ignore_index=True)",
302
+ script_content,
303
+ )
304
+ script_p.write_text(script_content)
305
+ auto_fixed = True
306
+ _cprint(
307
+ f" [#C08050]Auto-fix[{_fix_round+1}]:[/#C08050]"
308
+ " [dim]DataFrame.append→pd.concat[/dim]",
309
+ console=console, has_rich=has_rich,
310
+ )
311
+
312
+ if not auto_fixed and "TypeError" in combined_err:
313
+ if "auto_adjust" in combined_err and "auto_adjust" in script_content:
314
+ script_content = re.sub(
315
+ r",\s*auto_adjust\s*=\s*(True|False)", "", script_content
316
+ )
317
+ script_p.write_text(script_content)
318
+ auto_fixed = True
319
+ _cprint(
320
+ f" [#C08050]Auto-fix[{_fix_round+1}]:[/#C08050]"
321
+ " [dim]removed deprecated auto_adjust param[/dim]",
322
+ console=console, has_rich=has_rich,
323
+ )
324
+
325
+ mod_match = re.search(r"No module named ['\"]?(\w+)", combined_err)
326
+ if mod_match and not auto_fixed:
327
+ missing_mod = mod_match.group(1)
328
+ pip_map = {
329
+ "mplfinance": "mplfinance", "plotly": "plotly",
330
+ "pandas_ta": "pandas_ta", "ta": "ta",
331
+ "sklearn": "scikit-learn", "cv2": "opencv-python",
332
+ "bs4": "beautifulsoup4", "PIL": "Pillow",
333
+ "backtrader": "backtrader", "vectorbt": "vectorbt",
334
+ "ccxt": "ccxt", "prophet": "prophet",
335
+ "arch": "arch", "xgboost": "xgboost",
336
+ "lightgbm": "lightgbm", "statsmodels": "statsmodels",
337
+ "akshare": "akshare", "tushare": "tushare",
338
+ "empyrical": "empyrical", "pyfolio": "pyfolio",
339
+ "seaborn": "seaborn", "openpyxl": "openpyxl",
340
+ }
341
+ pip_pkg = pip_map.get(missing_mod, missing_mod)
342
+ _cprint(
343
+ f" [#C08050]Auto-fix[{_fix_round+1}]:[/#C08050]"
344
+ f" [dim]pip3 install {pip_pkg}[/dim]",
345
+ console=console, has_rich=has_rich,
346
+ )
347
+ pip_result = subprocess.run(
348
+ f"pip3 install {pip_pkg}", shell=True, capture_output=True,
349
+ text=True, timeout=60,
350
+ )
351
+ if pip_result.returncode == 0:
352
+ auto_fixed = True
353
+
354
+ if auto_fixed:
355
+ _cprint(
356
+ f" [dim]Re-running after auto-fix (round {_fix_round+1}/{MAX_AUTO_FIX_ROUNDS})...[/dim]",
357
+ console=console, has_rich=has_rich,
358
+ )
359
+ result = subprocess.run(
360
+ command, shell=True, capture_output=True, text=True,
361
+ timeout=timeout, cwd=cwd,
362
+ )
363
+ full_stdout = result.stdout
364
+ full_stderr = result.stderr
365
+ output = full_stdout[-5000:] if len(full_stdout) > 5000 else full_stdout
366
+ stderr = full_stderr[-2000:] if len(full_stderr) > 2000 else full_stderr
367
+ output_artifact = _persist_command_output(
368
+ command, full_stdout, full_stderr, result.returncode
369
+ )
370
+ if result.returncode == 0:
371
+ break
372
+ else:
373
+ break
374
+ # ── End auto-fix ─────────────────────────────────────────────────────
375
+
376
+ if has_rich and console is not None:
377
+ if result.returncode == 0:
378
+ console.print(f" [green]Command completed[/green] [dim](exit {result.returncode})[/dim]")
379
+ else:
380
+ console.print(f" [dim]Command exited {result.returncode}[/dim]")
381
+ out_preview = output.strip().splitlines()[:6]
382
+ for ol in out_preview:
383
+ console.print(f" [dim]{ol[:120]}[/dim]")
384
+ if len(output.strip().splitlines()) > 6:
385
+ console.print(" [dim]...truncated[/dim]")
386
+ if output_artifact.get("full_output_path"):
387
+ console.print(" [dim]full output saved[/dim]")
388
+ if stderr.strip() and result.returncode != 0:
389
+ for el in stderr.strip().splitlines()[:3]:
390
+ console.print(f" [red]{el[:120]}[/red]")
391
+ else:
392
+ print(f" Command exit: {result.returncode}")
393
+ data = {
394
+ "command": command, "exit_code": result.returncode,
395
+ "stdout": output, "stderr": stderr,
396
+ "stdout_truncated": len(full_stdout) > 5000,
397
+ "stderr_truncated": len(full_stderr) > 2000,
398
+ }
399
+ data.update(output_artifact)
400
+ return {"success": True, "data": data}
401
+ except subprocess.TimeoutExpired:
402
+ return {"success": False, "error": f"Command timed out ({timeout}s)"}
403
+ except KeyboardInterrupt:
404
+ _cprint(" [dim]Command interrupted[/dim]", console=console, has_rich=has_rich)
405
+ return {"success": False, "error": "Command interrupted by user (Ctrl+C)"}
406
+ except Exception as e:
407
+ return {"success": False, "error": str(e)}
408
+
409
+
410
+ def tool_web_fetch(params: dict) -> dict:
411
+ """Fetch the text content of any URL."""
412
+ url = params.get("url", "").strip()
413
+ if not url:
414
+ return {"success": False, "error": "Missing 'url' parameter"}
415
+ if not url.startswith(("http://", "https://")):
416
+ url = "https://" + url
417
+ max_chars = min(int(params.get("max_chars", 4000)), 12000)
418
+ timeout = min(int(params.get("timeout", 15)), 30)
419
+ try:
420
+ import urllib.request as _ur
421
+ import ssl as _ssl
422
+ _prx = _ur.getproxies()
423
+ # SEC EDGAR rejects browser-spoofed UAs from scripts (403) and requires
424
+ # a declarative UA with contact info — the root cause of "verify via
425
+ # 10-Q" tasks silently degrading to aggregator sites. Honor SEC's rule
426
+ # for sec.gov; keep a browser UA for sites that block obvious bots.
427
+ from urllib.parse import urlparse as _urlparse
428
+ _host = (_urlparse(url).hostname or "").lower()
429
+ if _host == "sec.gov" or _host.endswith(".sec.gov"):
430
+ _contact = os.environ.get("ARIA_SEC_CONTACT", "").strip() or "research@aria-code.local"
431
+ _ua = f"aria-code research client ({_contact})"
432
+ else:
433
+ _ua = (
434
+ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
435
+ "AppleWebKit/537.36 (KHTML, like Gecko) "
436
+ "Chrome/124.0 Safari/537.36"
437
+ )
438
+ headers = {
439
+ "User-Agent": _ua,
440
+ "Accept": "text/html,application/xhtml+xml,text/plain;q=0.9,*/*;q=0.8",
441
+ }
442
+ _gh_m = re.match(
443
+ r"https://github\.com/([^/]+/[^/]+)/blob/([^?#]+)", url
444
+ )
445
+ if _gh_m:
446
+ url = f"https://raw.githubusercontent.com/{_gh_m.group(1)}/{_gh_m.group(2)}"
447
+
448
+ import requests as _req
449
+ s = _req.Session()
450
+ s.proxies = _prx
451
+ # Certificate verification ON by default — research data fetched over
452
+ # an unverified channel is tamperable. ARIA_WEB_FETCH_INSECURE=1 is
453
+ # the explicit escape hatch for TLS-intercepting corporate proxies.
454
+ s.verify = os.environ.get("ARIA_WEB_FETCH_INSECURE", "") != "1"
455
+ r = s.get(url, headers=headers, timeout=timeout)
456
+ r.raise_for_status()
457
+ raw = r.text
458
+
459
+ ct = r.headers.get("content-type", "")
460
+ if "json" in ct or raw.lstrip().startswith(("{", "[")):
461
+ return {"success": True, "data": {
462
+ "url": url, "content_type": ct,
463
+ "text": raw[:max_chars], "length": len(raw),
464
+ }}
465
+
466
+ text = re.sub(r"<script[^>]*>.*?</script>", " ", raw, flags=re.DOTALL | re.I)
467
+ text = re.sub(r"<style[^>]*>.*?</style>", " ", text, flags=re.DOTALL | re.I)
468
+ text = re.sub(r"<[^>]+>", " ", text)
469
+ text = re.sub(r"&nbsp;", " ", text)
470
+ text = re.sub(r"&amp;", "&", text)
471
+ text = re.sub(r"&lt;", "<", text)
472
+ text = re.sub(r"&gt;", ">", text)
473
+ text = re.sub(r"&quot;", '"', text)
474
+ text = re.sub(r"\s{3,}", "\n", text)
475
+ text = text.strip()
476
+
477
+ return {"success": True, "data": {
478
+ "url": url, "content_type": ct,
479
+ "text": text[:max_chars], "length": len(text),
480
+ "truncated": len(text) > max_chars,
481
+ }}
482
+ except Exception as e:
483
+ return {"success": False, "error": f"web_fetch failed: {e}"}
484
+
485
+
486
+ def tool_github(
487
+ params: dict,
488
+ *,
489
+ console=None,
490
+ has_rich: bool = True,
491
+ ) -> dict:
492
+ """GitHub API / gh CLI integration.
493
+
494
+ actions: list_prs, list_issues, view_pr, view_issue, create_pr,
495
+ list_commits, read_file, search, pr_diff, pr_checks
496
+ """
497
+ action = params.get("action", "list_prs").lower().replace("-", "_")
498
+ cwd = params.get("cwd") or None
499
+ policy = "safe"
500
+
501
+ def _gh(cmd: str, timeout: int = 20) -> dict:
502
+ import shutil
503
+ if not shutil.which("gh"):
504
+ return {"success": False,
505
+ "error": "gh CLI not found. Install: brew install gh && gh auth login"}
506
+ return tool_run_command(
507
+ {"command": cmd, "cwd": cwd, "timeout": timeout, "policy": policy},
508
+ console=console,
509
+ has_rich=has_rich,
510
+ )
511
+
512
+ if action in ("list_prs", "prs", "pull_requests"):
513
+ state = params.get("state", "open")
514
+ limit = int(params.get("limit", 20))
515
+ return _gh(f"gh pr list --state {state} --limit {limit} "
516
+ "--json number,title,author,state,headRefName,url")
517
+
518
+ if action in ("list_issues", "issues"):
519
+ state = params.get("state", "open")
520
+ limit = int(params.get("limit", 20))
521
+ label = f' --label "{params["label"]}"' if params.get("label") else ""
522
+ return _gh(f"gh issue list --state {state} --limit {limit}{label} "
523
+ "--json number,title,author,state,labels,url")
524
+
525
+ if action in ("view_pr", "pr"):
526
+ number = params.get("number") or params.get("pr")
527
+ if not number:
528
+ return {"success": False, "error": "Missing 'number' parameter"}
529
+ return _gh(f"gh pr view {number} "
530
+ "--json number,title,body,state,headRefName,baseRefName,additions,deletions,files,url")
531
+
532
+ if action in ("view_issue", "issue"):
533
+ number = params.get("number") or params.get("issue")
534
+ if not number:
535
+ return {"success": False, "error": "Missing 'number' parameter"}
536
+ return _gh(f"gh issue view {number} "
537
+ "--json number,title,body,state,labels,comments,url")
538
+
539
+ if action == "create_pr":
540
+ title = params.get("title", "")
541
+ body = params.get("body", "")
542
+ branch = params.get("branch", "")
543
+ base = params.get("base", "main")
544
+ if not title:
545
+ return {"success": False, "error": "Missing 'title' for create_pr"}
546
+ b_flag = f"--head {shlex.quote(branch)}" if branch else ""
547
+ cmd = (
548
+ f"gh pr create --title {shlex.quote(title)} "
549
+ f"--body {shlex.quote(body)} "
550
+ f"--base {shlex.quote(base)} {b_flag}"
551
+ )
552
+ return _gh(cmd, timeout=30)
553
+
554
+ if action in ("list_commits", "commits", "log"):
555
+ limit = int(params.get("limit", 10))
556
+ return _gh(
557
+ f"gh api repos/{{owner}}/{{repo}}/commits?per_page={limit} "
558
+ "--jq '[.[] | {sha: .sha[:7], "
559
+ 'message: .commit.message | split("\\n")[0], '
560
+ "author: .commit.author.name, date: .commit.author.date}]'"
561
+ )
562
+
563
+ if action == "search":
564
+ q = params.get("q") or params.get("query", "")
565
+ kind = params.get("kind", "code")
566
+ if not q:
567
+ return {"success": False, "error": "Missing 'q' parameter"}
568
+ return _gh(f"gh search {kind} {shlex.quote(q)} --limit 10 "
569
+ "--json url,path,textMatches", timeout=15)
570
+
571
+ if action in ("read_file", "file"):
572
+ ref = params.get("ref", "")
573
+ file_path = params.get("path", "")
574
+ if ref:
575
+ m = re.match(r"([^@:]+)@([^:]+):(.+)", ref)
576
+ if m:
577
+ repo, branch, fp = m.groups()
578
+ url = f"https://raw.githubusercontent.com/{repo}/{branch}/{fp}"
579
+ return tool_web_fetch({"url": url, "max_chars": 20000})
580
+ if file_path:
581
+ return _gh(f"gh api repos/{{owner}}/{{repo}}/contents/{file_path} "
582
+ "--jq '.content' | base64 -d")
583
+ return {"success": False, "error": "Provide 'ref' (owner/repo@branch:path) or 'path'"}
584
+
585
+ if action in ("pr_diff", "diff"):
586
+ number = params.get("number") or params.get("pr")
587
+ if not number:
588
+ return {"success": False, "error": "Missing 'number' parameter"}
589
+ return _gh(f"gh pr diff {number}", timeout=30)
590
+
591
+ if action in ("pr_checks", "checks", "ci"):
592
+ number = params.get("number") or params.get("pr")
593
+ return _gh(f"gh pr checks {number or ''}")
594
+
595
+ if action in ("git_status", "status"):
596
+ return tool_run_command(
597
+ {"command": "git status --short && echo '---' && git log --oneline -5",
598
+ "cwd": cwd, "policy": policy},
599
+ console=console, has_rich=has_rich,
600
+ )
601
+
602
+ if action in ("commit_and_push", "commit", "push"):
603
+ message = params.get("message", "")
604
+ branch = params.get("branch", "")
605
+ add_files = params.get("files", [])
606
+ repo = params.get("repo", "") # e.g. "artherahq/aria-code"
607
+ coauthor = params.get("coauthor", "")
608
+
609
+ if not message:
610
+ return {"success": False, "error": "Missing 'message' for commit_and_push"}
611
+
612
+ # ── Aria Code[bot] identity via GitHub App ─────────────────────────────
613
+ try:
614
+ from aria_code.apps.cli.github_app_auth import (
615
+ get_installation_token, get_aria_git_url,
616
+ aria_bot_env, ARIA_BOT_NAME, ARIA_BOT_EMAIL,
617
+ )
618
+ owner = (repo.split("/")[0] if repo else None) or "artherahq"
619
+ token = get_installation_token(owner)
620
+ bot_env = aria_bot_env()
621
+ auth_available = True
622
+ except Exception as _auth_err:
623
+ # Fall back to local git credentials if App not configured
624
+ token = None
625
+ bot_env = {
626
+ "GIT_AUTHOR_NAME": "Aria Code",
627
+ "GIT_AUTHOR_EMAIL": "aria-code[bot]@artherahq.com",
628
+ "GIT_COMMITTER_NAME": "Aria Code",
629
+ "GIT_COMMITTER_EMAIL": "aria-code[bot]@artherahq.com",
630
+ }
631
+ auth_available = False
632
+ ARIA_BOT_NAME = "Aria Code"
633
+ ARIA_BOT_EMAIL = "aria-code[bot]@artherahq.com"
634
+
635
+ env_prefix = " ".join(f'{k}="{v}"' for k, v in bot_env.items()) + " "
636
+
637
+ # ── Build commit message with co-author attribution ────────────────────
638
+ body = message
639
+ if coauthor:
640
+ body += f"\n\nCo-Authored-By: {coauthor}"
641
+ # Always credit the Aria GitHub account so it appears in Contributors
642
+ if auth_available:
643
+ try:
644
+ from aria_code.apps.cli.github_app_auth import ARIA_GITHUB_LOGIN, ARIA_GITHUB_EMAIL
645
+ body += f"\n\nCo-Authored-By: {ARIA_GITHUB_LOGIN} <{ARIA_GITHUB_EMAIL}>"
646
+ except ImportError:
647
+ pass
648
+ user_name = tool_run_command({"command": "git config user.name", "policy": "safe", "cwd": cwd}).get("output", "").strip()
649
+ user_email = tool_run_command({"command": "git config user.email", "policy": "safe", "cwd": cwd}).get("output", "").strip()
650
+ if user_name and user_email and user_email != ARIA_BOT_EMAIL:
651
+ body += f"\n\nCo-Authored-By: {user_name} <{user_email}>"
652
+
653
+ # ── Stage ──────────────────────────────────────────────────────────────
654
+ stage_cmd = ("git add " + " ".join(shlex.quote(f) for f in add_files)) if add_files else "git add -A"
655
+ stage_result = tool_run_command(
656
+ {"command": stage_cmd, "cwd": cwd, "policy": policy},
657
+ console=console, has_rich=has_rich,
658
+ )
659
+ if not stage_result.get("success"):
660
+ return stage_result
661
+
662
+ # ── Commit ─────────────────────────────────────────────────────────────
663
+ safe_body = body.replace('"', '\\"').replace('$', '\\$')
664
+ commit_result = tool_run_command(
665
+ {"command": f'{env_prefix}git commit -m "{safe_body}"',
666
+ "cwd": cwd, "policy": policy},
667
+ console=console, has_rich=has_rich,
668
+ )
669
+ if not commit_result.get("success"):
670
+ return commit_result
671
+
672
+ # ── Push (use App token remote if available) ───────────────────────────
673
+ push_branch = branch or tool_run_command(
674
+ {"command": "git rev-parse --abbrev-ref HEAD", "policy": "safe", "cwd": cwd}
675
+ ).get("output", "").strip() or "main"
676
+
677
+ if auth_available and token and repo:
678
+ auth_url = get_aria_git_url(repo, token)
679
+ push_cmd = f"git push {shlex.quote(auth_url)} {shlex.quote(push_branch)}"
680
+ else:
681
+ push_cmd = f"git push origin {shlex.quote(push_branch)}"
682
+
683
+ push_result = tool_run_command(
684
+ {"command": push_cmd, "cwd": cwd, "policy": policy, "timeout": 60},
685
+ console=console, has_rich=has_rich,
686
+ )
687
+ return {
688
+ **push_result,
689
+ "committed_as": f"{ARIA_BOT_NAME} <{ARIA_BOT_EMAIL}>",
690
+ "branch": push_branch,
691
+ "app_auth": auth_available,
692
+ }
693
+
694
+ return {
695
+ "success": False,
696
+ "error": (
697
+ f"Unknown GitHub action: '{action}'. "
698
+ "Use: list_prs, list_issues, view_pr, view_issue, create_pr, "
699
+ "list_commits, search, read_file, pr_diff, pr_checks, "
700
+ "git_status, commit_and_push"
701
+ ),
702
+ }
703
+
704
+
705
+ def tool_ask_user(params: dict, *, console=None, has_rich: bool = True) -> dict:
706
+ """Pause execution and ask the user for clarification or a decision."""
707
+ question = params.get("question", "")
708
+ if not question:
709
+ return {"success": False, "error": "Missing 'question' parameter"}
710
+
711
+ if has_rich and console is not None:
712
+ from rich.prompt import Prompt
713
+ console.print(f"\n[bold magenta]🤔 Agent needs clarification:[/bold magenta] {question}")
714
+ try:
715
+ answer = Prompt.ask("[cyan]Your answer[/cyan]")
716
+ except (KeyboardInterrupt, EOFError):
717
+ return {"success": False, "error": "User cancelled the input."}
718
+ else:
719
+ print(f"\nAgent needs clarification: {question}")
720
+ try:
721
+ answer = input("Your answer: ")
722
+ except (KeyboardInterrupt, EOFError):
723
+ return {"success": False, "error": "User cancelled the input."}
724
+
725
+ return {"success": True, "data": {"user_answer": answer}}
726
+