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,192 @@
1
+ """P2 — fuse quantitative ground truth and calibrate confidence.
2
+
3
+ The qualitative team votes a signal with a self-reported confidence. That number
4
+ is uncalibrated — an agent saying "80%" doesn't mean it's right 80% of the time.
5
+ This layer:
6
+
7
+ 1. gathers quant signals (AI signal, risk metrics, backtest) as ground truth,
8
+ 2. nudges confidence by whether quant *agrees* with the qualitative verdict,
9
+ 3. scales by a *reliability* factor learned from realised outcomes (CalibrationStore),
10
+
11
+ so confidence drifts toward the historical hit-rate as outcomes accumulate.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import os
18
+ import time
19
+ from pathlib import Path
20
+ from typing import Callable, Dict, Optional, Tuple
21
+
22
+ from .models import QuantEvidence, Provenance
23
+
24
+ _BULL = ("STRONG_BUY", "BUY")
25
+ _BEAR = ("STRONG_SELL", "SELL")
26
+
27
+
28
+ # ── gather quant evidence ─────────────────────────────────────────────────────
29
+ def _default_provider(symbol: str) -> Dict[str, Dict]:
30
+ """Best-effort pull of quant signals from local_finance_tools (each optional)."""
31
+ out: Dict[str, Dict] = {}
32
+ try:
33
+ import local_finance_tools as lft
34
+ except Exception:
35
+ return out
36
+ for key, fn in (("ai", "_get_ai_signal"), ("risk", "_get_risk_metrics"),
37
+ ("backtest", "_backtest_strategy"), ("factors", "_calculate_factors")):
38
+ f = getattr(lft, fn, None)
39
+ if not f:
40
+ continue
41
+ try:
42
+ res = f({"symbol": symbol})
43
+ if isinstance(res, dict) and res.get("success"):
44
+ out[key] = res
45
+ except Exception:
46
+ pass
47
+ return out
48
+
49
+
50
+ def _num(d: Dict, *keys) -> Optional[float]:
51
+ for k in keys:
52
+ v = d.get(k)
53
+ if isinstance(v, (int, float)):
54
+ return float(v)
55
+ return None
56
+
57
+
58
+ def gather_quant_evidence(
59
+ symbol: str,
60
+ provider: Optional[Callable[[str], Dict[str, Dict]]] = None,
61
+ ) -> Tuple[QuantEvidence, list]:
62
+ """Return (QuantEvidence, [Provenance]). Never raises."""
63
+ raw = {}
64
+ try:
65
+ raw = (provider or _default_provider)(symbol)
66
+ except Exception:
67
+ raw = {}
68
+
69
+ prov: list = []
70
+ ev = QuantEvidence()
71
+ if not raw:
72
+ ev.note = "no quant backend available"
73
+ return ev, prov
74
+
75
+ ai = raw.get("ai") or {}
76
+ if ai:
77
+ action = str(ai.get("action") or ai.get("signal") or "").upper()
78
+ ev.ai_signal = action or None
79
+ conf = _num(ai, "confidence") or 0.0
80
+ if action in _BULL:
81
+ ev.ai_score = conf
82
+ elif action in _BEAR:
83
+ ev.ai_score = -conf
84
+ else:
85
+ ev.ai_score = 0.0
86
+ prov.append(Provenance("ai_signal", str(ai.get("provider", "quant")), note=action))
87
+
88
+ risk = raw.get("risk") or {}
89
+ if risk:
90
+ ev.sharpe = _num(risk, "sharpe", "sharpe_ratio")
91
+ ev.max_drawdown = _num(risk, "max_drawdown", "max_dd")
92
+ prov.append(Provenance("risk_metrics", "local_finance", note="VaR/Sharpe"))
93
+
94
+ bt = raw.get("backtest") or {}
95
+ if bt:
96
+ ev.backtest_return = _num(bt, "total_return", "return", "cagr")
97
+ if ev.sharpe is None:
98
+ ev.sharpe = _num(bt, "sharpe", "sharpe_ratio")
99
+ if ev.max_drawdown is None:
100
+ ev.max_drawdown = _num(bt, "max_drawdown", "max_dd")
101
+ prov.append(Provenance("backtest", "local_finance", note=str(bt.get("strategy", ""))))
102
+
103
+ fac = raw.get("factors") or {}
104
+ if fac:
105
+ ev.ic = _num(fac, "ic", "information_coefficient")
106
+ ev.factors = {k: v for k, v in fac.items()
107
+ if k not in ("success", "symbol") and isinstance(v, (int, float))}
108
+ prov.append(Provenance("factors", "local_finance"))
109
+
110
+ ev.available = bool(ai or risk or bt or fac)
111
+ return ev, prov
112
+
113
+
114
+ # ── confidence calibration ────────────────────────────────────────────────────
115
+ def _bucket(conf: float) -> str:
116
+ if conf < 0.4:
117
+ return "lo"
118
+ if conf < 0.7:
119
+ return "mid"
120
+ return "hi"
121
+
122
+
123
+ def _side(signal: str) -> str:
124
+ if signal in _BULL:
125
+ return "bull"
126
+ if signal in _BEAR:
127
+ return "bear"
128
+ return "neutral"
129
+
130
+
131
+ def agreement(agent_signal: str, quant_verdict: str) -> str:
132
+ a = _side(agent_signal)
133
+ q = {"BULLISH": "bull", "BEARISH": "bear", "NEUTRAL": "neutral"}.get(quant_verdict, "neutral")
134
+ if a == "neutral" or q == "neutral":
135
+ return "neutral"
136
+ return "agree" if a == q else "disagree"
137
+
138
+
139
+ class CalibrationStore:
140
+ """Tracks realised hit-rate per (signal-side, confidence-bucket) on disk.
141
+
142
+ ``reliability(conf, signal)`` returns observed_hit_rate / nominal_confidence,
143
+ clamped to a sane band, so a chronically over-confident bucket gets damped and
144
+ an under-confident one gets a small boost. With no history it returns 1.0.
145
+ """
146
+
147
+ _NOMINAL = {"lo": 0.30, "mid": 0.55, "hi": 0.80}
148
+
149
+ def __init__(self, path: Optional[Path] = None):
150
+ self.path = Path(path or os.path.expanduser("~/.arthera/deep_calibration.json"))
151
+ self._data: Dict[str, Dict[str, int]] = {}
152
+ try:
153
+ if self.path.exists():
154
+ self._data = json.loads(self.path.read_text())
155
+ except Exception:
156
+ self._data = {}
157
+
158
+ def _key(self, side: str, bucket: str) -> str:
159
+ return f"{side}:{bucket}"
160
+
161
+ def reliability(self, conf: float, signal: str = "") -> float:
162
+ rec = self._data.get(self._key(_side(signal), _bucket(conf)))
163
+ if not rec or rec.get("n", 0) < 8:
164
+ return 1.0
165
+ hit = rec["hit"] / rec["n"]
166
+ nominal = self._NOMINAL[_bucket(conf)] or 1.0
167
+ return max(0.6, min(1.25, hit / nominal))
168
+
169
+ def record_outcome(self, signal: str, conf: float, correct: bool) -> None:
170
+ k = self._key(_side(signal), _bucket(conf))
171
+ rec = self._data.setdefault(k, {"n": 0, "hit": 0})
172
+ rec["n"] += 1
173
+ rec["hit"] += 1 if correct else 0
174
+ try:
175
+ self.path.parent.mkdir(parents=True, exist_ok=True)
176
+ self.path.write_text(json.dumps(self._data, indent=2))
177
+ except Exception:
178
+ pass
179
+
180
+
181
+ def calibrate_confidence(
182
+ raw_conf: float,
183
+ agent_signal: str,
184
+ quant: QuantEvidence,
185
+ store: Optional[CalibrationStore] = None,
186
+ ) -> Tuple[float, str]:
187
+ """Return (calibrated_confidence, agreement_label)."""
188
+ agree = agreement(agent_signal, quant.verdict()) if quant and quant.available else "neutral"
189
+ factor = {"agree": 1.15, "neutral": 1.0, "disagree": 0.70}[agree]
190
+ rel = store.reliability(raw_conf, agent_signal) if store else 1.0
191
+ cal = max(0.0, min(1.0, raw_conf * factor * rel))
192
+ return cal, agree
@@ -0,0 +1,95 @@
1
+ """P1a — group agent results by theme and build a per-theme sub-synthesis.
2
+
3
+ Flat synthesis ("here are 8 opinions, here's the average") loses structure. Real
4
+ research clusters evidence: what does *valuation* say, what does *momentum* say,
5
+ what does *risk* say — then reconciles across clusters. This module does the
6
+ clustering and a deterministic per-cluster roll-up (no LLM needed).
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Dict, List, Tuple
12
+
13
+ from ..base import AgentResult
14
+ from .models import ThemeGroup
15
+
16
+ # Which theme each agent belongs to. Unknown agents fall into "other".
17
+ _AGENT_THEME = {
18
+ "fundamental": "valuation",
19
+ "earnings": "valuation",
20
+ "technical": "momentum",
21
+ "risk": "risk",
22
+ "news": "catalysts",
23
+ "catalyst": "catalysts",
24
+ "macro": "macro",
25
+ "sector": "macro",
26
+ "northbound": "macro",
27
+ "debate": "reconciliation",
28
+ }
29
+
30
+ # Display order + human labels.
31
+ _THEME_ORDER = ["valuation", "momentum", "macro", "catalysts", "risk",
32
+ "reconciliation", "other"]
33
+ _THEME_LABEL = {
34
+ "valuation": "估值/基本面",
35
+ "momentum": "动量/技术",
36
+ "macro": "宏观/板块",
37
+ "catalysts": "催化/消息",
38
+ "risk": "风险",
39
+ "reconciliation": "分歧调解",
40
+ "other": "其他",
41
+ }
42
+
43
+ _SCORE = {"STRONG_BUY": 2, "BUY": 1, "HOLD": 0, "SELL": -1, "STRONG_SELL": -2}
44
+
45
+
46
+ def theme_of(agent_name: str) -> str:
47
+ return _AGENT_THEME.get(agent_name, "other")
48
+
49
+
50
+ def _vote(results: List[AgentResult]) -> Tuple[str, float]:
51
+ """Confidence-weighted majority within a single theme."""
52
+ valid = [r for r in results if r.success and r.signal in _SCORE]
53
+ if not valid:
54
+ return "HOLD", 0.0
55
+ avg_score = sum(_SCORE[r.signal] * r.confidence for r in valid) / len(valid)
56
+ avg_conf = sum(r.confidence for r in valid) / len(valid)
57
+ if avg_score >= 1.5:
58
+ return "STRONG_BUY", avg_conf
59
+ if avg_score >= 0.5:
60
+ return "BUY", avg_conf
61
+ if avg_score <= -1.5:
62
+ return "STRONG_SELL", avg_conf
63
+ if avg_score <= -0.5:
64
+ return "SELL", avg_conf
65
+ return "HOLD", avg_conf
66
+
67
+
68
+ def group_by_theme(results: List[AgentResult]) -> List[ThemeGroup]:
69
+ """Cluster agent results into themes with a per-theme signal + summary."""
70
+ buckets: Dict[str, List[AgentResult]] = {}
71
+ for r in results:
72
+ buckets.setdefault(theme_of(r.agent), []).append(r)
73
+
74
+ groups: List[ThemeGroup] = []
75
+ for theme in _THEME_ORDER:
76
+ members = buckets.get(theme)
77
+ if not members:
78
+ continue
79
+ signal, conf = _vote(members)
80
+ points: List[str] = []
81
+ for r in members:
82
+ if r.success:
83
+ points.extend((r.key_points or [])[:2])
84
+ ok = sum(1 for r in members if r.success)
85
+ summary = (f"{_THEME_LABEL[theme]}: {signal}({ok}/{len(members)} agent 有效,"
86
+ f"置信度 {conf:.0%})")
87
+ groups.append(ThemeGroup(
88
+ theme=_THEME_LABEL[theme],
89
+ agents=[r.agent for r in members],
90
+ signal=signal,
91
+ confidence=conf,
92
+ summary=summary,
93
+ key_points=points[:5],
94
+ ))
95
+ return groups
@@ -0,0 +1,106 @@
1
+ """P3 — render the deep result at three depths, with data provenance.
2
+
3
+ brief — one glance: signal, calibrated confidence, headline
4
+ standard — + per-theme roll-up + synthesis
5
+ deep — + quant evidence + critique + provenance (data lineage) + agent points
6
+
7
+ Pure text/markdown so it renders in the terminal; the HTML report layer can reuse
8
+ the same DeepAnalysisResult.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from .models import DeepAnalysisResult
14
+
15
+ _SIGNAL_ICON = {
16
+ "STRONG_BUY": "🟢🟢", "BUY": "🟢", "HOLD": "⚪",
17
+ "SELL": "🔴", "STRONG_SELL": "🔴🔴",
18
+ }
19
+
20
+
21
+ def _headline(r: DeepAnalysisResult) -> str:
22
+ icon = _SIGNAL_ICON.get(r.final_signal, "⚪")
23
+ conf = f"{r.calibrated_confidence:.0%}"
24
+ note = ""
25
+ if r.raw_confidence and abs(r.calibrated_confidence - r.raw_confidence) >= 0.05:
26
+ note = f"(原始 {r.raw_confidence:.0%} → 校准 {conf})"
27
+ else:
28
+ note = f"(置信度 {conf})"
29
+ return f"{icon} **{r.symbol} · {r.final_signal}** {note}"
30
+
31
+
32
+ def render_brief(r: DeepAnalysisResult) -> str:
33
+ lines = [_headline(r)]
34
+ if r.quant and r.quant.available:
35
+ lines.append(f" 量化: {r.quant.verdict()}"
36
+ + (f" · IC {r.quant.ic:.2f}" if r.quant.ic is not None else "")
37
+ + (f" · Sharpe {r.quant.sharpe:.2f}" if r.quant.sharpe is not None else ""))
38
+ if r.critique and r.critique.high:
39
+ lines.append(f" ⚠️ {r.critique.high[0].message}")
40
+ return "\n".join(lines)
41
+
42
+
43
+ def render_standard(r: DeepAnalysisResult) -> str:
44
+ parts = [render_brief(r), ""]
45
+ if r.themes:
46
+ parts.append("### 分主题")
47
+ for t in r.themes:
48
+ parts.append(f"- {t.summary}")
49
+ parts.append("")
50
+ if r.synthesis:
51
+ parts.append("### 综合")
52
+ parts.append(r.synthesis)
53
+ return "\n".join(parts).rstrip()
54
+
55
+
56
+ def render_deep(r: DeepAnalysisResult) -> str:
57
+ parts = [render_standard(r), ""]
58
+
59
+ if r.quant and r.quant.available:
60
+ q = r.quant
61
+ parts.append("### 量化地面真值")
62
+ row = []
63
+ if q.ai_signal: row.append(f"AI信号 {q.ai_signal}")
64
+ if q.ai_score is not None:row.append(f"分值 {q.ai_score:+.2f}")
65
+ if q.ic is not None: row.append(f"IC {q.ic:.3f}")
66
+ if q.sharpe is not None: row.append(f"Sharpe {q.sharpe:.2f}")
67
+ if q.max_drawdown is not None: row.append(f"MaxDD {q.max_drawdown:.1%}")
68
+ if q.backtest_return is not None: row.append(f"回测收益 {q.backtest_return:+.1%}")
69
+ parts.append("- " + " · ".join(row) if row else "- (无)")
70
+ parts.append("")
71
+
72
+ if r.critique is not None:
73
+ parts.append("### 自检 (Critic)")
74
+ mark = "✅ 通过" if r.critique.passed else "❌ 存在高危问题"
75
+ parts.append(f"结论: {mark}" + ("(无问题)" if not r.critique.issues else ""))
76
+ for i in r.critique.issues:
77
+ sev = {"high": "🔴", "medium": "🟡", "low": "⚪"}.get(i.severity, "·")
78
+ parts.append(f"- {sev} [{i.kind}] {i.message}")
79
+ parts.append("")
80
+
81
+ if r.provenance:
82
+ parts.append("### 数据血缘")
83
+ parts.append("| 字段 | 来源 | 时效 | 备注 |")
84
+ parts.append("|------|------|------|------|")
85
+ for p in r.provenance:
86
+ parts.append(f"| {p.field} | {p.source} | {p.freshness} | {p.note} |")
87
+ parts.append("")
88
+
89
+ if r.agent_results:
90
+ parts.append("### 各 Agent 要点")
91
+ for a in r.agent_results:
92
+ if a.get("error"):
93
+ parts.append(f"- **{a['agent']}** ⚠️ {a['error']}")
94
+ continue
95
+ pts = a.get("key_points") or []
96
+ head = f"- **{a['agent']}** ({a.get('signal','?')}, {a.get('confidence',0):.0%})"
97
+ parts.append(head)
98
+ for pt in pts[:3]:
99
+ parts.append(f" • {pt}")
100
+
101
+ return "\n".join(parts).rstrip()
102
+
103
+
104
+ def render_tier(r: DeepAnalysisResult, tier: str = "standard") -> str:
105
+ return {"brief": render_brief, "standard": render_standard,
106
+ "deep": render_deep}.get(tier, render_standard)(r)
@@ -0,0 +1,14 @@
1
+ """
2
+ agents/engineering/__init__.py — Engineering & Code Execution Agents
3
+ ===================================================================
4
+ Provides Coder, Tester, and Self-Healing Debugger agents for Aria Code.
5
+ """
6
+
7
+ from .coder import CoderAgent
8
+ from .tester import TesterAgent, TesterSelfHealingAgent
9
+
10
+ __all__ = [
11
+ "CoderAgent",
12
+ "TesterAgent",
13
+ "TesterSelfHealingAgent",
14
+ ]
@@ -0,0 +1,222 @@
1
+ """
2
+ agents/engineering/coder.py — Coder Agent
3
+ =========================================
4
+ Engineering agent responsible for translating Quant Strategist Rule Trees
5
+ into self-contained, robust, executable Python / TypeScript backtest scripts.
6
+ Never dumps raw unexecuted code into user dialogue; saves directly to disk.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import inspect
12
+ import logging
13
+ import os
14
+ import pathlib
15
+ import re
16
+ import shlex
17
+ import subprocess
18
+ from typing import Any, Awaitable, Callable, Dict, Optional
19
+
20
+ from ..base import BaseAgent, AgentResult
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+
25
+ class CoderAgent(BaseAgent):
26
+ name = "coder"
27
+ description = "编码智能体 — 在指定工作区读写文件;执行命令需宿主明确授权"
28
+
29
+ def __init__(
30
+ self,
31
+ output_dir: Optional[pathlib.Path] = None,
32
+ llm_provider=None,
33
+ data_router=None,
34
+ on_token: Optional[Callable[[str], None]] = None,
35
+ on_thought: Optional[Callable[[str], None]] = None,
36
+ on_tool_start: Optional[Callable[[str, Dict], None]] = None,
37
+ on_tool_end: Optional[Callable[[str, Any], None]] = None,
38
+ lang: str = "zh",
39
+ command_approval: Optional[Callable[[list[str], pathlib.Path], bool | Awaitable[bool]]] = None,
40
+ on_user_question: Optional[Callable[[str], str | Awaitable[str]]] = None,
41
+ ) -> None:
42
+ super().__init__(
43
+ llm_provider=llm_provider,
44
+ data_router=data_router,
45
+ on_token=on_token,
46
+ on_thought=on_thought,
47
+ on_tool_start=on_tool_start,
48
+ on_tool_end=on_tool_end,
49
+ lang=lang,
50
+ )
51
+ self.output_dir = (output_dir or pathlib.Path.cwd() / "generated").resolve()
52
+ self.command_approval = command_approval
53
+ self.on_user_question = on_user_question
54
+
55
+ def _workspace_path(self, raw_path: str, *, required: bool = True) -> pathlib.Path:
56
+ """Resolve a model-supplied path without allowing access outside the workspace."""
57
+ if required and not raw_path:
58
+ raise ValueError("A workspace-relative path is required")
59
+ path = pathlib.Path(raw_path)
60
+ if path.is_absolute():
61
+ raise ValueError("Absolute paths are not allowed")
62
+ resolved = (self.output_dir / path).resolve()
63
+ if not resolved.is_relative_to(self.output_dir):
64
+ raise ValueError("Path escapes the workspace")
65
+ return resolved
66
+
67
+ async def _execute_tool(self, tool_name: str, tool_args: Dict[str, Any]) -> str:
68
+ if self.on_tool_start:
69
+ self.on_tool_start(tool_name, tool_args)
70
+
71
+ try:
72
+ if tool_name == "run_command":
73
+ argv = shlex.split(str(tool_args.get("command", "")))
74
+ if not argv:
75
+ raise ValueError("Command is required")
76
+ cwd = self._workspace_path(str(tool_args.get("cwd", "")), required=False)
77
+ if not cwd.is_dir():
78
+ raise ValueError("Command directory does not exist")
79
+ if self.command_approval is None:
80
+ result = "Error: command approval is unavailable; command was not run"
81
+ else:
82
+ approved = self.command_approval(argv, cwd)
83
+ if inspect.isawaitable(approved):
84
+ approved = await approved
85
+ if not approved:
86
+ result = "Error: command was denied; command was not run"
87
+ else:
88
+ proc = subprocess.run(argv, shell=False, cwd=cwd, capture_output=True, text=True, timeout=120)
89
+ result = f"STDOUT:\n{proc.stdout}\nSTDERR:\n{proc.stderr}\nReturnCode: {proc.returncode}"
90
+
91
+ elif tool_name in {"write_file", "read_file", "edit_file_chunk"}:
92
+ path = self._workspace_path(str(tool_args.get("filename", "")))
93
+ if tool_name == "write_file":
94
+ path.parent.mkdir(parents=True, exist_ok=True)
95
+ path.write_text(str(tool_args.get("content", "")), encoding="utf-8")
96
+ result = f"Successfully wrote full file to {path}"
97
+ elif tool_name == "read_file":
98
+ lines = path.read_text(encoding="utf-8").splitlines()
99
+ result = f"File: {path}\n" + "\n".join(f"{i:04d} | {line}" for i, line in enumerate(lines, 1))
100
+ else:
101
+ target = str(tool_args.get("target_content", ""))
102
+ if not target:
103
+ raise ValueError("target_content is required")
104
+ original = path.read_text(encoding="utf-8")
105
+ if target not in original:
106
+ result = f"Error: target_content not found in {path}"
107
+ else:
108
+ path.write_text(original.replace(target, str(tool_args.get("replacement_content", ""))), encoding="utf-8")
109
+ result = f"Successfully replaced chunk in {path}"
110
+
111
+ elif tool_name == "search_code":
112
+ query = str(tool_args.get("query", ""))
113
+ if not query:
114
+ raise ValueError("Search query is required")
115
+ matches: list[str] = []
116
+ for root, dirs, files in os.walk(self.output_dir, followlinks=False):
117
+ dirs[:] = [name for name in dirs if not name.startswith(".")]
118
+ for filename in files:
119
+ if len(matches) >= 50:
120
+ break
121
+ path = pathlib.Path(root) / filename
122
+ try:
123
+ if filename.startswith(".") or not path.resolve().is_relative_to(self.output_dir) or path.stat().st_size > 1_000_000:
124
+ continue
125
+ lines = path.read_text(encoding="utf-8").splitlines()
126
+ except (OSError, UnicodeError):
127
+ continue
128
+ for line_no, line in enumerate(lines, 1):
129
+ if query in line:
130
+ matches.append(f"{path.relative_to(self.output_dir)}:{line_no}:{line}")
131
+ if len(matches) >= 50:
132
+ break
133
+ if len(matches) >= 50:
134
+ break
135
+ result = "\n".join(matches) if matches else "No matches found."
136
+
137
+ elif tool_name == "list_dir":
138
+ path = self._workspace_path(str(tool_args.get("path", "")), required=False)
139
+ if not path.is_dir():
140
+ raise ValueError("Directory does not exist")
141
+ items: list[str] = []
142
+ for root, dirs, files in os.walk(path, followlinks=False):
143
+ depth = len(pathlib.Path(root).relative_to(path).parts)
144
+ if depth >= 3:
145
+ dirs[:] = []
146
+ dirs[:] = [name for name in dirs if not name.startswith(".")]
147
+ items.extend(str((pathlib.Path(root) / name).relative_to(path)) for name in dirs + files if not name.startswith("."))
148
+ if len(items) >= 200:
149
+ break
150
+ result = "\n".join(items[:200])
151
+
152
+ elif tool_name == "take_screenshot":
153
+ result = "Error: screenshot capability is not configured; no image was captured"
154
+
155
+ elif tool_name == "github_api":
156
+ repo = str(tool_args.get("repo", ""))
157
+ issue = str(tool_args.get("issue_number", ""))
158
+ if tool_args.get("action") != "read_issue" or not re.fullmatch(r"[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+", repo) or not issue.isdecimal():
159
+ raise ValueError("Only read_issue with a valid repository and issue number is supported")
160
+ proc = subprocess.run(["gh", "issue", "view", issue, "--repo", repo], shell=False, capture_output=True, text=True, timeout=30)
161
+ result = proc.stdout if proc.returncode == 0 else proc.stderr
162
+
163
+ elif tool_name == "ask_user":
164
+ question = str(tool_args.get("question", ""))
165
+ if self.on_user_question is None:
166
+ result = "Error: user interaction is unavailable; no user response was received"
167
+ else:
168
+ answer = self.on_user_question(question)
169
+ if inspect.isawaitable(answer):
170
+ answer = await answer
171
+ result = f"USER REPLIED: {answer}"
172
+ else:
173
+ result = await super()._execute_tool(tool_name, tool_args)
174
+ except (OSError, ValueError, subprocess.TimeoutExpired) as exc:
175
+ result = f"Tool Error: {exc}"
176
+
177
+ if self.on_tool_end:
178
+ self.on_tool_end(tool_name, result)
179
+ return result
180
+
181
+ async def analyze(self, symbol: str, data: Dict[str, Any]) -> AgentResult:
182
+ """
183
+ Advanced Autonomous Software Engineer Loop (Cursor/Claude Code caliber).
184
+ """
185
+ rule_tree = data.get("strategy_rules") or data.get("rule_tree")
186
+ request_text = data.get("request", "Write a Python script.")
187
+
188
+ system_prompt = (
189
+ "You are a software engineer working within a bounded local workspace. "
190
+ "Use workspace-relative paths only. Never claim an action succeeded without a tool result.\n"
191
+ "Tools: list_dir(path), search_code(query), read_file(filename), "
192
+ "write_file(filename, content), edit_file_chunk(filename, target_content, replacement_content), "
193
+ "github_api(action='read_issue', repo='owner/repo', issue_number='123').\n"
194
+ "run_command(command, cwd) executes an argv command only after host approval. "
195
+ "If approval is unavailable or denied, do not retry to bypass it; report that verification was not run. "
196
+ "Shell pipelines, redirects and command substitution are unavailable.\n"
197
+ "Screenshot capture is unavailable; do not claim visual verification.\n"
198
+ + ("ask_user(question) forwards a real question to the host.\n" if self.on_user_question else "User interaction is unavailable; do not invent a reply.\n")
199
+ + "Call a tool with one JSON block per turn: "
200
+ + '{"type":"tool_call","name":"<tool_name>","args":{}}\n'
201
+ + "Inspect context, make focused edits, request approved verification, and report observed results."
202
+ )
203
+
204
+ user_prompt = f"Workspace: {self.output_dir}\nTarget Symbol: {symbol}\nRules: {rule_tree}\nUser Request: {request_text}\n\nPlease begin autonomous execution."
205
+
206
+ analysis = await self._call_llm(system_prompt, user_prompt, max_tokens=2500, max_tool_loops=15)
207
+
208
+ report = (
209
+ f"### Aria Coder - Engineering Report\n\n"
210
+ f"**项目根目录**:`{self.output_dir.absolute()}`\n\n"
211
+ f"**执行记录与结果**:\n{analysis}\n"
212
+ )
213
+
214
+ return AgentResult(
215
+ agent=self.name,
216
+ symbol=symbol,
217
+ analysis=report,
218
+ confidence=0.5,
219
+ signal="HOLD",
220
+ key_points=[],
221
+ data_used={"workspace": str(self.output_dir)},
222
+ )