aria-code 0.167.0__py3-none-win_amd64.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 (601) hide show
  1. aria_bots/__init__.py +7 -0
  2. aria_bots/channels/README.md +97 -0
  3. aria_bots/channels/__init__.py +1 -0
  4. aria_bots/channels/approvals.py +263 -0
  5. aria_bots/channels/conversation.py +280 -0
  6. aria_bots/channels/digest.py +186 -0
  7. aria_bots/channels/registry.py +95 -0
  8. aria_bots/engine.py +144 -0
  9. aria_bots/feishu_bot.py +1385 -0
  10. aria_bots/logistics/__init__.py +1 -0
  11. aria_bots/logistics/carriers.py +253 -0
  12. aria_bots/logistics/freight.py +154 -0
  13. aria_bots/logistics/inventory.py +242 -0
  14. aria_bots/logistics/mcp_server.py +156 -0
  15. aria_bots/logistics/records.py +119 -0
  16. aria_bots/logistics/tenancy.py +152 -0
  17. aria_bots/market.py +91 -0
  18. aria_bots/relay_client.py +326 -0
  19. aria_bots/telegram_bot.py +299 -0
  20. aria_code/__init__.py +111 -0
  21. aria_code/_bin/aria-native.exe +0 -0
  22. aria_code/_bin/aria-runtime.exe +0 -0
  23. aria_code/_version.py +20 -0
  24. aria_code/account_memory.py +296 -0
  25. aria_code/account_session.py +166 -0
  26. aria_code/adk_apps/__init__.py +1 -0
  27. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  28. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  29. aria_code/adk_apps/aria_research/__init__.py +1 -0
  30. aria_code/adk_apps/aria_research/agent.py +31 -0
  31. aria_code/agents/__init__.py +49 -0
  32. aria_code/agents/base.py +329 -0
  33. aria_code/agents/code_review.py +200 -0
  34. aria_code/agents/deep/__init__.py +37 -0
  35. aria_code/agents/deep/calibration_loop.py +144 -0
  36. aria_code/agents/deep/critic.py +125 -0
  37. aria_code/agents/deep/deepen.py +193 -0
  38. aria_code/agents/deep/models.py +149 -0
  39. aria_code/agents/deep/pipeline.py +164 -0
  40. aria_code/agents/deep/quant_fusion.py +192 -0
  41. aria_code/agents/deep/themes.py +95 -0
  42. aria_code/agents/deep/tiers.py +106 -0
  43. aria_code/agents/engineering/__init__.py +14 -0
  44. aria_code/agents/engineering/coder.py +222 -0
  45. aria_code/agents/engineering/tester.py +145 -0
  46. aria_code/agents/financial/__init__.py +21 -0
  47. aria_code/agents/financial/cashflow_burn.py +119 -0
  48. aria_code/agents/financial/catalyst.py +279 -0
  49. aria_code/agents/financial/corporate_finance.py +175 -0
  50. aria_code/agents/financial/debate.py +224 -0
  51. aria_code/agents/financial/earnings.py +315 -0
  52. aria_code/agents/financial/fundamental.py +178 -0
  53. aria_code/agents/financial/macro.py +111 -0
  54. aria_code/agents/financial/news.py +231 -0
  55. aria_code/agents/financial/risk.py +146 -0
  56. aria_code/agents/financial/sector.py +279 -0
  57. aria_code/agents/financial/strategist.py +121 -0
  58. aria_code/agents/financial/stripe_revenue.py +127 -0
  59. aria_code/agents/financial/synthesis.py +274 -0
  60. aria_code/agents/financial/technical.py +277 -0
  61. aria_code/agents/orchestrator.py +44 -0
  62. aria_code/agents/portfolio_agent.py +392 -0
  63. aria_code/agents/realty/__init__.py +62 -0
  64. aria_code/agents/realty/asset_diagnosis.py +152 -0
  65. aria_code/agents/realty/business_match.py +166 -0
  66. aria_code/agents/realty/cashflow_verify.py +209 -0
  67. aria_code/agents/realty/contract_rules.py +210 -0
  68. aria_code/agents/realty/energy_anomaly.py +189 -0
  69. aria_code/agents/realty/exit_settlement.py +208 -0
  70. aria_code/agents/realty/fulfillment_risk.py +206 -0
  71. aria_code/agents/realty/ops_optimize.py +160 -0
  72. aria_code/agents/realty/revenue_share.py +215 -0
  73. aria_code/agents/registry.py +168 -0
  74. aria_code/agents/signal_scheme.py +111 -0
  75. aria_code/agents/sports/__init__.py +0 -0
  76. aria_code/agents/sports/football_agent.py +197 -0
  77. aria_code/agents/supervisor.py +113 -0
  78. aria_code/agents/team.py +500 -0
  79. aria_code/agents/video/__init__.py +16 -0
  80. aria_code/agents/video/director.py +376 -0
  81. aria_code/agents/video/qa_agent.py +143 -0
  82. aria_code/agents/video/script_doctor.py +112 -0
  83. aria_code/agents/warehouse/__init__.py +25 -0
  84. aria_code/agents/warehouse/contracts.py +26 -0
  85. aria_code/agents/warehouse/dashboard.py +155 -0
  86. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  87. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  88. aria_code/agents/warehouse/inventory_health.py +24 -0
  89. aria_code/agents/warehouse/local_run.py +71 -0
  90. aria_code/agents/warehouse/local_snapshot.py +132 -0
  91. aria_code/agents/warehouse/logistics_cost.py +84 -0
  92. aria_code/agents/warehouse/logistics_sync.py +21 -0
  93. aria_code/agents/warehouse/navigation.schema.json +110 -0
  94. aria_code/agents/warehouse/task_agent.py +172 -0
  95. aria_code/agents/warehouse/workflow.py +31 -0
  96. aria_code/aliyun_data_client.py +4 -0
  97. aria_code/apps/README.md +12 -0
  98. aria_code/apps/__init__.py +2 -0
  99. aria_code/apps/channels/README.md +9 -0
  100. aria_code/apps/channels/__init__.py +17 -0
  101. aria_code/apps/channels/approvals.py +11 -0
  102. aria_code/apps/channels/conversation.py +11 -0
  103. aria_code/apps/channels/digest.py +11 -0
  104. aria_code/apps/channels/intake.py +92 -0
  105. aria_code/apps/channels/registry.py +11 -0
  106. aria_code/apps/channels/tradingview.py +94 -0
  107. aria_code/apps/cli/README.md +13 -0
  108. aria_code/apps/cli/__init__.py +2 -0
  109. aria_code/apps/cli/_optional.py +120 -0
  110. aria_code/apps/cli/app_server.py +656 -0
  111. aria_code/apps/cli/auto_update.py +125 -0
  112. aria_code/apps/cli/bootstrap.py +206 -0
  113. aria_code/apps/cli/broker_render.py +206 -0
  114. aria_code/apps/cli/chat_turn.py +1327 -0
  115. aria_code/apps/cli/cloud_models.py +231 -0
  116. aria_code/apps/cli/codegen_paths.py +29 -0
  117. aria_code/apps/cli/commands/__init__.py +34 -0
  118. aria_code/apps/cli/commands/_business_render.py +78 -0
  119. aria_code/apps/cli/commands/_ui.py +71 -0
  120. aria_code/apps/cli/commands/analysis_cmds.py +378 -0
  121. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  122. aria_code/apps/cli/commands/auth_cmds.py +215 -0
  123. aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
  124. aria_code/apps/cli/commands/broker_cmds.py +1236 -0
  125. aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
  126. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  127. aria_code/apps/cli/commands/catalog.py +189 -0
  128. aria_code/apps/cli/commands/core_cmds.py +2748 -0
  129. aria_code/apps/cli/commands/data_cmds.py +521 -0
  130. aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
  131. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  132. aria_code/apps/cli/commands/engineering_cmds.py +399 -0
  133. aria_code/apps/cli/commands/file_cmds.py +312 -0
  134. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  135. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  136. aria_code/apps/cli/commands/market.py +544 -0
  137. aria_code/apps/cli/commands/market_cmds.py +1419 -0
  138. aria_code/apps/cli/commands/market_context.py +432 -0
  139. aria_code/apps/cli/commands/market_render.py +7 -0
  140. aria_code/apps/cli/commands/model_cmds.py +2374 -0
  141. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  142. aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
  143. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  144. aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
  145. aria_code/apps/cli/commands/report.py +476 -0
  146. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  147. aria_code/apps/cli/commands/session_cmds.py +236 -0
  148. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  149. aria_code/apps/cli/commands/team.py +723 -0
  150. aria_code/apps/cli/commands/team_render.py +8 -0
  151. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  152. aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
  153. aria_code/apps/cli/commands/workflow_cmds.py +629 -0
  154. aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
  155. aria_code/apps/cli/config_paths.py +73 -0
  156. aria_code/apps/cli/config_store.py +158 -0
  157. aria_code/apps/cli/constants.py +88 -0
  158. aria_code/apps/cli/context.py +34 -0
  159. aria_code/apps/cli/conversation_policy.py +53 -0
  160. aria_code/apps/cli/deterministic.py +200 -0
  161. aria_code/apps/cli/direct.py +48 -0
  162. aria_code/apps/cli/earnings_view.py +172 -0
  163. aria_code/apps/cli/endpoint_migration.py +64 -0
  164. aria_code/apps/cli/exec_events.py +255 -0
  165. aria_code/apps/cli/file_references.py +155 -0
  166. aria_code/apps/cli/finance_service_catalog.py +111 -0
  167. aria_code/apps/cli/football_reports.py +231 -0
  168. aria_code/apps/cli/github_app_auth.py +135 -0
  169. aria_code/apps/cli/google_login.py +239 -0
  170. aria_code/apps/cli/handlers/__init__.py +11 -0
  171. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  172. aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
  173. aria_code/apps/cli/handlers/market_handlers.py +2570 -0
  174. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  175. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  176. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  177. aria_code/apps/cli/headless.py +352 -0
  178. aria_code/apps/cli/helpers.py +326 -0
  179. aria_code/apps/cli/hooks.py +180 -0
  180. aria_code/apps/cli/i18n.py +310 -0
  181. aria_code/apps/cli/intent.py +136 -0
  182. aria_code/apps/cli/intent_router.py +305 -0
  183. aria_code/apps/cli/intent_shadow.py +72 -0
  184. aria_code/apps/cli/intent_signals.py +347 -0
  185. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  186. aria_code/apps/cli/local_tool_schemas.py +550 -0
  187. aria_code/apps/cli/main.py +39 -0
  188. aria_code/apps/cli/market_hours.py +100 -0
  189. aria_code/apps/cli/market_metadata.py +135 -0
  190. aria_code/apps/cli/market_universe.py +487 -0
  191. aria_code/apps/cli/message_processing.py +310 -0
  192. aria_code/apps/cli/mixin_binding.py +32 -0
  193. aria_code/apps/cli/model_catalog.py +652 -0
  194. aria_code/apps/cli/model_probe.py +169 -0
  195. aria_code/apps/cli/model_retirement.py +65 -0
  196. aria_code/apps/cli/native_branding.py +12 -0
  197. aria_code/apps/cli/native_bridge.py +118 -0
  198. aria_code/apps/cli/native_entry.py +58 -0
  199. aria_code/apps/cli/pdf_report.py +977 -0
  200. aria_code/apps/cli/plan_mode.py +139 -0
  201. aria_code/apps/cli/plotly_html.py +15 -0
  202. aria_code/apps/cli/prediction_feedback.py +202 -0
  203. aria_code/apps/cli/preflight.py +502 -0
  204. aria_code/apps/cli/project_aria.py +60 -0
  205. aria_code/apps/cli/prompt_assembly.py +78 -0
  206. aria_code/apps/cli/prompts/__init__.py +0 -0
  207. aria_code/apps/cli/prompts/coding.py +71 -0
  208. aria_code/apps/cli/prompts/select.py +193 -0
  209. aria_code/apps/cli/prompts/system_prompts.py +562 -0
  210. aria_code/apps/cli/prompts/ui.py +436 -0
  211. aria_code/apps/cli/provider_endpoints.py +237 -0
  212. aria_code/apps/cli/provider_keys.py +73 -0
  213. aria_code/apps/cli/providers/__init__.py +1 -0
  214. aria_code/apps/cli/providers/base.py +775 -0
  215. aria_code/apps/cli/providers/chat_routing.py +139 -0
  216. aria_code/apps/cli/providers/collaboration.py +98 -0
  217. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  218. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  219. aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
  220. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  221. aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
  222. aria_code/apps/cli/providers/runtime_bridge.py +586 -0
  223. aria_code/apps/cli/providers/vertexai_stream.py +592 -0
  224. aria_code/apps/cli/release_download.py +159 -0
  225. aria_code/apps/cli/review_runner.py +111 -0
  226. aria_code/apps/cli/runtime_consumer.py +769 -0
  227. aria_code/apps/cli/sandbox.py +32 -0
  228. aria_code/apps/cli/session_export.py +120 -0
  229. aria_code/apps/cli/session_jsonl.py +208 -0
  230. aria_code/apps/cli/session_refresh.py +123 -0
  231. aria_code/apps/cli/session_store.py +125 -0
  232. aria_code/apps/cli/skills_catalog.py +273 -0
  233. aria_code/apps/cli/task_isolation.py +211 -0
  234. aria_code/apps/cli/terminal_chrome.py +412 -0
  235. aria_code/apps/cli/todo_tracker.py +190 -0
  236. aria_code/apps/cli/tool_executor.py +932 -0
  237. aria_code/apps/cli/tool_registry.py +34 -0
  238. aria_code/apps/cli/tool_scope.py +113 -0
  239. aria_code/apps/cli/tools/__init__.py +40 -0
  240. aria_code/apps/cli/tools/context.py +46 -0
  241. aria_code/apps/cli/tools/file_tools.py +181 -0
  242. aria_code/apps/cli/tools/market_tools.py +626 -0
  243. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  244. aria_code/apps/cli/tools/system_tools.py +656 -0
  245. aria_code/apps/cli/tools/write_tools.py +961 -0
  246. aria_code/apps/cli/tradingview_bridge.py +529 -0
  247. aria_code/apps/cli/transactions.py +269 -0
  248. aria_code/apps/cli/turn_planning.py +137 -0
  249. aria_code/apps/cli/update_check.py +257 -0
  250. aria_code/apps/cli/updater.py +373 -0
  251. aria_code/apps/cli/utils/__init__.py +0 -0
  252. aria_code/apps/cli/utils/market_detect.py +1663 -0
  253. aria_code/apps/cli/web_login.py +136 -0
  254. aria_code/apps/cli/workflow_runner.py +127 -0
  255. aria_code/apps/cli/workspace_route.py +121 -0
  256. aria_code/apps/daemon/README.md +14 -0
  257. aria_code/apps/vscode/README.md +115 -0
  258. aria_code/apps/vscode/package.json +70 -0
  259. aria_code/aria_cli.py +5244 -0
  260. aria_code/aria_daemon.py +1372 -0
  261. aria_code/aria_feishu_bot.py +16 -0
  262. aria_code/aria_mcp_server.py +61 -0
  263. aria_code/aria_relay_client.py +4 -0
  264. aria_code/aria_telegram_bot.py +11 -0
  265. aria_code/ariarc.py +385 -0
  266. aria_code/artifacts.py +581 -0
  267. aria_code/backtest_engine.py +4 -0
  268. aria_code/backtest_report.py +4 -0
  269. aria_code/brokers/__init__.py +78 -0
  270. aria_code/brokers/automation.py +193 -0
  271. aria_code/brokers/base.py +231 -0
  272. aria_code/brokers/capabilities.py +264 -0
  273. aria_code/brokers/cn/__init__.py +10 -0
  274. aria_code/brokers/cn/easytrader_broker.py +193 -0
  275. aria_code/brokers/cn/futu_broker.py +194 -0
  276. aria_code/brokers/cn/longbridge_broker.py +190 -0
  277. aria_code/brokers/cn/tiger_broker.py +196 -0
  278. aria_code/brokers/cn/xtquant_broker.py +225 -0
  279. aria_code/brokers/config.py +414 -0
  280. aria_code/brokers/intl/__init__.py +5 -0
  281. aria_code/brokers/intl/alpaca_broker.py +183 -0
  282. aria_code/brokers/intl/ibkr_broker.py +215 -0
  283. aria_code/brokers/intl/trading212_broker.py +183 -0
  284. aria_code/brokers/intl/webull_broker.py +156 -0
  285. aria_code/brokers/paper_broker.py +266 -0
  286. aria_code/brokers/planning.py +296 -0
  287. aria_code/brokers/registry.py +213 -0
  288. aria_code/brokers/trading.py +267 -0
  289. aria_code/canva_client.py +4 -0
  290. aria_code/change_store.py +127 -0
  291. aria_code/clients/__init__.py +0 -0
  292. aria_code/clients/aliyun_data_client.py +660 -0
  293. aria_code/clients/aria_relay_client.py +16 -0
  294. aria_code/clients/canva_client.py +284 -0
  295. aria_code/clients/figma_client.py +100 -0
  296. aria_code/clients/football_data_client.py +1671 -0
  297. aria_code/clients/kling_video_client.py +182 -0
  298. aria_code/clients/market_data_client.py +1985 -0
  299. aria_code/clients/openai_image_client.py +242 -0
  300. aria_code/clients/runway_video_client.py +157 -0
  301. aria_code/clients/warehouse_erp_client.py +105 -0
  302. aria_code/cloud_memory.py +190 -0
  303. aria_code/command_safety.py +19 -0
  304. aria_code/computer_use_server.py +276 -0
  305. aria_code/computer_use_tools.py +4 -0
  306. aria_code/dashboard_generator.py +579 -0
  307. aria_code/data_analysis_tools.py +4 -0
  308. aria_code/data_cleaner.py +483 -0
  309. aria_code/data_service.py +513 -0
  310. aria_code/datasources/__init__.py +23 -0
  311. aria_code/datasources/base.py +166 -0
  312. aria_code/datasources/router.py +253 -0
  313. aria_code/datasources/sec_statements.py +217 -0
  314. aria_code/datasources/sources/__init__.py +15 -0
  315. aria_code/datasources/sources/akshare_source.py +269 -0
  316. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  317. aria_code/datasources/sources/edgar_source.py +219 -0
  318. aria_code/datasources/sources/finnhub_source.py +198 -0
  319. aria_code/datasources/sources/fred_source.py +223 -0
  320. aria_code/datasources/sources/tushare_source.py +143 -0
  321. aria_code/datasources/sources/web_scraper_source.py +278 -0
  322. aria_code/datasources/sources/world_bank_source.py +206 -0
  323. aria_code/datasources/sources/yfinance_source.py +152 -0
  324. aria_code/doctor.py +860 -0
  325. aria_code/domain/__init__.py +0 -0
  326. aria_code/domain/_finance_core/__init__.py +5 -0
  327. aria_code/domain/_finance_core/backtest_engine.py +325 -0
  328. aria_code/domain/_finance_core/performance_stats.py +101 -0
  329. aria_code/domain/backtest_engine.py +75 -0
  330. aria_code/domain/backtest_report.py +488 -0
  331. aria_code/domain/finance_formulas.py +389 -0
  332. aria_code/domain/performance_stats.py +6 -0
  333. aria_code/domain/portfolio_ledger.py +307 -0
  334. aria_code/domain/strategy_vault.py +421 -0
  335. aria_code/evals/__init__.py +37 -0
  336. aria_code/evals/behavior.py +113 -0
  337. aria_code/evals/gap.py +93 -0
  338. aria_code/evals/harness.py +949 -0
  339. aria_code/evals/review_probe.py +132 -0
  340. aria_code/evals/runner.py +425 -0
  341. aria_code/evals/scorecard.py +322 -0
  342. aria_code/evals/summary.py +125 -0
  343. aria_code/external_agent_runner.py +115 -0
  344. aria_code/figma_client.py +4 -0
  345. aria_code/file_analysis_tools.py +4 -0
  346. aria_code/finance_formulas.py +4 -0
  347. aria_code/football_data_client.py +4 -0
  348. aria_code/image_gen_tools.py +4 -0
  349. aria_code/intent_classifier.py +459 -0
  350. aria_code/kling_video_client.py +4 -0
  351. aria_code/licensing.py +151 -0
  352. aria_code/local_finance_tools.py +4 -0
  353. aria_code/local_image_provider.py +4 -0
  354. aria_code/local_llm_provider.py +4 -0
  355. aria_code/macro_tools.py +4 -0
  356. aria_code/markdown_pdf.py +289 -0
  357. aria_code/market_data_client.py +4 -0
  358. aria_code/mcp_client.py +744 -0
  359. aria_code/memory_manager.py +273 -0
  360. aria_code/model_capability.py +512 -0
  361. aria_code/notification_tools.py +4 -0
  362. aria_code/openai_image_client.py +4 -0
  363. aria_code/packages/__init__.py +26 -0
  364. aria_code/packages/adk_bridge/__init__.py +11 -0
  365. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  366. aria_code/packages/adk_bridge/market_tools.py +125 -0
  367. aria_code/packages/aria_agents/__init__.py +5 -0
  368. aria_code/packages/aria_agents/manifest.py +78 -0
  369. aria_code/packages/aria_core/__init__.py +34 -0
  370. aria_code/packages/aria_core/architecture.py +269 -0
  371. aria_code/packages/aria_core/export.py +147 -0
  372. aria_code/packages/aria_core/manifest.py +65 -0
  373. aria_code/packages/aria_core/paths.py +57 -0
  374. aria_code/packages/aria_core/secure_file.py +68 -0
  375. aria_code/packages/aria_infra/__init__.py +15 -0
  376. aria_code/packages/aria_infra/arthera.py +58 -0
  377. aria_code/packages/aria_infra/doctor.py +246 -0
  378. aria_code/packages/aria_infra/product.py +40 -0
  379. aria_code/packages/aria_mcp/__init__.py +25 -0
  380. aria_code/packages/aria_mcp/bridge.py +232 -0
  381. aria_code/packages/aria_mcp/circuit.py +82 -0
  382. aria_code/packages/aria_mcp/config.py +162 -0
  383. aria_code/packages/aria_mcp/installer.py +95 -0
  384. aria_code/packages/aria_mcp/server.py +1705 -0
  385. aria_code/packages/aria_mcp/tools.py +81 -0
  386. aria_code/packages/aria_sdk/__init__.py +19 -0
  387. aria_code/packages/aria_sdk/client.py +393 -0
  388. aria_code/packages/aria_sdk/providers.py +70 -0
  389. aria_code/packages/aria_sdk/streaming.py +75 -0
  390. aria_code/packages/aria_sdk/types.py +86 -0
  391. aria_code/packages/aria_services/__init__.py +92 -0
  392. aria_code/packages/aria_services/cache.py +60 -0
  393. aria_code/packages/aria_services/context.py +356 -0
  394. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  395. aria_code/packages/aria_services/data.py +11 -0
  396. aria_code/packages/aria_services/provider_health.py +304 -0
  397. aria_code/packages/aria_services/references.py +247 -0
  398. aria_code/packages/aria_services/registry.py +249 -0
  399. aria_code/packages/aria_services/research_protocol.py +137 -0
  400. aria_code/packages/aria_services/research_quality.py +186 -0
  401. aria_code/packages/aria_services/settings.py +160 -0
  402. aria_code/packages/aria_services/usage.py +151 -0
  403. aria_code/packages/aria_skills/__init__.py +46 -0
  404. aria_code/packages/aria_skills/catalog.py +149 -0
  405. aria_code/packages/aria_skills/loader.py +455 -0
  406. aria_code/packages/aria_skills/registry.py +84 -0
  407. aria_code/packages/aria_tools/__init__.py +5 -0
  408. aria_code/packages/aria_tools/financial/__init__.py +5 -0
  409. aria_code/packages/aria_tools/financial/_paths.py +92 -0
  410. aria_code/packages/aria_tools/registry.py +129 -0
  411. aria_code/packages/quant_engine/__init__.py +33 -0
  412. aria_code/packages/quant_engine/_namespace.py +24 -0
  413. aria_code/packages/quant_engine/backtest/__init__.py +10 -0
  414. aria_code/packages/quant_engine/backtest/core.py +53 -0
  415. aria_code/packages/quant_engine/backtest/engine.py +102 -0
  416. aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
  417. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  418. aria_code/packages/quant_engine/sports/__init__.py +76 -0
  419. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  420. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  421. aria_code/packages/quant_engine/sports/elo.py +300 -0
  422. aria_code/packages/quant_engine/sports/form.py +188 -0
  423. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  424. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  425. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  426. aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
  427. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  428. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  429. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  430. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  431. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  432. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  433. aria_code/packs/__init__.py +85 -0
  434. aria_code/packs/base.py +168 -0
  435. aria_code/packs/finance/__init__.py +250 -0
  436. aria_code/packs/healthcare/__init__.py +40 -0
  437. aria_code/packs/logistics/__init__.py +171 -0
  438. aria_code/packs/payments/__init__.py +139 -0
  439. aria_code/packs/realty/__init__.py +190 -0
  440. aria_code/packs/registry.py +202 -0
  441. aria_code/packs/rules.py +90 -0
  442. aria_code/plan_utils.py +194 -0
  443. aria_code/plugin_loader.py +328 -0
  444. aria_code/portfolio_ledger.py +4 -0
  445. aria_code/preview_server.py +449 -0
  446. aria_code/privacy/__init__.py +5 -0
  447. aria_code/privacy/feedback.py +123 -0
  448. aria_code/project_review.py +278 -0
  449. aria_code/project_review_api.py +235 -0
  450. aria_code/project_review_server.py +18 -0
  451. aria_code/project_tools.py +4 -0
  452. aria_code/providers/__init__.py +30 -0
  453. aria_code/providers/google_video_provider.py +256 -0
  454. aria_code/providers/llm/__init__.py +19 -0
  455. aria_code/providers/llm/anthropic.py +184 -0
  456. aria_code/providers/llm/arthera_gateway.py +90 -0
  457. aria_code/providers/llm/autoconfig.py +213 -0
  458. aria_code/providers/llm/base.py +139 -0
  459. aria_code/providers/llm/custom.py +158 -0
  460. aria_code/providers/llm/ollama.py +137 -0
  461. aria_code/providers/llm/openai_compat.py +350 -0
  462. aria_code/providers/llm/registry.py +457 -0
  463. aria_code/providers/local_image_provider.py +202 -0
  464. aria_code/providers/local_llm_provider.py +549 -0
  465. aria_code/providers/ollama_models.py +79 -0
  466. aria_code/providers/openai_params.py +32 -0
  467. aria_code/providers/tool_messages.py +100 -0
  468. aria_code/realty_data_tools.py +4 -0
  469. aria_code/report_exporters.py +150 -0
  470. aria_code/report_generator.py +1764 -0
  471. aria_code/review_service.py +497 -0
  472. aria_code/runtime/__init__.py +176 -0
  473. aria_code/runtime/acceptance.py +632 -0
  474. aria_code/runtime/agent_loop.py +1851 -0
  475. aria_code/runtime/agent_profiles.py +75 -0
  476. aria_code/runtime/approval.py +71 -0
  477. aria_code/runtime/baseline.py +219 -0
  478. aria_code/runtime/budget.py +232 -0
  479. aria_code/runtime/checkpoints.py +481 -0
  480. aria_code/runtime/consult.py +65 -0
  481. aria_code/runtime/contract.py +282 -0
  482. aria_code/runtime/delivery.py +432 -0
  483. aria_code/runtime/events.py +113 -0
  484. aria_code/runtime/gateway.py +167 -0
  485. aria_code/runtime/lsp.py +484 -0
  486. aria_code/runtime/native_index.py +153 -0
  487. aria_code/runtime/orchestrator.py +55 -0
  488. aria_code/runtime/processes.py +343 -0
  489. aria_code/runtime/project_graph.py +614 -0
  490. aria_code/runtime/repo_map.py +712 -0
  491. aria_code/runtime/research_bundle.py +105 -0
  492. aria_code/runtime/review.py +269 -0
  493. aria_code/runtime/run_state.py +127 -0
  494. aria_code/runtime/run_store.py +522 -0
  495. aria_code/runtime/scoped_runs.py +113 -0
  496. aria_code/runtime/self_healing.py +422 -0
  497. aria_code/runtime/semantic_diff.py +171 -0
  498. aria_code/runtime/subagent.py +721 -0
  499. aria_code/runtime/symbol_edit.py +117 -0
  500. aria_code/runtime/task_brief.py +46 -0
  501. aria_code/runtime/task_context.py +145 -0
  502. aria_code/runtime/task_ledger.py +108 -0
  503. aria_code/runtime/task_orchestrator.py +90 -0
  504. aria_code/runtime/task_outcome.py +33 -0
  505. aria_code/runtime/task_verification.py +266 -0
  506. aria_code/runtime/task_worktree.py +448 -0
  507. aria_code/runtime/tool_executor.py +329 -0
  508. aria_code/runtime/tool_policy.py +107 -0
  509. aria_code/runtime/transactions.py +216 -0
  510. aria_code/runtime/windows_job.py +115 -0
  511. aria_code/runtime/workflows.py +222 -0
  512. aria_code/runtime/worktrees.py +215 -0
  513. aria_code/runway_video_client.py +4 -0
  514. aria_code/safety/__init__.py +27 -0
  515. aria_code/safety/client_isolation.py +178 -0
  516. aria_code/safety/permissions.py +339 -0
  517. aria_code/safety/risk.py +699 -0
  518. aria_code/safety/sandbox.py +148 -0
  519. aria_code/safety/service.py +102 -0
  520. aria_code/service_endpoints.py +14 -0
  521. aria_code/setup_wizard.py +684 -0
  522. aria_code/spreadsheet_tools.py +4 -0
  523. aria_code/strategy_vault.py +4 -0
  524. aria_code/tools/__init__.py +0 -0
  525. aria_code/tools/artifact_tools.py +252 -0
  526. aria_code/tools/broker_tools.py +92 -0
  527. aria_code/tools/build_quant_engine.py +97 -0
  528. aria_code/tools/code_audit_tools.py +192 -0
  529. aria_code/tools/computer_use_tools.py +509 -0
  530. aria_code/tools/data_analysis_tools.py +831 -0
  531. aria_code/tools/enterprise_finance_tools.py +202 -0
  532. aria_code/tools/extended_tools.py +240 -0
  533. aria_code/tools/file_analysis_tools.py +880 -0
  534. aria_code/tools/image_gen_tools.py +114 -0
  535. aria_code/tools/local_finance_tools.py +3488 -0
  536. aria_code/tools/logistics_carriers.py +11 -0
  537. aria_code/tools/logistics_inventory.py +11 -0
  538. aria_code/tools/logistics_io.py +11 -0
  539. aria_code/tools/logistics_tenancy.py +11 -0
  540. aria_code/tools/logistics_tools.py +11 -0
  541. aria_code/tools/macro_tools.py +412 -0
  542. aria_code/tools/notification_tools.py +249 -0
  543. aria_code/tools/project_tools.py +525 -0
  544. aria_code/tools/realty_data_tools.py +662 -0
  545. aria_code/tools/spreadsheet_tools.py +399 -0
  546. aria_code/tools/stripe_tools.py +115 -0
  547. aria_code/tools/video_tools.py +500 -0
  548. aria_code/training/__init__.py +1 -0
  549. aria_code/training/compare.py +117 -0
  550. aria_code/training/prelude_commands.py +111 -0
  551. aria_code/training/prelude_eval.py +390 -0
  552. aria_code/training/prelude_real.py +183 -0
  553. aria_code/training/prelude_train.py +248 -0
  554. aria_code/training/sft_dataset.py +230 -0
  555. aria_code/training/trajectory_dataset.py +263 -0
  556. aria_code/ui/__init__.py +105 -0
  557. aria_code/ui/banner.py +404 -0
  558. aria_code/ui/completer.py +571 -0
  559. aria_code/ui/console.py +285 -0
  560. aria_code/ui/input_box.py +505 -0
  561. aria_code/ui/mentions.py +206 -0
  562. aria_code/ui/picker.py +248 -0
  563. aria_code/ui/render/__init__.py +11 -0
  564. aria_code/ui/render/actions.py +297 -0
  565. aria_code/ui/render/finance.py +1732 -0
  566. aria_code/ui/render/market.py +253 -0
  567. aria_code/ui/render/output.py +954 -0
  568. aria_code/ui/render/responsive.py +75 -0
  569. aria_code/ui/render/team.py +367 -0
  570. aria_code/ui/robot.py +239 -0
  571. aria_code/ui/startup_dashboard.py +110 -0
  572. aria_code/video_analysis.py +147 -0
  573. aria_code/video_editor.py +518 -0
  574. aria_code/video_workflow/__init__.py +8 -0
  575. aria_code/video_workflow/__main__.py +61 -0
  576. aria_code/video_workflow/engines/__init__.py +18 -0
  577. aria_code/video_workflow/engines/base.py +69 -0
  578. aria_code/video_workflow/engines/custom_model.py +204 -0
  579. aria_code/video_workflow/engines/google_veo.py +94 -0
  580. aria_code/video_workflow/engines/local_engine.py +71 -0
  581. aria_code/video_workflow/engines/registry.py +98 -0
  582. aria_code/video_workflow/google_models.py +278 -0
  583. aria_code/video_workflow/music/__init__.py +5 -0
  584. aria_code/video_workflow/music/foley_service.py +132 -0
  585. aria_code/video_workflow/music/service.py +212 -0
  586. aria_code/video_workflow/pipeline.py +620 -0
  587. aria_code/video_workflow/plan.py +202 -0
  588. aria_code/video_workflow/session.py +297 -0
  589. aria_code/workspace/__init__.py +6 -0
  590. aria_code/workspace/files.py +219 -0
  591. aria_code/workspace/verify.py +167 -0
  592. aria_code/workspace_changes.py +199 -0
  593. aria_code/workspace_service.py +196 -0
  594. aria_code/workspace_tools.py +150 -0
  595. aria_code-0.167.0.dist-info/METADATA +330 -0
  596. aria_code-0.167.0.dist-info/RECORD +601 -0
  597. aria_code-0.167.0.dist-info/WHEEL +5 -0
  598. aria_code-0.167.0.dist-info/entry_points.txt +5 -0
  599. aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
  600. aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
  601. aria_code-0.167.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,614 @@
