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,60 @@
1
+ """Short-TTL response cache shared by the CLI and the extracted Ollama stream.
2
+
3
+ These four names used to live in aria_cli.py and reach stream_ollama only
4
+ through the module-global rebind in that file (see
5
+ ``_rebind_module_function_globals`` and the stream_ollama rebind site). Any
6
+ caller that had not imported aria_cli first — the SDK and the daemon — got the
7
+ un-rebound function instead and died on the first cache-eligible turn with
8
+ ``NameError: name '_cache_key' is not defined``.
9
+
10
+ Keeping them here means both callers share one cache dict rather than each
11
+ growing its own, and removes four of the borrowed globals that
12
+ packages/aria_core/architecture.py tracks as debt.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import hashlib
18
+ import time
19
+
20
+ __all__ = ["RESPONSE_CACHE", "RESPONSE_CACHE_TTL", "RESPONSE_CACHE_MAX_ENTRIES",
21
+ "cache_get", "cache_set", "cache_clear", "cache_key"]
22
+
23
+ RESPONSE_CACHE: dict = {} # key → (response_text, expire_ts)
24
+ RESPONSE_CACHE_TTL = 60.0 # seconds
25
+ RESPONSE_CACHE_MAX_ENTRIES = 200
26
+
27
+
28
+ def cache_get(key: str) -> str | None:
29
+ """Return cached response text if still valid, else None."""
30
+ entry = RESPONSE_CACHE.get(key)
31
+ if entry and time.time() < entry[1]:
32
+ return entry[0]
33
+ return None
34
+
35
+
36
+ def cache_set(key: str, value: str) -> None:
37
+ """Store response in cache with TTL expiry."""
38
+ RESPONSE_CACHE[key] = (value, time.time() + RESPONSE_CACHE_TTL)
39
+ # Keep cache small — evict expired entries when it grows large
40
+ if len(RESPONSE_CACHE) > RESPONSE_CACHE_MAX_ENTRIES:
41
+ now = time.time()
42
+ for k in list(RESPONSE_CACHE.keys()):
43
+ if RESPONSE_CACHE[k][1] < now:
44
+ del RESPONSE_CACHE[k]
45
+
46
+
47
+ def cache_clear() -> None:
48
+ """Drop every cached response — for /clear and for test isolation.
49
+
50
+ v4.4.2 grew a second copy of this module at apps/cli/response_cache.py.
51
+ That copy is not taken: this one is already where the two callers share it,
52
+ and duplicating it would put two cache dicts behind one name. Only the
53
+ parts this one lacked came across.
54
+ """
55
+ RESPONSE_CACHE.clear()
56
+
57
+
58
+ def cache_key(model: str, message: str) -> str:
59
+ raw = f"{model}::{message.strip().lower()}"
60
+ return hashlib.md5(raw.encode()).hexdigest()
@@ -0,0 +1,257 @@
1
+ """stream_chat — Aria cloud SSE provider extracted from aria_cli.py.
2
+
3
+ Streams AI responses from the Aria backend via Server-Sent Events (SSE).
4
+ Supports cancellation, thinking tokens, tool calls, retries, and usage stats.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import asyncio
9
+ import json
10
+ from typing import Callable, Optional
11
+
12
+
13
+ def build_chat_payload(
14
+ message: str,
15
+ history: list,
16
+ *,
17
+ model: str,
18
+ thinking_mode: str,
19
+ user_context: Optional[dict],
20
+ project_context: str,
21
+ use_react_gateway: bool,
22
+ ) -> tuple[str, dict]:
23
+ """Build either the legacy chat payload or the shared ReAct envelope.
24
+
25
+ This is deliberately pure: it gives CLI, desktop, and iOS an auditable
26
+ parity point without forcing the local-first CLI to use cloud services.
27
+ """
28
+ if use_react_gateway:
29
+ context = dict(user_context or {})
30
+ if project_context:
31
+ context["project_context"] = project_context
32
+ mode = str(context.pop("workspace_mode", "code"))
33
+ return "/api/v2/chat/react", {
34
+ "message": {
35
+ "role": "user",
36
+ "content": [{"type": "text", "text": message}],
37
+ },
38
+ "mode": mode,
39
+ "surface": "aria_code",
40
+ "history": history[-20:],
41
+ "model": {"id": model or "auto", "effort": thinking_mode or "auto"},
42
+ "context": context,
43
+ }
44
+
45
+ payload: dict = {
46
+ "message": message,
47
+ "conversation_history": history[-20:],
48
+ "model": model,
49
+ "thinking_mode": thinking_mode,
50
+ "stream": True,
51
+ }
52
+ if user_context:
53
+ if project_context:
54
+ user_context = {**user_context, "project_context": project_context}
55
+ payload["user_context"] = user_context
56
+ return "/api/v2/ai/chat/stream", payload
57
+
58
+
59
+ async def stream_chat(
60
+ base_url: str,
61
+ message: str,
62
+ history: list,
63
+ model: str = "qwen2.5:7b",
64
+ thinking_mode: str = "auto",
65
+ user_context: Optional[dict] = None,
66
+ auth_token: Optional[str] = None,
67
+ on_token: Optional[Callable[[str], None]] = None,
68
+ on_thinking: Optional[Callable[[str], None]] = None,
69
+ on_tool_call: Optional[Callable[[str, dict], None]] = None,
70
+ on_tool_result: Optional[Callable[[str, str], None]] = None,
71
+ on_status: Optional[Callable[[str, str], None]] = None,
72
+ cancel_event: Optional[asyncio.Event] = None,
73
+ project_context: str = "",
74
+ use_react_gateway: bool = False,
75
+ ) -> dict:
76
+ """Stream AI chat via SSE with cancel support and user context.
77
+
78
+ Parameters
79
+ ----------
80
+ project_context:
81
+ ARIA.md / CLAUDE.md content to inject into user_context.
82
+ Callers pass ``_PROJECT_CONTEXT`` from aria_cli, keeping this
83
+ module free of global state.
84
+ """
85
+ import aiohttp
86
+
87
+ endpoint, payload = build_chat_payload(
88
+ message, history, model=model, thinking_mode=thinking_mode,
89
+ user_context=user_context, project_context=project_context,
90
+ use_react_gateway=use_react_gateway,
91
+ )
92
+ url = f"{base_url.rstrip('/')}{endpoint}"
93
+
94
+ headers: dict = {}
95
+ if auth_token:
96
+ headers["Authorization"] = f"Bearer {auth_token}"
97
+
98
+ full_response = ""
99
+ thinking_content = ""
100
+ tools_used: list = []
101
+ sources: list = []
102
+ tool_calls_pending: list = []
103
+ usage: dict = {"prompt_tokens": 0, "completion_tokens": 0, "thinking_tokens": 0}
104
+
105
+ _max_connect_retries = 2
106
+ _last_connect_error: Optional[str] = None
107
+
108
+ for _attempt in range(_max_connect_retries + 1):
109
+ if cancel_event and cancel_event.is_set():
110
+ return {
111
+ "success": True, "response": "", "cancelled": True,
112
+ "tools_used": [], "sources": [], "usage": usage,
113
+ }
114
+ # Reset per-attempt accumulators
115
+ full_response = ""
116
+ thinking_content = ""
117
+ tools_used = []
118
+ sources = []
119
+ tool_calls_pending = []
120
+ usage = {"prompt_tokens": 0, "completion_tokens": 0, "thinking_tokens": 0}
121
+
122
+ try:
123
+ async with aiohttp.ClientSession() as session:
124
+ async with session.post(
125
+ url, json=payload, headers=headers,
126
+ timeout=aiohttp.ClientTimeout(total=120),
127
+ ) as resp:
128
+ if resp.status != 200:
129
+ error_text = await resp.text()
130
+ return {"success": False, "error": f"HTTP {resp.status}: {error_text[:200]}"}
131
+
132
+ buffer = ""
133
+ event_type = "delta"
134
+
135
+ async for chunk in resp.content:
136
+ if cancel_event and cancel_event.is_set():
137
+ try:
138
+ if not use_react_gateway:
139
+ await session.post(
140
+ f"{base_url.rstrip('/')}/api/v2/ai/chat/cancel",
141
+ headers=headers,
142
+ timeout=aiohttp.ClientTimeout(total=3),
143
+ )
144
+ except Exception:
145
+ pass
146
+ return {
147
+ "success": True, "response": full_response,
148
+ "cancelled": True, "tools_used": tools_used,
149
+ "sources": sources, "usage": usage,
150
+ }
151
+
152
+ text = chunk.decode("utf-8", errors="ignore")
153
+ buffer += text
154
+
155
+ while "\n" in buffer:
156
+ line, buffer = buffer.split("\n", 1)
157
+ line = line.strip()
158
+
159
+ if not line or line.startswith(":"):
160
+ continue
161
+ if line.startswith("event:"):
162
+ event_type = line[6:].strip()
163
+ continue
164
+ if not line.startswith("data:"):
165
+ continue
166
+
167
+ data_str = line[5:].strip()
168
+ if data_str == "[DONE]":
169
+ break
170
+ try:
171
+ data = json.loads(data_str)
172
+ except json.JSONDecodeError:
173
+ continue
174
+
175
+ # Backend error: {"success": false, "error": "..."}
176
+ if data.get("success") is False:
177
+ err_msg = data.get("error", "Backend error")
178
+ return {"success": False, "error": f"Backend: {err_msg}"}
179
+
180
+ evt = data.get("type", event_type)
181
+
182
+ if evt == "delta":
183
+ token = data.get("text", data.get("content", ""))
184
+ if token:
185
+ full_response += token
186
+ usage["completion_tokens"] += 1
187
+ if on_token:
188
+ on_token(token)
189
+
190
+ elif evt == "thinking_content":
191
+ tc = data.get("content", "")
192
+ if tc:
193
+ thinking_content += tc
194
+ usage["thinking_tokens"] += 1
195
+ if on_thinking:
196
+ on_thinking(tc)
197
+
198
+ elif evt == "tool_call":
199
+ tool = data.get("tool", data.get("name", ""))
200
+ params = data.get("params", {})
201
+ tools_used.append(tool)
202
+ tool_calls_pending.append({"tool": tool, "params": params})
203
+ if on_tool_call:
204
+ on_tool_call(tool, params)
205
+
206
+ elif evt == "tool_result":
207
+ if on_tool_result:
208
+ on_tool_result(data.get("tool", ""), data.get("summary", ""))
209
+
210
+ elif evt == "status":
211
+ if on_status:
212
+ on_status(data.get("state", ""), data.get("label", data.get("message", "")))
213
+
214
+ elif evt == "final":
215
+ full_response = data.get("answer", full_response)
216
+ sources = data.get("sources", [])
217
+ if data.get("usage"):
218
+ u = data["usage"]
219
+ usage["prompt_tokens"] = u.get("prompt_tokens", usage["prompt_tokens"])
220
+ usage["completion_tokens"] = u.get("completion_tokens", usage["completion_tokens"])
221
+
222
+ elif evt == "error":
223
+ return {"success": False, "error": data.get("message", data.get("error", "Unknown error"))}
224
+
225
+ return {
226
+ "success": True,
227
+ "response": full_response,
228
+ "thinking": thinking_content,
229
+ "tools_used": tools_used,
230
+ "sources": sources,
231
+ "tool_calls_pending": tool_calls_pending,
232
+ "usage": usage,
233
+ }
234
+
235
+ except asyncio.TimeoutError:
236
+ return {"success": False, "error": "Request timed out (120s)"}
237
+ except asyncio.CancelledError:
238
+ return {
239
+ "success": True, "response": full_response, "cancelled": True,
240
+ "tools_used": tools_used, "sources": sources, "usage": usage,
241
+ }
242
+ except aiohttp.ClientConnectorError as exc:
243
+ _last_connect_error = str(exc)
244
+ if _attempt < _max_connect_retries:
245
+ wait = 1.5 * (_attempt + 1)
246
+ await asyncio.sleep(wait)
247
+ if on_status:
248
+ on_status("retry", f"Connection failed, retrying ({_attempt + 2}/{_max_connect_retries + 1})...")
249
+ continue
250
+ break
251
+ except Exception as exc:
252
+ return {"success": False, "error": str(exc)}
253
+
254
+ return {
255
+ "success": False,
256
+ "error": f"Connection failed after {_max_connect_retries + 1} attempts: {_last_connect_error}",
257
+ }
@@ -0,0 +1,353 @@
1
+ """Phase-3 bridge: run one chat turn through the shared ``runtime.run_agent``.
2
+
3
+ The documented runtime next step is to route the CLI tool loop through
4
+ ``run_agent`` and keep aria_cli as orchestration glue. This module is that engine
5
+ — the two adapters run_agent needs plus a thin driver:
6
+
7
+ • build_tool_executor() — wraps the CLI's ``LOCAL_TOOLS`` ({name: (handler, schema)})
8
+ • make_provider_fn() — selects the provider (chat_routing) + streams it
9
+ • run_chat_via_runtime() — drives run_agent, renders via callbacks, returns text
10
+
11
+ This is THE chat path: ``send_message`` runs every turn through it (the old
12
+ inline per-round loop was retired in 2026-07 after the runtime path was
13
+ validated with real turns). Tool approval and execution context are threaded
14
+ through to run_agent's tool loop; on failure send_message attempts one direct
15
+ cloud rescue (``providers/llm/registry.stream_cloud_fallback``) before
16
+ presenting the error.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from typing import Any, Callable, List, Optional
22
+
23
+ from .chat_routing import first_round_route, is_placeholder_response, should_fallback
24
+
25
+
26
+ def build_tool_executor(
27
+ local_tools,
28
+ config: Optional[dict] = None,
29
+ execution_context: Optional[Callable[[], dict]] = None,
30
+ ):
31
+ """Wrap the CLI's LOCAL_TOOLS registry for run_agent."""
32
+ from aria_code.runtime.tool_executor import ToolExecutor
33
+ return ToolExecutor(
34
+ local_tools,
35
+ config=config or {},
36
+ execution_context=execution_context,
37
+ )
38
+
39
+
40
+ # Modes in which nothing can be written, so nothing can need verifying.
41
+ _READ_ONLY_MODES = frozenset({"read-only", "readonly", "read_only", "plan"})
42
+
43
+
44
+ def _declared_acceptance_commands(cfg: dict, message: str) -> tuple:
45
+ """What this workspace says "correct" means for this particular message.
46
+
47
+ Precedence, most specific first:
48
+
49
+ 1. ``acceptance_commands`` in the session config — the user said it out
50
+ loud for this session, so nothing overrides it.
51
+ 2. The active domain packs' commands. A pack contributes only when it
52
+ resolved a concrete entity from *this* message, so a declared
53
+ logistics check cannot fire on a payments question.
54
+ 3. The workspace's ``acceptance.default`` from ``.ariarc``.
55
+
56
+ Nothing declared falls through to inference from the changed files, which
57
+ is the behaviour that existed before packs could speak here at all.
58
+ """
59
+ explicit = tuple(cfg.get("acceptance_commands") or ())
60
+ if explicit:
61
+ return explicit
62
+
63
+ commands: list = []
64
+ try:
65
+ from aria_code.packs import (
66
+ activate_packs,
67
+ active_acceptance_commands,
68
+ load_builtin_packs,
69
+ )
70
+
71
+ load_builtin_packs()
72
+ commands.extend(active_acceptance_commands(activate_packs(message or "")))
73
+ except Exception:
74
+ pass
75
+
76
+ try:
77
+ from aria_code.packs.rules import default_acceptance_commands
78
+
79
+ for command in default_acceptance_commands():
80
+ if command not in commands:
81
+ commands.append(command)
82
+ except Exception:
83
+ pass
84
+
85
+ return tuple(commands)
86
+
87
+
88
+ def build_acceptance_gate(executor, config: Optional[dict] = None, message: str = ""):
89
+ """The CLI's acceptance gate, or ``None`` when this session shouldn't have one.
90
+
91
+ The gate runs its checks through the session's own ``run_command`` tool
92
+ rather than spawning subprocesses itself, so the workspace sandbox, the
93
+ command policy and the trace all apply to a verification run exactly as
94
+ they apply to a command the model asked for.
95
+
96
+ ``user_approved`` is set because the commands are the *planner's*, not the
97
+ model's or the user's prose — the same inferred plan ``/verify`` already
98
+ runs on request. Anything else would put an approval prompt between the
99
+ model finishing and the check that tells us whether it finished correctly,
100
+ which is the one place a prompt cannot help.
101
+ """
102
+ cfg = config or {}
103
+ if not cfg.get("acceptance_gate", True):
104
+ return None
105
+ mode = str(cfg.get("permission_mode", "workspace-write") or "")
106
+ if mode in _READ_ONLY_MODES:
107
+ return None
108
+ if "run_command" not in getattr(executor, "local_tools", {}):
109
+ return None
110
+
111
+ import os
112
+ from aria_code.runtime.acceptance import AcceptanceGate
113
+
114
+ timeout = int(cfg.get("acceptance_timeout", 300) or 300)
115
+
116
+ def _runner(command: str) -> dict:
117
+ return executor.execute_local("run_command", {
118
+ "command": command,
119
+ "policy": "balanced",
120
+ "permission_mode": mode,
121
+ "network_enabled": bool(cfg.get("network_enabled", True)),
122
+ "user_approved": True,
123
+ "timeout": timeout,
124
+ })
125
+
126
+ return AcceptanceGate(
127
+ runner=_runner,
128
+ root=os.getcwd(),
129
+ max_attempts=int(cfg.get("acceptance_max_attempts", 2) or 2),
130
+ commands=_declared_acceptance_commands(cfg, message),
131
+ )
132
+
133
+
134
+ async def run_with_fallback(
135
+ route: str,
136
+ *,
137
+ run_cloud: Callable,
138
+ run_ollama: Callable,
139
+ on_token: Optional[Callable[[str], None]] = None,
140
+ ) -> dict:
141
+ """Primary generation per ``route``, with cloud→Ollama fallback parity.
142
+
143
+ Mirrors ``send_message``'s inline fallback, but keyed on *route* (via
144
+ ``should_fallback``) so a genuinely-good forced-backend answer is kept
145
+ instead of being discarded and re-run:
146
+
147
+ • ``skip`` / ``ollama`` → run local Ollama directly (no cloud round)
148
+ • ``cloud`` → run cloud; if it fails or returns a placeholder
149
+ (empty / canned / backend stub), fall back to
150
+ local Ollama
151
+
152
+ ``run_cloud`` / ``run_ollama`` are async ``(on_token) -> result dict``
153
+ closures; injecting them keeps this orchestration unit-testable without
154
+ real providers or network.
155
+ """
156
+ if route in ("skip", "ollama", "configured"):
157
+ return await run_ollama(on_token)
158
+
159
+ # route == "cloud": count streamed tokens so a long-but-unstreamed canned
160
+ # backend reply is recognised as a placeholder, not a real generation.
161
+ _tokens = [0]
162
+
163
+ def _counting_on_token(tok: str) -> None:
164
+ _tokens[0] += 1
165
+ if on_token is not None:
166
+ on_token(tok)
167
+
168
+ result = await run_cloud(_counting_on_token)
169
+ if result.get("cancelled"):
170
+ return result # user-cancelled — never silently re-run on a different provider
171
+
172
+ placeholder = is_placeholder_response(result.get("response", ""), _tokens[0])
173
+ if should_fallback("cloud", result, is_placeholder=placeholder):
174
+ return await run_ollama(on_token)
175
+ return result
176
+
177
+
178
+ def make_provider_fn(
179
+ *,
180
+ model: str,
181
+ config: dict,
182
+ api_url: Optional[str],
183
+ ollama_url: str,
184
+ tool_schemas: List[dict],
185
+ thinking_mode: str = "auto",
186
+ user_context: Optional[dict] = None,
187
+ auth_token: Optional[str] = None,
188
+ project_context: Any = None,
189
+ system_override: Optional[str] = None,
190
+ ) -> Callable:
191
+ """Build an async ``provider_fn`` for run_agent.
192
+
193
+ Selects the provider per chat_routing (cloud → AriaSSE backend; ollama/skip →
194
+ local Ollama) and streams it through the shared ``stream_provider_result``.
195
+ A pending system-role override is threaded the same way ``send_message`` does
196
+ it: cloud via ``user_context['system_role_override']``, Ollama via the
197
+ provider's ``system_override`` argument.
198
+ """
199
+ # Bare root on purpose: this is the module the tests patch, and the two
200
+ # roots are distinct module objects — importing the packaged one made
201
+ # monkeypatched providers silently unused.
202
+ from apps.cli.providers.base import AriaSSEProvider, ConfiguredProvider, OllamaProvider
203
+ # Bare root: the tests patch packages.aria_sdk.streaming, and the packaged
204
+ # root is a different module object, so the patch would not apply.
205
+ from packages.aria_sdk.streaming import stream_provider_result
206
+
207
+ _cloud_uctx = dict(user_context or {})
208
+ if system_override:
209
+ _cloud_uctx["system_role_override"] = system_override
210
+
211
+ def _scoped_tools(prompt: str) -> List[dict]:
212
+ """Tools this message may use: core always, domain only when claimed."""
213
+ try:
214
+ from aria_code.apps.cli.tool_scope import select_tool_schemas
215
+
216
+ return select_tool_schemas(tool_schemas, prompt)
217
+ except Exception:
218
+ return list(tool_schemas)
219
+
220
+ def _system_for(prompt: str) -> str:
221
+ """The rules this turn runs under, for the non-Ollama providers.
222
+
223
+ ollama_stream assembles its own (with prefetched data and sized
224
+ project context); everything else used to get nothing at all, so a
225
+ cloud model was never told the tool discipline. Built per message
226
+ because the right prompt depends on what was asked.
227
+ """
228
+ try:
229
+ from aria_code.apps.cli.prompts.select import build_turn_system_prompt
230
+
231
+ return build_turn_system_prompt(
232
+ prompt,
233
+ override=system_override,
234
+ project_context=str(project_context or ""),
235
+ )
236
+ except Exception:
237
+ return system_override or ""
238
+
239
+ async def _provider_fn(prompt, history, *, on_token=None, on_thinking=None,
240
+ on_tool_call=None, on_tool_result=None, on_status=None,
241
+ cancel_event=None):
242
+ route = first_round_route(model, config, api_url)
243
+
244
+ # ollama_stream does its own intent-based selection; the other
245
+ # providers had none, so a coding turn reached Gemini carrying all 74
246
+ # tool schemas including 34 domain tools. Scoped per message because
247
+ # what a turn may call depends on what it asked.
248
+ _scoped = _scoped_tools(prompt)
249
+
250
+ async def _stream(provider, _on_token):
251
+ return await stream_provider_result(
252
+ provider, prompt, history, tools=_scoped,
253
+ cancel_event=cancel_event, on_token=_on_token, on_thinking=on_thinking,
254
+ on_tool_call=on_tool_call, on_tool_result=on_tool_result, on_status=on_status,
255
+ )
256
+
257
+ async def run_cloud(_on_token):
258
+ return await _stream(
259
+ AriaSSEProvider(
260
+ api_url, model, thinking_mode=thinking_mode,
261
+ user_context=_cloud_uctx, auth_token=auth_token,
262
+ project_context=project_context,
263
+ use_react_gateway=bool(config.get("arthera_react_gateway")),
264
+ ),
265
+ _on_token,
266
+ )
267
+
268
+ async def run_ollama(_on_token):
269
+ selected = (
270
+ OllamaProvider(ollama_url, model, system_override=system_override)
271
+ if route == "ollama" else
272
+ ConfiguredProvider(config, model, system_override=_system_for(prompt))
273
+ )
274
+ return await _stream(
275
+ selected,
276
+ _on_token,
277
+ )
278
+
279
+ return await run_with_fallback(
280
+ route, run_cloud=run_cloud, run_ollama=run_ollama, on_token=on_token,
281
+ )
282
+
283
+ return _provider_fn
284
+
285
+
286
+ async def run_chat_via_runtime(
287
+ *,
288
+ prompt: str,
289
+ history: list,
290
+ local_tools,
291
+ tool_schemas: List[dict],
292
+ model: str,
293
+ config: dict,
294
+ api_url: Optional[str],
295
+ ollama_url: str,
296
+ cancel_event=None,
297
+ on_token: Optional[Callable[[str], None]] = None,
298
+ on_thinking: Optional[Callable[[str], None]] = None,
299
+ on_tool_call: Optional[Callable[[str, dict], None]] = None,
300
+ on_tool_result: Optional[Callable[[str, dict], None]] = None,
301
+ on_status: Optional[Callable[[str, str], None]] = None,
302
+ thinking_mode: str = "auto",
303
+ user_context: Optional[dict] = None,
304
+ auth_token: Optional[str] = None,
305
+ project_context: Any = None,
306
+ system_override: Optional[str] = None,
307
+ max_rounds: int = 30,
308
+ return_result: bool = False,
309
+ execution_context: Optional[Callable[[], dict]] = None,
310
+ confirm_tools=(),
311
+ approval_callback: Optional[Callable] = None,
312
+ approval_applier: Optional[Callable] = None,
313
+ requires_evidence: bool = False,
314
+ grounding_tools=(),
315
+ evidence_already_grounded: bool = False,
316
+ ):
317
+ """Run one chat turn through the shared runtime Gateway.
318
+
319
+ This is the CLI *adapter* for ``runtime.gateway.run_turn``: it builds the
320
+ CLI's ``provider_fn`` (AriaSSE/Ollama selection + cloud→Ollama fallback) and
321
+ tool executor (the LOCAL_TOOLS registry), then hands them to the neutral
322
+ gateway, which drives ``run_agent`` and streams via the callbacks. By
323
+ default this returns assistant text for compatibility. ``return_result``
324
+ exposes the gateway result so terminal adapters can preserve provider and
325
+ usage metadata during final rendering.
326
+ """
327
+ from aria_code.runtime.gateway import run_turn
328
+
329
+ provider_fn = make_provider_fn(
330
+ model=model, config=config, api_url=api_url, ollama_url=ollama_url,
331
+ tool_schemas=tool_schemas, thinking_mode=thinking_mode,
332
+ user_context=user_context, auth_token=auth_token, project_context=project_context,
333
+ system_override=system_override,
334
+ )
335
+ executor = build_tool_executor(local_tools, config, execution_context)
336
+ gate = build_acceptance_gate(executor, config, prompt)
337
+
338
+ result = await run_turn(
339
+ prompt, history,
340
+ provider_fn=provider_fn, tool_executor=executor,
341
+ tool_schemas=list(tool_schemas),
342
+ on_token=on_token, on_thinking=on_thinking,
343
+ on_tool_call=on_tool_call, on_tool_result=on_tool_result, on_status=on_status,
344
+ cancel_event=cancel_event, max_rounds=max_rounds,
345
+ confirm_tools=confirm_tools,
346
+ approval_callback=approval_callback,
347
+ approval_applier=approval_applier,
348
+ requires_evidence=requires_evidence,
349
+ grounding_tools=grounding_tools,
350
+ evidence_already_grounded=evidence_already_grounded,
351
+ acceptance=gate,
352
+ )
353
+ return result if return_result else result.text