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,202 @@
1
+ """
2
+ aria_telegram_bot.py — Lightweight Telegram Bot client for Aria Daemon.
3
+
4
+ Uses the Telegram Bot API directly via httpx (no heavy python-telegram-bot dep).
5
+ Supports long-polling updates and sending messages/documents back to users.
6
+
7
+ Commands handled:
8
+ /price SYMBOL — quick quote
9
+ /report SYMBOL — full analysis (async, returns text summary)
10
+ /brief — morning brief
11
+ /alerts — list active alerts
12
+ /alert SYMBOL cond v — add alert (e.g. /alert 600362 price_below 39.5)
13
+ /screen — hot A-share screener
14
+ /help — command list
15
+
16
+ Usage:
17
+ from aria_telegram_bot import TelegramBot
18
+ bot = TelegramBot(token="...", allowed_ids={123456})
19
+ await bot.start(command_handler) # command_handler(cmd, args, chat_id) -> str
20
+ """
21
+ from __future__ import annotations
22
+
23
+ import asyncio
24
+ import logging
25
+ from typing import Any, Callable, Coroutine, Optional, Set
26
+
27
+ import httpx
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+ _API = "https://api.telegram.org/bot{token}/{method}"
32
+
33
+
34
+ class TelegramBot:
35
+ def __init__(
36
+ self,
37
+ token: str,
38
+ allowed_chat_ids: Optional[Set[int]] = None,
39
+ poll_timeout: int = 30,
40
+ ):
41
+ self.token = token
42
+ self.allowed_chat_ids = allowed_chat_ids or set()
43
+ self.poll_timeout = poll_timeout
44
+ self._offset = 0
45
+ self._running = False
46
+ self._client: Optional[httpx.AsyncClient] = None
47
+
48
+ # ── Low-level API ─────────────────────────────────────────────────────────
49
+
50
+ def _url(self, method: str) -> str:
51
+ return _API.format(token=self.token, method=method)
52
+
53
+ async def _call(self, method: str, **kwargs: Any) -> Optional[dict]:
54
+ try:
55
+ if self._client is None:
56
+ self._client = httpx.AsyncClient(timeout=self.poll_timeout + 5)
57
+ resp = await self._client.post(self._url(method), json=kwargs)
58
+ data = resp.json()
59
+ if not data.get("ok"):
60
+ logger.warning("Telegram %s error: %s", method, data.get("description"))
61
+ return None
62
+ return data.get("result")
63
+ except Exception as exc:
64
+ logger.error("Telegram API call %s failed: %s", method, exc)
65
+ return None
66
+
67
+ # ── Sending ───────────────────────────────────────────────────────────────
68
+
69
+ async def send_message(
70
+ self, chat_id: int, text: str, parse_mode: str = "Markdown"
71
+ ) -> bool:
72
+ # Telegram has a 4096-char message limit
73
+ if len(text) > 4096:
74
+ text = text[:4090] + "\n…"
75
+ result = await self._call(
76
+ "sendMessage",
77
+ chat_id=chat_id,
78
+ text=text,
79
+ parse_mode=parse_mode,
80
+ disable_web_page_preview=True,
81
+ )
82
+ return result is not None
83
+
84
+ async def send_long_message(self, chat_id: int, text: str) -> None:
85
+ """Split and send messages that exceed Telegram's 4096-char limit."""
86
+ chunks = [text[i : i + 4000] for i in range(0, len(text), 4000)]
87
+ for chunk in chunks:
88
+ await self.send_message(chat_id, chunk)
89
+
90
+ async def send_typing(self, chat_id: int) -> None:
91
+ await self._call("sendChatAction", chat_id=chat_id, action="typing")
92
+
93
+ async def send_document(
94
+ self, chat_id: int, file_path: str, caption: str = ""
95
+ ) -> bool:
96
+ """Send a file as a document attachment."""
97
+ try:
98
+ import aiofiles
99
+ url = self._url("sendDocument")
100
+ async with aiofiles.open(file_path, "rb") as f:
101
+ content = await f.read()
102
+ import os
103
+ filename = os.path.basename(file_path)
104
+ if self._client is None:
105
+ self._client = httpx.AsyncClient(timeout=60)
106
+ resp = await self._client.post(
107
+ url,
108
+ data={"chat_id": str(chat_id), "caption": caption},
109
+ files={"document": (filename, content)},
110
+ )
111
+ return resp.json().get("ok", False)
112
+ except ImportError:
113
+ # Fallback: just send caption as text
114
+ await self.send_message(chat_id, caption or "File ready (aiofiles not installed)")
115
+ return False
116
+ except Exception as exc:
117
+ logger.error("send_document failed: %s", exc)
118
+ return False
119
+
120
+ # ── Polling loop ─────────────────────────────────────────────────────────
121
+
122
+ async def get_updates(self) -> list[dict]:
123
+ result = await self._call(
124
+ "getUpdates",
125
+ offset=self._offset,
126
+ timeout=self.poll_timeout,
127
+ allowed_updates=["message"],
128
+ )
129
+ return result or []
130
+
131
+ async def start(
132
+ self,
133
+ command_handler: Callable[[str, str, int], Coroutine[Any, Any, str]],
134
+ ) -> None:
135
+ """
136
+ Start long-polling. For each received message, parse the command and
137
+ call command_handler(command, args, chat_id) → reply text.
138
+ command_handler should be an async coroutine.
139
+ """
140
+ self._running = True
141
+ logger.info("Telegram bot polling started")
142
+ while self._running:
143
+ try:
144
+ updates = await self.get_updates()
145
+ except asyncio.CancelledError:
146
+ break
147
+ except Exception as exc:
148
+ logger.error("Polling error: %s", exc)
149
+ await asyncio.sleep(5)
150
+ continue
151
+
152
+ for update in updates:
153
+ self._offset = max(self._offset, update["update_id"] + 1)
154
+ msg = update.get("message", {})
155
+ text = (msg.get("text") or "").strip()
156
+ chat_id = msg.get("chat", {}).get("id")
157
+ if not text or not chat_id:
158
+ continue
159
+
160
+ # ACL check
161
+ if self.allowed_chat_ids and chat_id not in self.allowed_chat_ids:
162
+ await self.send_message(
163
+ chat_id,
164
+ "⛔ 未授权。请将你的 Chat ID 添加到 `TELEGRAM_ALLOWED_IDS`。\n你的 ID: `" + str(chat_id) + "`",
165
+ )
166
+ continue
167
+
168
+ asyncio.create_task(self._handle(text, chat_id, command_handler))
169
+
170
+ async def _handle(
171
+ self,
172
+ text: str,
173
+ chat_id: int,
174
+ command_handler: Callable[[str, str, int], Coroutine[Any, Any, str]],
175
+ ) -> None:
176
+ # Parse "/command args" or plain text
177
+ if text.startswith("/"):
178
+ parts = text[1:].split(None, 1)
179
+ cmd = parts[0].lower().split("@")[0] # strip @botname suffix
180
+ args = parts[1] if len(parts) > 1 else ""
181
+ else:
182
+ cmd = "chat"
183
+ args = text
184
+
185
+ await self.send_typing(chat_id)
186
+ try:
187
+ reply = await command_handler(cmd, args, chat_id)
188
+ except Exception as exc:
189
+ reply = f"⚠️ 执行出错: {exc}"
190
+ logger.exception("command_handler error cmd=%s", cmd)
191
+
192
+ if reply:
193
+ await self.send_long_message(chat_id, reply)
194
+
195
+ async def stop(self) -> None:
196
+ self._running = False
197
+ if self._client:
198
+ await self._client.aclose()
199
+ self._client = None
200
+
201
+ async def get_me(self) -> Optional[dict]:
202
+ return await self._call("getMe")
aria_code/ariarc.py ADDED
@@ -0,0 +1,385 @@
1
+ """
2
+ ariarc.py — Project-level configuration loader for Aria Code.
3
+
4
+ Searches for ``.ariarc`` or ``.ariarc.json`` in the current directory and all
5
+ parent directories (walks up to filesystem root, stops at $HOME).
6
+
7
+ .ariarc format (JSON or JSONC)::
8
+
9
+ {
10
+ // Project identity
11
+ "project": "Arthera Quant Engine",
12
+ "description": "Quantitative trading system for A-share and US markets",
13
+
14
+ // Extra system prompt injected before every conversation
15
+ "system_prompt": "You are helping with an A-share quant strategy codebase...",
16
+
17
+ // Files whose contents are prepended as context
18
+ "context_files": ["README.md", "docs/architecture.md"],
19
+
20
+ // Tool allow/deny lists (applied on top of global policy)
21
+ "tools_whitelist": ["read_file", "search_code", "calculate_factors"],
22
+ "tools_blacklist": ["run_command"],
23
+
24
+ // Default symbols for watchlist / quick commands
25
+ "default_symbols": ["sh600519", "sh601318", "sz000858"],
26
+ "market": "cn", // cn | us | global
27
+
28
+ // A-share specific settings
29
+ "ashare": {
30
+ "broker": "东方财富",
31
+ "account_type": "普通账户",
32
+ "risk_level": "moderate" // conservative | moderate | aggressive
33
+ },
34
+
35
+ // Slash commands defined in-project
36
+ "commands": {
37
+ "/morning-cn": "生成A股早盘简报,重点关注 {default_symbols}",
38
+ "/factor-check": "计算 {symbol} 的技术因子并分析当前趋势"
39
+ },
40
+
41
+ // Files to auto-read at session start (feeds AI context). These are
42
+ // paths in YOUR project; the two below are only an illustration of the
43
+ // shape. Earlier they named packages/quant_engine/strategies/ and
44
+ // analysis/, which are not in this repository, so copying the example
45
+ // read nothing.
46
+ "auto_context": [
47
+ "docs/architecture.md",
48
+ "src/my_project/strategy_base.py"
49
+ ],
50
+
51
+ // Which model this project runs on. Pins the backend for everyone who
52
+ // opens the repo, so it does not depend on each developer's global
53
+ // config. An explicit /model during a session still wins.
54
+ "model": "google/gemini-2.5-pro",
55
+ "provider": "google",
56
+ "max_rounds": 40,
57
+
58
+ // Disable AI from proposing certain file patterns (safety)
59
+ "write_deny_patterns": ["*.env", "config/secrets.*", "**/credentials*"],
60
+
61
+ // What "correct" means here, per domain. The acceptance gate runs these
62
+ // after Aria changes files and refuses to call the turn done while they
63
+ // are red. "default" applies to every turn; a pack name applies only when
64
+ // that pack recognises something concrete in the user's message.
65
+ "acceptance": {
66
+ "default": ["python3 -m pytest -q"],
67
+ "logistics": ["python3 scripts/reconcile_waybills.py"],
68
+ "payments": ["python3 scripts/verify_stripe_sync.py"]
69
+ }
70
+ }
71
+ """
72
+
73
+ from __future__ import annotations
74
+
75
+ import json
76
+ import os
77
+ import pathlib
78
+ import re
79
+ from typing import Any, Dict, List, Optional
80
+
81
+ # ---------------------------------------------------------------------------
82
+ # JSONC parser (JSON with // and /* */ comments)
83
+ # ---------------------------------------------------------------------------
84
+
85
+ def _strip_comments(text: str) -> str:
86
+ """Remove // line comments and /* */ block comments from JSON text."""
87
+ # Block comments
88
+ text = re.sub(r"/\*.*?\*/", "", text, flags=re.DOTALL)
89
+ # Line comments (not inside strings — good-enough heuristic)
90
+ text = re.sub(r'(?<!:)(?<!https)//[^\n]*', "", text)
91
+ return text
92
+
93
+
94
+ def _parse_jsonc(text: str) -> Any:
95
+ return json.loads(_strip_comments(text))
96
+
97
+
98
+ # ---------------------------------------------------------------------------
99
+ # Default / empty ariarc
100
+ # ---------------------------------------------------------------------------
101
+
102
+ ARIARC_DEFAULTS: Dict[str, Any] = {
103
+ "project": None,
104
+ "description": None,
105
+ "system_prompt": "",
106
+ "context_files": [],
107
+ "tools_whitelist": [],
108
+ "tools_blacklist": [],
109
+ "default_symbols": [],
110
+ "market": "global",
111
+ "ashare": {},
112
+ "commands": {},
113
+ "auto_context": [],
114
+ "write_deny_patterns": ["*.env", "**/.env*", "**/secrets.*", "**/credentials*"],
115
+ "acceptance": {},
116
+ # Which model this project runs on. A project that is standardised on one
117
+ # backend should be able to say so in the file it already keeps under
118
+ # version control, rather than every developer configuring it by hand and
119
+ # rather than the code hardcoding a default that suits one of them.
120
+ "model": None,
121
+ "provider": None,
122
+ "max_rounds": None,
123
+ }
124
+
125
+
126
+ # ---------------------------------------------------------------------------
127
+ # Finder
128
+ # ---------------------------------------------------------------------------
129
+
130
+ def find_ariarc(start_dir: Optional[str] = None) -> Optional[pathlib.Path]:
131
+ """
132
+ Walk up from *start_dir* (default: cwd) looking for .ariarc or .ariarc.json.
133
+ Stops at $HOME or filesystem root.
134
+ """
135
+ home = pathlib.Path.home()
136
+ cwd = pathlib.Path(start_dir or os.getcwd()).resolve()
137
+ names = [".ariarc", ".ariarc.json", ".ariarc.jsonc"]
138
+
139
+ current = cwd
140
+ while True:
141
+ for name in names:
142
+ candidate = current / name
143
+ if candidate.exists() and candidate.is_file():
144
+ return candidate
145
+ if current == home or current.parent == current:
146
+ break
147
+ current = current.parent
148
+ return None
149
+
150
+
151
+ # ---------------------------------------------------------------------------
152
+ # Loader
153
+ # ---------------------------------------------------------------------------
154
+
155
+ class AriaRC:
156
+ """
157
+ Parsed project configuration from .ariarc.
158
+
159
+ Usage::
160
+
161
+ rc = AriaRC.load() # searches cwd upward
162
+ rc = AriaRC.load("/path") # explicit start dir
163
+
164
+ rc.project # "Arthera Quant Engine"
165
+ rc.system_prompt # extra text injected into system prompt
166
+ rc.get_context_text() # concatenated content of context_files
167
+ rc.is_tool_allowed("run_command")
168
+ """
169
+
170
+ def __init__(self, data: Dict[str, Any], source_path: Optional[pathlib.Path] = None):
171
+ cfg = {**ARIARC_DEFAULTS, **data}
172
+ self.source_path: Optional[pathlib.Path] = source_path
173
+ self.project: Optional[str] = cfg.get("project")
174
+ self.description: Optional[str] = cfg.get("description")
175
+ self.system_prompt: str = cfg.get("system_prompt", "")
176
+ self.context_files: List[str] = list(cfg.get("context_files", []))
177
+ self.tools_whitelist: List[str] = list(cfg.get("tools_whitelist", []))
178
+ self.tools_blacklist: List[str] = list(cfg.get("tools_blacklist", []))
179
+ self.default_symbols: List[str] = list(cfg.get("default_symbols", []))
180
+ self.market: str = cfg.get("market", "global")
181
+ self.ashare: Dict[str, Any] = cfg.get("ashare", {})
182
+ self.commands: Dict[str, str] = cfg.get("commands", {})
183
+ self.auto_context: List[str] = list(cfg.get("auto_context", []))
184
+ self.write_deny_patterns: List[str] = list(cfg.get("write_deny_patterns", []))
185
+ _acceptance = cfg.get("acceptance", {})
186
+ self.acceptance: Dict[str, Any] = _acceptance if isinstance(_acceptance, dict) else {}
187
+ self.model: Optional[str] = cfg.get("model") or None
188
+ self.provider: Optional[str] = cfg.get("provider") or None
189
+ _rounds = cfg.get("max_rounds")
190
+ self.max_rounds: Optional[int] = int(_rounds) if isinstance(_rounds, int) and _rounds > 0 else None
191
+
192
+ # ── class methods ──────────────────────────────────────────────────────
193
+
194
+ @classmethod
195
+ def load(cls, start_dir: Optional[str] = None) -> "AriaRC":
196
+ path = find_ariarc(start_dir)
197
+ if path is None:
198
+ return cls({})
199
+ try:
200
+ text = path.read_text(encoding="utf-8")
201
+ data = _parse_jsonc(text)
202
+ if not isinstance(data, dict):
203
+ data = {}
204
+ return cls(data, source_path=path)
205
+ except Exception:
206
+ return cls({}, source_path=path)
207
+
208
+ @classmethod
209
+ def empty(cls) -> "AriaRC":
210
+ return cls({})
211
+
212
+ # ── helpers ────────────────────────────────────────────────────────────
213
+
214
+ @property
215
+ def found(self) -> bool:
216
+ return self.source_path is not None
217
+
218
+ def is_tool_allowed(self, tool_name: str) -> bool:
219
+ """Return True if tool is allowed under whitelist/blacklist rules."""
220
+ if self.tools_blacklist and tool_name in self.tools_blacklist:
221
+ return False
222
+ if self.tools_whitelist:
223
+ return tool_name in self.tools_whitelist
224
+ return True
225
+
226
+ def get_context_text(self, base_dir: Optional[str] = None) -> str:
227
+ """
228
+ Read all context_files and return their concatenated content.
229
+ Paths are relative to the .ariarc location (or cwd if not found).
230
+ """
231
+ base = pathlib.Path(base_dir or (self.source_path.parent if self.source_path else os.getcwd()))
232
+ parts: List[str] = []
233
+ for rel_path in self.context_files:
234
+ p = base / rel_path
235
+ if p.exists() and p.is_file():
236
+ try:
237
+ content = p.read_text(encoding="utf-8", errors="replace")
238
+ parts.append(f"## {rel_path}\n\n```\n{content[:4000]}\n```")
239
+ except Exception:
240
+ pass
241
+ return "\n\n".join(parts)
242
+
243
+ def get_auto_context_text(self, base_dir: Optional[str] = None) -> str:
244
+ """Same as get_context_text but for auto_context files."""
245
+ base = pathlib.Path(base_dir or (self.source_path.parent if self.source_path else os.getcwd()))
246
+ parts: List[str] = []
247
+ for rel_path in self.auto_context:
248
+ p = base / rel_path
249
+ if p.exists() and p.is_file():
250
+ try:
251
+ content = p.read_text(encoding="utf-8", errors="replace")
252
+ # Show only first 80 lines to avoid blowing up context
253
+ lines = content.splitlines()[:80]
254
+ snippet = "\n".join(lines)
255
+ parts.append(f"## {rel_path} (first {len(lines)} lines)\n\n```python\n{snippet}\n```")
256
+ except Exception:
257
+ pass
258
+ return "\n\n".join(parts)
259
+
260
+ def build_system_prompt_block(self, base_dir: Optional[str] = None) -> str:
261
+ """
262
+ Build the full system-prompt injection block from this ariarc.
263
+ Returns empty string if nothing to inject.
264
+ """
265
+ lines: List[str] = []
266
+
267
+ if self.project:
268
+ lines.append(f"**Project:** {self.project}")
269
+ if self.description:
270
+ lines.append(f"**Description:** {self.description}")
271
+ if self.market != "global":
272
+ mkt = "A股 (Chinese equities)" if self.market == "cn" else self.market.upper()
273
+ lines.append(f"**Primary market:** {mkt}")
274
+ if self.default_symbols:
275
+ lines.append(f"**Default symbols:** {', '.join(self.default_symbols)}")
276
+ if self.ashare:
277
+ a = self.ashare
278
+ if a.get("risk_level"):
279
+ lines.append(f"**Risk preference:** {a['risk_level']}")
280
+
281
+ header = "\n".join(lines)
282
+ extra = self.system_prompt.strip()
283
+ ctx = self.get_context_text(base_dir)
284
+ auto = self.get_auto_context_text(base_dir)
285
+
286
+ parts: List[str] = []
287
+ if header:
288
+ parts.append(header)
289
+ if extra:
290
+ parts.append(extra)
291
+ if ctx:
292
+ parts.append("### Project Context Files\n\n" + ctx)
293
+ if auto:
294
+ parts.append("### Auto-loaded Code Context\n\n" + auto)
295
+
296
+ if not parts:
297
+ return ""
298
+
299
+ return "\n\n---\n\n# Project Context (.ariarc)\n\n" + "\n\n".join(parts)
300
+
301
+ def resolve_command(self, command: str, symbol: str = "", **kwargs) -> Optional[str]:
302
+ """
303
+ Resolve a custom command defined in .ariarc ``commands`` dict.
304
+
305
+ Example:
306
+ .ariarc: { "commands": { "/morning-cn": "生成A股早盘简报 {symbols}" } }
307
+ rc.resolve_command("/morning-cn")
308
+ → "生成A股早盘简报 sh600519, sh601318"
309
+ """
310
+ template = self.commands.get(command)
311
+ if template is None:
312
+ return None
313
+ syms = symbol or ", ".join(self.default_symbols)
314
+ return template.format(
315
+ symbol=symbol,
316
+ symbols=syms,
317
+ default_symbols=syms,
318
+ market=self.market,
319
+ **kwargs,
320
+ )
321
+
322
+ def is_write_denied(self, file_path: str) -> bool:
323
+ """Return True if writing to file_path is blocked by write_deny_patterns."""
324
+ import fnmatch
325
+ p = file_path.replace("\\", "/")
326
+ for pattern in self.write_deny_patterns:
327
+ if fnmatch.fnmatch(p, pattern) or fnmatch.fnmatch(pathlib.Path(p).name, pattern):
328
+ return True
329
+ return False
330
+
331
+ def to_dict(self) -> Dict[str, Any]:
332
+ return {
333
+ "source_path": str(self.source_path) if self.source_path else None,
334
+ "project": self.project,
335
+ "description": self.description,
336
+ "market": self.market,
337
+ "default_symbols": self.default_symbols,
338
+ "tools_whitelist": self.tools_whitelist,
339
+ "tools_blacklist": self.tools_blacklist,
340
+ "commands": list(self.commands.keys()),
341
+ "context_files": self.context_files,
342
+ "auto_context": self.auto_context,
343
+ "write_deny_patterns": self.write_deny_patterns,
344
+ }
345
+
346
+
347
+ # ---------------------------------------------------------------------------
348
+ # Module-level singleton
349
+ # ---------------------------------------------------------------------------
350
+
351
+ _current_rc: Optional[AriaRC] = None
352
+
353
+
354
+ def apply_to_config(config: Dict[str, Any], rc: Optional[AriaRC] = None) -> Dict[str, Any]:
355
+ """Overlay a project's ``.ariarc`` model settings onto *config*, in place.
356
+
357
+ Precedence is the point. The project file outranks the global default —
358
+ that is what makes "this repo runs on Vertex" true for everyone who opens
359
+ it — but it must not outrank a choice the user made in this session, or
360
+ ``/model`` would appear to do nothing in a repo that pins a model.
361
+ ``_model_pinned_by_session`` records that distinction for the UI.
362
+ """
363
+ arc = rc if rc is not None else get_ariarc()
364
+ if not arc.found:
365
+ return config
366
+ if arc.model and not config.get("_model_set_in_session"):
367
+ config["model"] = arc.model
368
+ config["_model_pinned_by_project"] = True
369
+ if arc.provider and not config.get("_model_set_in_session"):
370
+ config["local_provider"] = arc.provider
371
+ if arc.max_rounds:
372
+ config.setdefault("max_rounds", arc.max_rounds)
373
+ return config
374
+
375
+
376
+ def get_ariarc(reload: bool = False) -> AriaRC:
377
+ """Return the current session's AriaRC (lazy-loaded from cwd)."""
378
+ global _current_rc
379
+ if _current_rc is None or reload:
380
+ _current_rc = AriaRC.load()
381
+ return _current_rc
382
+
383
+
384
+ def reload_ariarc() -> AriaRC:
385
+ return get_ariarc(reload=True)