1
+ """Project graph — what a change reaches before it is made.
2
+
3
+ ``repo_map`` answers "where is X defined and who mentions it" for one name at
4
+ a time. Deciding whether an edit is safe needs the other direction, walked:
5
+ change ``session.py`` and which files import it, which import those, which
6
+ tests exercise any of them, which deployable service ships them. That is what
7
+ this builds, from four kinds of node and the edges between them:
8
+
9
+ * files, and the symbols they define (``repo_map``'s scan);
10
+ * ``imports`` between files — Python resolved with ``ast``, JS/TS relative
11
+ specifiers resolved against the tree; other languages fall back to
12
+ references;
13
+ * ``references`` — a file naming a symbol another file defines, using the
14
+ same filters that keep ``repo_map``'s ranking honest (no names under four
15
+ characters, none defined in more than a handful of files);
16
+ * tests (files under ``tests/``, ``test_*``, ``*.test.ts`` …), which cover
17
+ what they import or reference;
18
+
19
+ Impact follows imports up to three hops but not through a package
20
+ ``__init__`` or a file importing more than twenty others: past those,
21
+ everything reaches everything.
22
+ * services — a ``docker-compose`` service's build context, or a directory
23
+ with its own ``pyproject.toml`` / ``package.json`` / ``go.mod`` /
24
+ ``Cargo.toml`` — which contain the files below them.
25
+
26
+ The graph is saved per repository under ``<aria home>/graphs``. A session
27
+ that finds every file at the size and mtime it was saved with reuses it after
28
+ a stat per file; otherwise it is rebuilt, re-parsing only changed files'
29
+ imports.
30
+ """
31
+
32
+ from __future__ import annotations
33
+
34
+ import ast
35
+ import hashlib
36
+ import json
37
+ import os
38
+ import re
39
+ import time
40
+ from collections import deque
41
+ from dataclasses import dataclass, field
42
+ from pathlib import Path
43
+ from typing import Iterable, Optional, Sequence
44
+
45
+ from . import native_index
46
+ from .repo_map import RepoMap
47
+
48
+ GRAPH_VERSION = 1
49
+
50
+ _TEST_PATH = re.compile(r"(^|/)(tests?|__tests__|spec)/|(^|/)test_[^/]+$|_test\.[a-z]+$|\.(test|spec)\.[a-z]+$")
51
+ _JS_IMPORT = re.compile(
52
+ r"""(?:\bimport\s[^'"]*?\bfrom\s*|\bimport\s*\(\s*|\brequire\s*\(\s*|\bexport\s[^'"]*?\bfrom\s*|\bimport\s+)"""
53
+ r"""['"](\.{1,2}/[^'"]+)['"]"""
54
+ )
55
+ _JS_EXTENSIONS = (".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".vue", ".svelte")
56
+ _PACKAGE_MARKERS = ("pyproject.toml", "setup.py", "package.json", "go.mod", "Cargo.toml")
57
+ _COMPOSE_FILES = ("docker-compose.yml", "docker-compose.yaml", "compose.yml", "compose.yaml")
58
+
59
+ # Same thresholds as repo_map's ranking: a short or widely defined name says
60
+ # nothing about which file a mention depends on.
61
+ _MIN_NAME = 4
62
+ _MAX_DEFINING_FILES = 3
63
+ _TRANSITIVE_DEPTH = 3
64
+ # A file importing this many others is an aggregator (a CLI entry point, an
65
+ # app factory). It is reached, but the walk does not continue through it, nor
66
+ # through a package ``__init__``: past either, everything imports everything
67
+ # and "reached through imports" would list the whole repository.
68
+ _HUB_FAN_OUT = 20
69
+
70
+
71
+ def is_test_path(path: str) -> bool:
72
+ return bool(_TEST_PATH.search(path.replace("\\", "/")))
73
+
74
+
75
+ @dataclass
76
+ class Impact:
77
+ targets: list[str]
78
+ unresolved: list[str] = field(default_factory=list)
79
+ direct: list[str] = field(default_factory=list) # import or reference a target
80
+ indirect: list[str] = field(default_factory=list) # import those, up to three hops
81
+ tests: list[str] = field(default_factory=list)
82
+ services: list[str] = field(default_factory=list)
83
+
84
+ @property
85
+ def breadth(self) -> str:
86
+ reach = len(self.direct) + len(self.indirect)
87
+ if reach == 0:
88
+ return "isolated"
89
+ if reach <= 5:
90
+ return "narrow"
91
+ if reach <= 25:
92
+ return "moderate"
93
+ return "wide"
94
+
95
+ def as_dict(self, *, limit: int = 40) -> dict:
96
+ return {
97
+ "targets": self.targets,
98
+ "unresolved": self.unresolved,
99
+ "breadth": self.breadth,
100
+ "direct_dependents": self.direct[:limit],
101
+ "indirect_dependents": self.indirect[:limit],
102
+ "tests": self.tests[:limit],
103
+ "services": self.services,
104
+ "counts": {"direct": len(self.direct), "indirect": len(self.indirect),
105
+ "tests": len(self.tests)},
106
+ }
107
+
108
+ def render(self, *, limit: int = 12) -> str:
109
+ def block(title: str, items: Sequence[str]) -> list[str]:
110
+ if not items:
111
+ return []
112
+ shown = [f" {item}" for item in items[:limit]]
113
+ if len(items) > limit:
114
+ shown.append(f" … and {len(items) - limit} more")
115
+ return [f" {title} ({len(items)})", *shown]
116
+
117
+ lines = [f"Impact of {', '.join(self.targets) or '(nothing)'}: {self.breadth}"]
118
+ if self.unresolved:
119
+ lines.append(f" not found: {', '.join(self.unresolved)}")
120
+ lines += block("Used directly by", self.direct)
121
+ lines += block("Reached through imports", self.indirect)
122
+ lines += block("Tests", self.tests)
123
+ if self.services:
124
+ lines.append(f" Services {', '.join(self.services)}")
125
+ if not (self.direct or self.indirect or self.tests):
126
+ lines.append(" Nothing else in the repository depends on it.")
127
+ return "\n".join(lines)
128
+
129
+
130
+ class ProjectGraph:
131
+ def __init__(self, root: Path | str) -> None:
132
+ self.root = Path(root).expanduser().resolve()
133
+ self.files: dict[str, dict] = {} # path -> {mtime, size, language, imports, symbols}
134
+ self.services: dict[str, str] = {} # name -> directory ("" for the root)
135
+ self.refs: dict[str, list[str]] = {} # symbol name -> files mentioning it
136
+ self.defs: dict[str, list[str]] = {} # symbol name -> files defining it
137
+ self.built_at = 0.0
138
+
139
+ # ── building ───────────────────────────────────────────────────────────
140
+
141
+ def build(self, previous: Optional["ProjectGraph"] = None) -> "ProjectGraph":
142
+ repo = RepoMap(self.root).build()
143
+ prior = previous.files if previous is not None else {}
144
+ modules = _module_index(repo.files)
145
+ stale = [path for path, entry in repo.files.items()
146
+ if not ((cached := prior.get(path))
147
+ and cached.get("mtime") == entry.mtime and cached.get("size") == entry.size)]
148
+ native = native_index.resolve_imports(
149
+ self.root, [(path, entry.language) for path, entry in repo.files.items()], stale)
150
+ resolved, python_side = native if native is not None else ({}, stale)
151
+ for path in python_side:
152
+ entry = repo.files[path]
153
+ resolved[path] = _imports(self.root, path, entry.language, modules, repo.files)
154
+ files: dict[str, dict] = {}
155
+ for path, entry in repo.files.items():
156
+ if path in resolved:
157
+ imports = resolved[path]
158
+ else:
159
+ imports = prior[path].get("imports") or []
160
+ files[path] = {
161
+ "mtime": entry.mtime, "size": entry.size, "language": entry.language,
162
+ "imports": sorted(set(imports) - {path}),
163
+ "symbols": [[s.name, s.kind, s.line, s.parent] for s in entry.symbols],
164
+ }
165
+ self.files = files
166
+ self.defs = {name: sorted({path for path, _symbol in places}) for name, places in repo.defs.items()}
167
+ self.refs = {name: sorted(paths) for name, paths in repo.refs.items()
168
+ if len(name) >= _MIN_NAME and len(self.defs.get(name, ())) <= _MAX_DEFINING_FILES}
169
+ self.services = _services(self.root, files)
170
+ self.built_at = time.time()
171
+ return self
172
+
173
+ def is_current(self) -> bool:
174
+ """Every saved file still at its size and mtime, and no new ones."""
175
+ repo = RepoMap(self.root)
176
+ seen = 0
177
+ for path in repo._walk():
178
+ try:
179
+ rel = str(path.relative_to(self.root))
180
+ stat = path.stat()
181
+ except (OSError, ValueError):
182
+ return False
183
+ if stat.st_size > repo.max_file_bytes:
184
+ continue
185
+ saved = self.files.get(rel)
186
+ if saved is None or saved.get("mtime") != stat.st_mtime or saved.get("size") != stat.st_size:
187
+ return False
188
+ seen += 1
189
+ return seen == len(self.files)
190
+
191
+ # ── persistence ────────────────────────────────────────────────────────
192
+
193
+ def to_dict(self) -> dict:
194
+ return {"version": GRAPH_VERSION, "root": str(self.root), "built_at": self.built_at,
195
+ "files": self.files, "services": self.services, "refs": self.refs, "defs": self.defs}
196
+
197
+ @classmethod
198
+ def from_dict(cls, data: dict) -> "ProjectGraph":
199
+ graph = cls(data["root"])
200
+ graph.files = dict(data.get("files") or {})
201
+ graph.services = dict(data.get("services") or {})
202
+ graph.refs = dict(data.get("refs") or {})
203
+ graph.defs = dict(data.get("defs") or {})
204
+ graph.built_at = float(data.get("built_at") or 0.0)
205
+ return graph
206
+
207
+ # ── edges ──────────────────────────────────────────────────────────────
208
+
209
+ def importers(self) -> dict[str, set[str]]:
210
+ reverse: dict[str, set[str]] = {}
211
+ for path, info in self.files.items():
212
+ for target in info.get("imports") or ():
213
+ reverse.setdefault(target, set()).add(path)
214
+ return reverse
215
+
216
+ def service_of(self, path: str) -> Optional[str]:
217
+ best, best_len = None, -1
218
+ for name, directory in self.services.items():
219
+ if directory == "" or path == directory or path.startswith(directory.rstrip("/") + "/"):
220
+ if len(directory) > best_len:
221
+ best, best_len = name, len(directory)
222
+ return best
223
+
224
+ def _is_hub(self, path: str) -> bool:
225
+ return (Path(path).name == "__init__.py"
226
+ or len(self.files.get(path, {}).get("imports") or ()) > _HUB_FAN_OUT)
227
+
228
+ def _symbol_users(self, dotted: str) -> tuple[set[str], set[str]]:
229
+ """(defining files, mentioning files) for ``name`` or ``Class.method``."""
230
+ parts = [part for part in dotted.split(".") if part]
231
+ if not parts:
232
+ return set(), set()
233
+ defining = set(self.defs.get(parts[-1], ()))
234
+ users = set(self.refs.get(parts[-1], ()))
235
+ for outer in parts[:-1]:
236
+ users &= set(self.refs.get(outer, ()))
237
+ defining &= set(self.defs.get(outer, ())) or defining
238
+ return defining, users
239
+
240
+ def _file_users(self, path: str) -> set[str]:
241
+ users = set(self.importers().get(path, ()))
242
+ for name, _kind, _line, parent in self.files.get(path, {}).get("symbols") or ():
243
+ if parent:
244
+ continue # a method is reached through its class
245
+ if path in self.defs.get(name, ()) and name in self.refs:
246
+ users |= set(self.refs[name])
247
+ users.discard(path)
248
+ return users
249
+
250
+ def _resolve(self, target: str) -> Optional[str]:
251
+ raw = target.strip()
252
+ candidate = Path(raw).expanduser()
253
+ if candidate.is_absolute():
254
+ try:
255
+ raw = str(candidate.resolve().relative_to(self.root))
256
+ except ValueError:
257
+ return None
258
+ raw = raw.removeprefix("./")
259
+ if raw in self.files:
260
+ return raw
261
+ matches = [path for path in self.files if path.endswith("/" + raw)]
262
+ return matches[0] if len(matches) == 1 else None
263
+
264
+ # ── impact ─────────────────────────────────────────────────────────────
265
+
266
+ def impact(self, targets: Iterable[str]) -> Impact:
267
+ wanted = [str(t).strip() for t in targets if str(t).strip()]
268
+ result = Impact(targets=wanted)
269
+ origin: set[str] = set()
270
+ changed_files: set[str] = set() # walked from; a symbol's file is not
271
+ direct: set[str] = set()
272
+ for target in wanted:
273
+ path = self._resolve(target)
274
+ if path is not None:
275
+ origin.add(path)
276
+ changed_files.add(path)
277
+ direct |= self._file_users(path)
278
+ continue
279
+ # Importing the file that defines a symbol is not using it: only
280
+ # files that name it are affected, and the walk starts from them.
281
+ defining, users = self._symbol_users(target)
282
+ if defining or users:
283
+ origin |= defining
284
+ direct |= users
285
+ else:
286
+ result.unresolved.append(target)
287
+ direct -= origin
288
+
289
+ importers = self.importers()
290
+ indirect: set[str] = set()
291
+ frontier = deque((path, 1) for path in direct | changed_files)
292
+ visited = set(direct | origin)
293
+ while frontier:
294
+ path, depth = frontier.popleft()
295
+ if depth > _TRANSITIVE_DEPTH or (path not in changed_files and self._is_hub(path)):
296
+ continue
297
+ for parent in importers.get(path, ()):
298
+ if parent not in visited:
299
+ visited.add(parent)
300
+ indirect.add(parent)
301
+ frontier.append((parent, depth + 1))
302
+
303
+ reached = origin | direct | indirect
304
+ result.direct = sorted(direct)
305
+ result.indirect = sorted(indirect - direct)
306
+ result.tests = sorted(path for path in (direct | indirect) if is_test_path(path))
307
+ result.services = sorted({s for s in (self.service_of(p) for p in reached) if s})
308
+ return result
309
+
310
+ def summary(self) -> dict:
311
+ edges = sum(len(info.get("imports") or ()) for info in self.files.values())
312
+ return {"files": len(self.files), "tests": sum(1 for p in self.files if is_test_path(p)),
313
+ "import_edges": edges, "symbols": len(self.defs), "services": dict(self.services),
314
+ "built_at": self.built_at}
315
+
316
+
317
+ # ── imports ────────────────────────────────────────────────────────────────
318
+
319
+ def _module_index(files) -> dict[str, list[str]]:
320
+ """Dotted module names, every suffix of the path, mapped to Python files.
321
+
322
+ ``src/aria_code/runtime/agent.py`` is ``agent``, ``runtime.agent``,
323
+ ``aria_code.runtime.agent`` and ``src.aria_code.runtime.agent``: which of
324
+ those an import uses depends on what the project puts on its path.
325
+ """
326
+ index: dict[str, list[str]] = {}
327
+ for path, entry in files.items():
328
+ if entry.language != "python":
329
+ continue
330
+ parts = list(Path(path).with_suffix("").parts)
331
+ if parts and parts[-1] == "__init__":
332
+ parts = parts[:-1]
333
+ for start in range(len(parts)):
334
+ index.setdefault(".".join(parts[start:]), []).append(path)
335
+ return index
336
+
337
+
338
+ def _pick(candidates: Sequence[str], importer: str) -> Optional[str]:
339
+ if not candidates:
340
+ return None
341
+ if len(candidates) == 1:
342
+ return candidates[0]
343
+ # Several files answer to a short name: prefer the one sharing most of the
344
+ # importer's directory.
345
+ importer_parts = Path(importer).parts
346
+ def shared(path: str) -> int:
347
+ count = 0
348
+ for a, b in zip(Path(path).parts, importer_parts):
349
+ if a != b:
350
+ break
351
+ count += 1
352
+ return count
353
+ ranked = sorted(candidates, key=shared, reverse=True)
354
+ return ranked[0] if shared(ranked[0]) > shared(ranked[1]) else None
355
+
356
+
357
+ def _python_imports(root: Path, path: str, modules: dict) -> list[str]:
358
+ try:
359
+ tree = ast.parse((root / path).read_text(encoding="utf-8-sig", errors="replace"))
360
+ except (SyntaxError, ValueError, OSError, RecursionError):
361
+ return []
362
+ package = list(Path(path).parent.parts)
363
+ found: list[str] = []
364
+
365
+ def resolve(name: str) -> Optional[str]:
366
+ return _pick(modules.get(name, ()), path)
367
+
368
+ for node in ast.walk(tree):
369
+ if isinstance(node, ast.Import):
370
+ for alias in node.names:
371
+ hit = resolve(alias.name)
372
+ if hit:
373
+ found.append(hit)
374
+ elif isinstance(node, ast.ImportFrom):
375
+ if node.level:
376
+ base_parts = package[: len(package) - (node.level - 1)] if node.level > 1 else package
377
+ base = ".".join(base_parts + ([node.module] if node.module else []))
378
+ else:
379
+ base = node.module or ""
380
+ for alias in node.names:
381
+ hit = resolve(f"{base}.{alias.name}" if base else alias.name) or (resolve(base) if base else None)
382
+ if hit:
383
+ found.append(hit)
384
+ return found
385
+
386
+
387
+ def _js_imports(root: Path, path: str, files) -> list[str]:
388
+ try:
389
+ source = (root / path).read_text(encoding="utf-8-sig", errors="replace")
390
+ except OSError:
391
+ return []
392
+ found = []
393
+ folder = Path(path).parent
394
+ for spec in _JS_IMPORT.findall(source):
395
+ base = os.path.normpath(str(folder / spec))
396
+ candidates = [base, *(base + ext for ext in _JS_EXTENSIONS),
397
+ *(os.path.join(base, f"index{ext}") for ext in _JS_EXTENSIONS)]
398
+ hit = next((c for c in candidates if c in files), None)
399
+ if hit:
400
+ found.append(hit)
401
+ return found
402
+
403
+
404
+ def _imports(root: Path, path: str, language: str, modules: dict, files) -> list[str]:
405
+ if language == "python":
406
+ return _python_imports(root, path, modules)
407
+ if path.endswith(_JS_EXTENSIONS):
408
+ return _js_imports(root, path, files)
409
+ return []
410
+
411
+
412
+ # ── services ───────────────────────────────────────────────────────────────
413
+
414
+ def _services(root: Path, files: dict) -> dict[str, str]:
415
+ services: dict[str, str] = {}
416
+ for name in _COMPOSE_FILES:
417
+ compose = root / name
418
+ if not compose.is_file():
419
+ continue
420
+ try:
421
+ import yaml
422
+
423
+ data = yaml.safe_load(compose.read_text(encoding="utf-8")) or {}
424
+ except Exception:
425
+ continue
426
+ for service, spec in (data.get("services") or {}).items():
427
+ build = (spec or {}).get("build") if isinstance(spec, dict) else None
428
+ context = build.get("context") if isinstance(build, dict) else build
429
+ if isinstance(context, str):
430
+ directory = os.path.normpath(context)
431
+ services[str(service)] = "" if directory in (".", "") else directory
432
+ directories = {str(parent) for p in files for parent in Path(p).parents}
433
+ for directory in sorted(directories):
434
+ if directory in (".", "") or directory in services.values():
435
+ continue
436
+ if any((root / directory / marker).is_file() for marker in _PACKAGE_MARKERS):
437
+ services.setdefault(Path(directory).name, directory)
438
+ return services
439
+
440
+
441
+ # ── loading ────────────────────────────────────────────────────────────────
442
+
443
+ def _graph_file(root: Path) -> Path:
444
+ from ..packages.aria_core.paths import aria_home
445
+
446
+ digest = hashlib.sha1(str(root).encode("utf-8")).hexdigest()[:12]
447
+ return aria_home() / "graphs" / f"{root.name}-{digest}.json"
448
+
449
+
450
+ _CACHE: dict[str, ProjectGraph] = {}
451
+
452
+
453
+ def load_project_graph(root: Path | str = ".", *, refresh: bool = True) -> ProjectGraph:
454
+ """The graph for ``root``: from memory, then disk, rebuilt when stale."""
455
+ resolved = Path(root).expanduser().resolve()
456
+ key = str(resolved)
457
+ graph = _CACHE.get(key)
458
+ path = _graph_file(resolved)
459
+ if graph is None:
460
+ try:
461
+ data = json.loads(path.read_text(encoding="utf-8"))
462
+ if data.get("version") == GRAPH_VERSION and data.get("root") == key:
463
+ graph = ProjectGraph.from_dict(data)
464
+ except (OSError, ValueError, KeyError, TypeError):
465
+ graph = None
466
+ if graph is not None and (not refresh or graph.is_current()):
467
+ _CACHE[key] = graph
468
+ return graph
469
+ fresh = ProjectGraph(resolved).build(previous=graph)
470
+ _CACHE[key] = fresh
471
+ try:
472
+ path.parent.mkdir(parents=True, exist_ok=True)
473
+ temp = path.with_suffix(".tmp")
474
+ temp.write_text(json.dumps(fresh.to_dict()), encoding="utf-8")
475
+ os.replace(temp, path)
476
+ except OSError:
477
+ pass
478
+ return fresh
479
+
480
+
481
+ def clear_cache() -> None:
482
+ _CACHE.clear()
483
+
484
+
485
+ def cached_graph(root: Path | str) -> Optional[ProjectGraph]:
486
+ """The graph already in memory or on disk; never builds one.
487
+
488
+ For approval cards and reports, which must not stall on a first scan.
489
+ It may be a little stale, which is fine for a hint.
490
+ """
491
+ resolved = Path(root).expanduser().resolve()
492
+ graph = _CACHE.get(str(resolved))
493
+ if graph is not None:
494
+ return graph
495
+ try:
496
+ data = json.loads(_graph_file(resolved).read_text(encoding="utf-8"))
497
+ if data.get("version") == GRAPH_VERSION and data.get("root") == str(resolved):
498
+ graph = ProjectGraph.from_dict(data)
499
+ _CACHE[str(resolved)] = graph
500
+ return graph
501
+ except (OSError, ValueError, KeyError, TypeError):
502
+ pass
503
+ return None
504
+
505
+
506
+ def _repository_of(path: Path) -> Optional[tuple[Path, str]]:
507
+ """``(repository root, path inside it)`` — a task worktree maps to its repository."""
508
+ import subprocess
509
+
510
+ folder = path if path.is_dir() else path.parent
511
+ while not folder.exists() and folder != folder.parent:
512
+ folder = folder.parent
513
+ try:
514
+ out = subprocess.run(["git", "rev-parse", "--show-toplevel", "--git-common-dir"], cwd=folder,
515
+ capture_output=True, text=True, timeout=5, check=True).stdout.splitlines()
516
+ except (OSError, subprocess.SubprocessError):
517
+ out = []
518
+ if len(out) < 2:
519
+ # Not a git repository: the nearest folder that has a graph.
520
+ for parent in [folder, *folder.parents]:
521
+ if str(parent.resolve()) in _CACHE or _graph_file(parent.resolve()).is_file():
522
+ try:
523
+ return parent.resolve(), str(path.resolve().relative_to(parent.resolve()))
524
+ except ValueError:
525
+ return None
526
+ return None
527
+ top = Path(out[0]).resolve()
528
+ common = Path(out[1])
529
+ common = (folder / common).resolve() if not common.is_absolute() else common.resolve()
530
+ origin = common.parent if common.name == ".git" else top
531
+ try:
532
+ return origin, str(path.resolve().relative_to(top))
533
+ except ValueError:
534
+ return None
535
+
536
+
537
+ def impact_of_paths(paths: Sequence[str]) -> Optional[Impact]:
538
+ """Impact of changing these absolute paths, from a cached graph only."""
539
+ targets: list[str] = []
540
+ graph: Optional[ProjectGraph] = None
541
+ for raw in paths:
542
+ located = _repository_of(Path(raw))
543
+ if located is None:
544
+ continue
545
+ root, relative = located
546
+ candidate = cached_graph(root)
547
+ if candidate is None or (graph is not None and candidate.root != graph.root):
548
+ continue
549
+ graph = candidate
550
+ targets.append(relative)
551
+ if graph is None or not targets:
552
+ return None
553
+ return graph.impact(targets)
554
+
555
+
556
+ def impact_line(impact: Optional[Impact]) -> str:
557
+ """One line for a card or report: ``9 files · 7 tests · api``."""
558
+ if impact is None or not impact.targets or impact.unresolved == impact.targets:
559
+ return ""
560
+ reach = len(impact.direct) + len(impact.indirect)
561
+ parts = [f"{impact.breadth}", f"{reach} file{'s' if reach != 1 else ''} depend on it",
562
+ f"{len(impact.tests)} test{'s' if len(impact.tests) != 1 else ''}"]
563
+ if impact.services:
564
+ parts.append(", ".join(impact.services))
565
+ return " · ".join(parts)
566
+
567
+
568
+ # ── model-facing tool ──────────────────────────────────────────────────────
569
+
570
+ def tool_impact_analysis(params: dict) -> dict:
571
+ """What a change to these files or symbols reaches, before making it."""
572
+ try:
573
+ targets = params.get("targets") or params.get("target") or []
574
+ if isinstance(targets, str):
575
+ targets = [targets]
576
+ targets = [str(t) for t in targets if str(t).strip()]
577
+ if not targets:
578
+ return {"success": False, "error": "Missing 'targets': file paths or symbol names"}
579
+ graph = load_project_graph(params.get("path") or ".")
580
+ impact = graph.impact(targets)
581
+ return {"success": True, "data": {**impact.as_dict(), "report": impact.render()}}
582
+ except Exception as exc:
583
+ return {"success": False, "error": f"impact_analysis failed: {exc}"}
584
+
585
+
586
+ PROJECT_GRAPH_TOOLS = {
587
+ "impact_analysis": (tool_impact_analysis,
588
+ "What depends on a file or symbol: callers, importers, tests, services"),
589
+ }
590
+
591
+ PROJECT_GRAPH_SCHEMAS = [
592
+ {
593
+ "name": "impact_analysis",
594
+ "description": (
595
+ "Before editing, find what a change would reach. Give file paths and/or symbol "
596
+ "names (`Class.method` for a method). Returns the files that use them directly, "
597
+ "the files reached through imports (up to three hops), the tests that cover any "
598
+ "of those, the services (compose services, packages) that ship them, and a "
599
+ "breadth rating. Unknown targets are listed as not found rather than guessed."
600
+ ),
601
+ "parameters": {
602
+ "type": "object",
603
+ "properties": {
604
+ "targets": {
605
+ "type": "array",
606
+ "items": {"type": "string"},
607
+ "description": "Repository-relative paths or symbol names, e.g. src/auth/session.py, Session.refresh",
608
+ },
609
+ "path": {"type": "string", "description": "Repository root (default: current directory)"},
610
+ },
611
+ "required": ["targets"],
612
+ },
613
+ },
614
+ ]