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,249 @@
1
+ """Product-level service contracts for Aria Code.
2
+
3
+ These manifests describe stable service boundaries. They intentionally wrap the
4
+ current modules instead of moving code all at once, so the CLI can migrate away
5
+ from the large legacy entrypoint incrementally.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from dataclasses import dataclass, field
11
+ from typing import Dict, List
12
+
13
+ from aria_code.packages.aria_core import CapabilityManifest, PackageLink, PermissionLevel, ServiceKind
14
+
15
+
16
+ @dataclass(frozen=True)
17
+ class ServiceSpec:
18
+ name: str
19
+ description: str
20
+ owner_package: str
21
+ module: str
22
+ capabilities: List[str] = field(default_factory=list)
23
+ permissions: List[PermissionLevel] = field(default_factory=list)
24
+ required: bool = True
25
+ migration_target: str = ""
26
+
27
+ def manifest(self) -> CapabilityManifest:
28
+ tags = ["service"]
29
+ if self.required:
30
+ tags.append("required")
31
+ return CapabilityManifest(
32
+ name=self.name,
33
+ kind=ServiceKind.DATA if self.name == "data" else ServiceKind.SERVICE,
34
+ description=self.description,
35
+ capabilities=self.capabilities,
36
+ permissions=self.permissions,
37
+ package_link=PackageLink(
38
+ package=self.owner_package,
39
+ module=self.module,
40
+ object_name=self.migration_target,
41
+ notes="Facade over existing modules; migrate implementation behind this boundary.",
42
+ ),
43
+ tags=tags,
44
+ )
45
+
46
+
47
+ def list_service_specs() -> List[ServiceSpec]:
48
+ """Return stable service boundaries for the Aria Code product."""
49
+
50
+ return [
51
+ ServiceSpec(
52
+ name="gateway",
53
+ description="Local-first control plane for CLI, daemon, MCP, and channel entrypoints.",
54
+ owner_package="packages.aria_services",
55
+ module="apps.gateway",
56
+ capabilities=["session.routing", "task.queue", "mcp.routing", "channel.routing"],
57
+ permissions=[PermissionLevel.READ_ONLY, PermissionLevel.WORKSPACE_WRITE],
58
+ migration_target="GatewayService",
59
+ ),
60
+ ServiceSpec(
61
+ name="runtime",
62
+ description="Agent turn loop, tool execution, approvals, traces, and parallel tool orchestration.",
63
+ owner_package="runtime",
64
+ module="runtime",
65
+ capabilities=["agent.loop", "tool.execution", "approval.policy", "trace"],
66
+ permissions=[PermissionLevel.READ_ONLY, PermissionLevel.WORKSPACE_WRITE],
67
+ migration_target="AgentRuntime",
68
+ ),
69
+ ServiceSpec(
70
+ name="settings",
71
+ description="Configuration, secrets, model profiles, runtime flags, and permission policy resolution.",
72
+ owner_package="packages.aria_services",
73
+ module="packages.aria_services.settings",
74
+ capabilities=[
75
+ "settings.config",
76
+ "settings.secrets",
77
+ "settings.model_profiles",
78
+ "settings.permission_policy",
79
+ ],
80
+ permissions=[PermissionLevel.READ_ONLY],
81
+ migration_target="SettingsService",
82
+ ),
83
+ ServiceSpec(
84
+ name="context",
85
+ description="Conversation memory, context pressure checks, compaction, and resumable summary envelopes.",
86
+ owner_package="packages.aria_services",
87
+ module="packages.aria_services.context",
88
+ capabilities=[
89
+ "context.memory",
90
+ "context.compaction",
91
+ "context.resume",
92
+ "context.checkpoint",
93
+ ],
94
+ permissions=[PermissionLevel.READ_ONLY, PermissionLevel.WORKSPACE_WRITE],
95
+ migration_target="ContextService",
96
+ ),
97
+ ServiceSpec(
98
+ name="references",
99
+ description="Typed read-only @ references for files, folders, assets, and research artifacts.",
100
+ owner_package="packages.aria_services",
101
+ module="packages.aria_services.references",
102
+ capabilities=[
103
+ "context.reference.parse",
104
+ "context.reference.resolve",
105
+ "context.reference.tool_plan",
106
+ "context.reference.complete",
107
+ ],
108
+ permissions=[PermissionLevel.READ_ONLY],
109
+ migration_target="ReferenceService",
110
+ ),
111
+ ServiceSpec(
112
+ name="tools",
113
+ description="Typed tool manifests, schemas, permissions, local tool adapters, and MCP tool conversion.",
114
+ owner_package="packages.aria_tools",
115
+ module="packages.aria_tools",
116
+ capabilities=[
117
+ "tool.registry",
118
+ "tool.schema",
119
+ "tool.permissions",
120
+ "tool.local_adapter",
121
+ "tool.mcp_adapter",
122
+ ],
123
+ permissions=[PermissionLevel.READ_ONLY, PermissionLevel.WORKSPACE_WRITE, PermissionLevel.NETWORK],
124
+ migration_target="ToolRegistry",
125
+ ),
126
+ ServiceSpec(
127
+ name="data",
128
+ description="Multi-source market data with timestamps, provider attribution, and stale-data flags.",
129
+ owner_package="packages.aria_services",
130
+ module="data_service",
131
+ capabilities=["market.quote", "market.history", "technical.indicators", "data.quality"],
132
+ permissions=[PermissionLevel.NETWORK],
133
+ migration_target="MarketDataService",
134
+ ),
135
+ ServiceSpec(
136
+ name="reports",
137
+ description="Report, chart, and artifact generation with local workspace storage policies.",
138
+ owner_package="packages.aria_services",
139
+ module="report_generator",
140
+ capabilities=[
141
+ "report.markdown",
142
+ "report.html",
143
+ "chart.render",
144
+ "artifact.storage",
145
+ ],
146
+ permissions=[PermissionLevel.READ_ONLY, PermissionLevel.WORKSPACE_WRITE],
147
+ migration_target="ReportService",
148
+ ),
149
+ ServiceSpec(
150
+ name="research_quality",
151
+ description="Deterministic report completion, plausibility, coverage, and provenance gates.",
152
+ owner_package="packages.aria_services",
153
+ module="packages.aria_services.research_quality",
154
+ capabilities=["report.quality_gate", "report.provenance", "report.plausibility"],
155
+ permissions=[PermissionLevel.READ_ONLY],
156
+ migration_target="ReportQualityAssessment",
157
+ ),
158
+ ServiceSpec(
159
+ name="brokers",
160
+ description="Broker account reads, paper trading, order previews, guarded execution, and audit trails.",
161
+ owner_package="brokers",
162
+ module="brokers",
163
+ capabilities=[
164
+ "broker.account",
165
+ "broker.positions",
166
+ "broker.orders",
167
+ "broker.paper",
168
+ "broker.trade_preview",
169
+ "broker.confirmed_execution",
170
+ "broker.audit",
171
+ ],
172
+ permissions=[PermissionLevel.BROKER_READ, PermissionLevel.BROKER_TRADE],
173
+ migration_target="BrokerService",
174
+ ),
175
+ ServiceSpec(
176
+ name="skills",
177
+ description="Workflow recipes that bind tools, agents, data, reports, and brokers.",
178
+ owner_package="packages.aria_skills",
179
+ module="packages.aria_skills",
180
+ capabilities=[
181
+ "skill.registry",
182
+ "skill.discovery",
183
+ "skill.metadata_route",
184
+ "skill.namespace",
185
+ "skill.integrity",
186
+ "skill.activation_trace",
187
+ "workflow.binding",
188
+ ],
189
+ permissions=[PermissionLevel.READ_ONLY],
190
+ migration_target="SkillRegistry",
191
+ ),
192
+ ServiceSpec(
193
+ name="mcp",
194
+ description="MCP server discovery, health, tool provenance, reconnect policy, and manifest conversion.",
195
+ owner_package="packages.aria_mcp",
196
+ module="packages.aria_mcp",
197
+ capabilities=[
198
+ "mcp.discovery",
199
+ "mcp.health",
200
+ "mcp.tool_manifests",
201
+ "mcp.reconnect",
202
+ "mcp.provenance",
203
+ ],
204
+ permissions=[PermissionLevel.READ_ONLY, PermissionLevel.NETWORK],
205
+ migration_target="MCPService",
206
+ ),
207
+ ServiceSpec(
208
+ name="channels",
209
+ description="Optional external entrypoints such as daemon, relay, Feishu, Telegram, and webhooks.",
210
+ owner_package="apps.channels",
211
+ module="apps.channels",
212
+ capabilities=["channel.daemon", "channel.relay", "channel.feishu", "channel.telegram"],
213
+ permissions=[PermissionLevel.NETWORK],
214
+ required=False,
215
+ migration_target="ChannelRegistry",
216
+ ),
217
+ ServiceSpec(
218
+ name="safety",
219
+ description="Sandbox, workspace permissions, command policy, privacy controls, and audit hooks.",
220
+ owner_package="safety",
221
+ module="safety",
222
+ capabilities=["sandbox", "command.policy", "privacy", "audit"],
223
+ permissions=[PermissionLevel.READ_ONLY],
224
+ migration_target="SafetyService",
225
+ ),
226
+ ServiceSpec(
227
+ name="observability",
228
+ description="Doctor checks, runtime traces, provider health, architecture coverage, and support bundles.",
229
+ owner_package="packages.aria_infra",
230
+ module="packages.aria_infra.doctor",
231
+ capabilities=[
232
+ "doctor.checks",
233
+ "trace.export",
234
+ "provider.health",
235
+ "architecture.coverage",
236
+ "support.bundle",
237
+ ],
238
+ permissions=[PermissionLevel.READ_ONLY, PermissionLevel.WORKSPACE_WRITE],
239
+ migration_target="ObservabilityService",
240
+ ),
241
+ ]
242
+
243
+
244
+ def service_map() -> Dict[str, ServiceSpec]:
245
+ return {service.name: service for service in list_service_specs()}
246
+
247
+
248
+ def required_service_names() -> List[str]:
249
+ return [service.name for service in list_service_specs() if service.required]
@@ -0,0 +1,57 @@
1
+ """Financial-research grounding policy shared by Aria adapters."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from collections.abc import Iterable, Mapping
7
+
8
+
9
+ _MARKET_IDENTIFIER = re.compile(
10
+ r"\b[A-Z]{1,5}(?:\.[A-Z]{1,4})?\b|\b\d{6}(?:\.(?:SH|SZ|SS|HK))?\b"
11
+ )
12
+ _FINANCIAL_SUBJECTS = (
13
+ "stock", "share", "equity", "market", "portfolio", "holding", "fund",
14
+ "option", "bond", "forex", "crypto", "price", "volume", "valuation",
15
+ "股票", "股价", "行情", "市场", "持仓", "组合", "基金", "期权", "债券",
16
+ "外汇", "加密", "成交量", "估值", "市值", "财报", "基本面",
17
+ )
18
+ _EVIDENCE_INTENTS = (
19
+ "analyze", "analysis", "forecast", "predict", "recommend", "buy", "sell",
20
+ "current", "latest", "today", "trend", "risk", "target", "outlook",
21
+ "分析", "预测", "推荐", "买入", "卖出", "最新", "当前", "今天", "走势",
22
+ "趋势", "风险", "目标价", "前景", "成交量", "全面分析",
23
+ )
24
+ _GROUNDING_TOOL_FRAGMENTS = (
25
+ "market", "quote", "price", "ohlcv", "fundamental", "filing", "news",
26
+ "factor", "risk", "backtest", "portfolio", "prediction", "signal",
27
+ "regime", "financial", "earnings", "valuation", "macro", "flow",
28
+ )
29
+
30
+
31
+ def requires_financial_evidence(query: str) -> bool:
32
+ """Return whether a request needs current external financial evidence."""
33
+ text = str(query or "").strip()
34
+ if not text:
35
+ return False
36
+ lowered = text.lower()
37
+ has_subject = bool(_MARKET_IDENTIFIER.search(text)) or any(
38
+ term in lowered for term in _FINANCIAL_SUBJECTS
39
+ )
40
+ return has_subject and any(term in lowered for term in _EVIDENCE_INTENTS)
41
+
42
+
43
+ def grounding_tool_names(tool_schemas: Iterable[Mapping]) -> frozenset[str]:
44
+ """Select registered tools whose outputs can ground a financial conclusion."""
45
+ names: set[str] = set()
46
+ for schema in tool_schemas:
47
+ function = schema.get("function") if isinstance(schema, Mapping) else None
48
+ name = str(
49
+ (function or {}).get("name")
50
+ if isinstance(function, Mapping)
51
+ else schema.get("name", "")
52
+ )
53
+ canonical = name.rsplit("__", 1)[-1].lower()
54
+ if name and any(fragment in canonical for fragment in _GROUNDING_TOOL_FRAGMENTS):
55
+ names.add(name)
56
+ return frozenset(names)
57
+
@@ -0,0 +1,186 @@
1
+ """Deterministic completion gates for multi-agent research reports."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from dataclasses import asdict, dataclass, field
6
+ from datetime import datetime, timezone
7
+ from enum import Enum
8
+ from typing import Any, Iterable
9
+
10
+
11
+ class ReportQualityDecision(str, Enum):
12
+ COMPLETE = "complete"
13
+ PARTIAL = "partial"
14
+ BLOCKED = "blocked"
15
+
16
+
17
+ def _utc_timestamp() -> str:
18
+ return datetime.now(timezone.utc).isoformat(timespec="seconds")
19
+
20
+
21
+ @dataclass(frozen=True)
22
+ class ReportQualityPolicy:
23
+ min_agent_coverage: float = 0.60
24
+ min_core_coverage: float = 2 / 3
25
+ core_agents: tuple[str, ...] = ("technical", "fundamental", "risk")
26
+ max_degraded_ratio: float = 0.50
27
+ require_quote: bool = True
28
+ allow_stale: bool = False
29
+ max_market_cap: float = 100_000_000_000_000.0
30
+ max_implied_shares: float = 100_000_000_000.0
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class ReportQualityAssessment:
35
+ decision: ReportQualityDecision
36
+ requested_agents: tuple[str, ...]
37
+ usable_agents: tuple[str, ...]
38
+ degraded_agents: tuple[str, ...]
39
+ failed_agents: tuple[str, ...]
40
+ agent_coverage: float
41
+ core_coverage: float
42
+ reasons: tuple[str, ...] = field(default_factory=tuple)
43
+ warnings: tuple[str, ...] = field(default_factory=tuple)
44
+ blocking_reasons: tuple[str, ...] = field(default_factory=tuple)
45
+ assessed_at: str = field(default_factory=_utc_timestamp)
46
+
47
+ @property
48
+ def metadata_status(self) -> str:
49
+ if self.decision is ReportQualityDecision.BLOCKED:
50
+ return "data_unavailable"
51
+ return self.decision.value
52
+
53
+ def to_dict(self) -> dict[str, Any]:
54
+ data = asdict(self)
55
+ data["decision"] = self.decision.value
56
+ data["metadata_status"] = self.metadata_status
57
+ data["core_agent_coverage"] = data["core_coverage"]
58
+ data["_contract_type"] = "ResearchQualityAssessment"
59
+ data["_contract_version"] = "1.0.0"
60
+ return data
61
+
62
+
63
+ def _as_float(value: Any) -> float | None:
64
+ try:
65
+ if value in (None, ""):
66
+ return None
67
+ return float(value)
68
+ except (TypeError, ValueError):
69
+ return None
70
+
71
+
72
+ def _unique(values: Iterable[str]) -> tuple[str, ...]:
73
+ return tuple(dict.fromkeys(value for value in values if value))
74
+
75
+
76
+ def assess_team_report(
77
+ team_result: Any,
78
+ data_bundle: Any = None,
79
+ policy: ReportQualityPolicy | None = None,
80
+ ) -> ReportQualityAssessment:
81
+ """Assess whether a report is complete, usable with caveats, or blocked."""
82
+ policy = policy or ReportQualityPolicy()
83
+ requested = _unique(
84
+ str(name)
85
+ for name in (getattr(team_result, "agents_run", None) or [])
86
+ if name not in {"synthesis", "debate"}
87
+ )
88
+ results = list(getattr(team_result, "results", None) or [])
89
+ if not requested:
90
+ requested = _unique(
91
+ str(getattr(result, "agent", ""))
92
+ for result in results
93
+ if getattr(result, "agent", "") not in {"synthesis", "debate"}
94
+ )
95
+
96
+ result_by_agent = {
97
+ str(getattr(result, "agent", "")): result
98
+ for result in results
99
+ if getattr(result, "agent", "")
100
+ }
101
+ usable = _unique(
102
+ name
103
+ for name in requested
104
+ if name in result_by_agent
105
+ and bool(getattr(result_by_agent[name], "success", False))
106
+ and bool(str(getattr(result_by_agent[name], "analysis", "") or "").strip())
107
+ )
108
+ degraded = _unique(
109
+ name for name in usable if bool(getattr(result_by_agent[name], "degraded", False))
110
+ )
111
+ failed = _unique(name for name in requested if name not in usable)
112
+ coverage = len(usable) / len(requested) if requested else 0.0
113
+ expected_core = tuple(name for name in policy.core_agents if name in requested)
114
+ core_coverage = (
115
+ sum(1 for name in expected_core if name in usable) / len(expected_core)
116
+ if expected_core else 1.0
117
+ )
118
+
119
+ warnings: list[str] = []
120
+ blocking: list[str] = []
121
+ reasons: list[str] = []
122
+
123
+ quote = dict(getattr(data_bundle, "quote", {}) or {}) if data_bundle else {}
124
+ price = _as_float(quote.get("price") or quote.get("current_price"))
125
+ volume = _as_float(quote.get("volume"))
126
+ market_cap = _as_float(quote.get("market_cap") or quote.get("marketCap"))
127
+ data_status = str(getattr(data_bundle, "status", "") or "").lower() if data_bundle else ""
128
+ stale = bool((getattr(data_bundle, "quality", {}) or {}).get("stale", False)) if data_bundle else False
129
+
130
+ if policy.require_quote and (price is None or price <= 0):
131
+ blocking.append("missing or invalid reference price")
132
+ if volume is not None and volume < 0:
133
+ blocking.append("negative volume")
134
+ if market_cap is not None:
135
+ if market_cap <= 0:
136
+ warnings.append("invalid market capitalization")
137
+ elif market_cap > policy.max_market_cap:
138
+ warnings.append("market capitalization exceeds plausibility limit")
139
+ if price and price > 0 and market_cap / price > policy.max_implied_shares:
140
+ warnings.append("implied share count exceeds plausibility limit")
141
+
142
+ if data_status in {"data_unavailable", "unavailable", "failed"}:
143
+ blocking.append("market data bundle unavailable")
144
+ elif data_status in {"partial", "stale"}:
145
+ warnings.append(f"market data status is {data_status}")
146
+ if stale and not policy.allow_stale:
147
+ warnings.append("market data is stale")
148
+ if not usable:
149
+ blocking.append("no agent produced usable analysis")
150
+ if coverage < policy.min_agent_coverage:
151
+ warnings.append(
152
+ f"agent coverage {coverage:.0%} is below {policy.min_agent_coverage:.0%}"
153
+ )
154
+ if core_coverage < policy.min_core_coverage:
155
+ warnings.append(
156
+ f"core-agent coverage {core_coverage:.0%} is below {policy.min_core_coverage:.0%}"
157
+ )
158
+ degraded_ratio = len(degraded) / len(usable) if usable else 0.0
159
+ if degraded_ratio > policy.max_degraded_ratio:
160
+ warnings.append(f"degraded-agent ratio {degraded_ratio:.0%} is too high")
161
+
162
+ if blocking:
163
+ decision = ReportQualityDecision.BLOCKED
164
+ elif warnings:
165
+ decision = ReportQualityDecision.PARTIAL
166
+ else:
167
+ decision = ReportQualityDecision.COMPLETE
168
+
169
+ reasons.append(f"usable agents: {len(usable)}/{len(requested)}")
170
+ if degraded:
171
+ reasons.append(f"degraded agents: {', '.join(degraded)}")
172
+ if failed:
173
+ reasons.append(f"failed agents: {', '.join(failed)}")
174
+
175
+ return ReportQualityAssessment(
176
+ decision=decision,
177
+ requested_agents=requested,
178
+ usable_agents=usable,
179
+ degraded_agents=degraded,
180
+ failed_agents=failed,
181
+ agent_coverage=round(coverage, 4),
182
+ core_coverage=round(core_coverage, 4),
183
+ reasons=_unique(reasons),
184
+ warnings=_unique(warnings),
185
+ blocking_reasons=_unique(blocking),
186
+ )
@@ -0,0 +1,157 @@
1
+ """SettingsService — single owner of persistent Aria configuration.
2
+
3
+ This is the concrete implementation behind the ``settings`` ServiceSpec in
4
+ ``packages.aria_services.registry`` (capability ``settings.config``). It owns
5
+ the merge/normalize/persist mechanics that previously lived in
6
+ ``apps/cli/config_store.py``; that module is now a thin CLI adapter which
7
+ constructs this service with CLI-specific hooks.
8
+
9
+ Layering: this package must stay importable by launcher, daemon, brokers and
10
+ MCP without dragging in the CLI. Anything CLI-flavored (provider-name
11
+ normalization, i18n language detection, model auto-selection, policy sync) is
12
+ injected as an optional callable rather than imported from ``apps.cli``.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import json
18
+ from collections.abc import Callable
19
+ from dataclasses import dataclass, field
20
+ from pathlib import Path
21
+ from typing import Any, Optional
22
+
23
+
24
+ # Models that no longer exist / were renamed; a saved config pointing at one
25
+ # is silently reset to the caller's default so the CLI never boots into a
26
+ # permanently-broken model selection.
27
+ STALE_ARIA_MODEL_PREFIXES = ("aria-opus", "aria-prelude", "aria-sonata:3", "aria-sonata:4")
28
+
29
+ # Keys that are session state, not configuration — never persisted.
30
+ NEVER_PERSIST = frozenset({"conversation_history"})
31
+
32
+
33
+ @dataclass
34
+ class SettingsService:
35
+ """Load, normalize, persist, and hand out the Aria config dict.
36
+
37
+ ``config_dir``/``config_file``/``sessions_dir`` duck-type
38
+ ``apps.cli.config_paths.AriaConfigPaths`` so the CLI can pass its existing
39
+ paths object fields straight through.
40
+ """
41
+
42
+ config_dir: Path
43
+ config_file: Path
44
+ sessions_dir: Path
45
+ defaults: dict = field(default_factory=dict)
46
+ # Optional environment hooks (all safe to omit):
47
+ normalize_provider: Optional[Callable[[str], str]] = None
48
+ detect_lang: Optional[Callable[[], str]] = None
49
+ # There was an auto_select_model hook here. It probed Ollama on first run
50
+ # and replaced the caller's default model with whatever was installed; see
51
+ # load(). Removed rather than left unwired, so it is not reconnected by
52
+ # someone who finds an unused hook and assumes it should be doing something.
53
+ on_loaded: Optional[Callable[[dict], None]] = None # e.g. CLI policy sync
54
+ stale_model_prefixes: tuple = STALE_ARIA_MODEL_PREFIXES
55
+
56
+ _snapshot: Optional[dict] = field(default=None, repr=False)
57
+
58
+ # ── core API ─────────────────────────────────────────────────────────────
59
+
60
+ def load(self) -> dict:
61
+ """Read config file, merge over defaults, normalize, fire on_loaded."""
62
+ self.config_dir.mkdir(parents=True, exist_ok=True)
63
+ self.sessions_dir.mkdir(parents=True, exist_ok=True)
64
+ if self.config_file.exists():
65
+ try:
66
+ saved = json.loads(self.config_file.read_text(encoding="utf-8"))
67
+ merged = {**self.defaults, **saved}
68
+ self._infer_local_provider(merged, saved)
69
+ self._reset_stale_model(merged)
70
+ self._ensure_ui_lang(merged)
71
+ self._finish_load(merged)
72
+ return merged
73
+ except Exception:
74
+ pass
75
+
76
+ # First run: the defaults stand as written. This used to probe Ollama
77
+ # here and overwrite cfg["model"] with whatever was installed, which
78
+ # contradicted the comment above DEFAULT_MODEL in apps/cli/bootstrap.py
79
+ # -- "provider configuration is the user's to make ... the code's job is
80
+ # to have a sane default and then get out of the way". It also cost a
81
+ # 3s HTTP timeout on every first start, and it was not needed for
82
+ # discoverability: the banner already reports "Ollama online · N models"
83
+ # (ui/banner.ollama_status_label), so a user with local models can see
84
+ # them and run /model.
85
+ cfg = dict(self.defaults)
86
+ try:
87
+ if self.detect_lang is not None:
88
+ cfg["ui_lang"] = self.detect_lang()
89
+ except Exception:
90
+ cfg["ui_lang"] = "en"
91
+ cfg.setdefault("ui_lang", "en")
92
+ self._finish_load(cfg)
93
+ return cfg
94
+
95
+ def save(self, cfg: dict) -> None:
96
+ self.config_dir.mkdir(parents=True, exist_ok=True)
97
+ payload = {k: v for k, v in cfg.items() if k not in NEVER_PERSIST}
98
+ # config.json holds auth_token / refresh_token from /login, so it is
99
+ # written owner-only like the other credential files.
100
+ from aria_code.packages.aria_core.secure_file import write_secret_json
101
+
102
+ write_secret_json(self.config_file, payload)
103
+ self._snapshot = dict(cfg)
104
+
105
+ def get(self, key: str, default: Any = None) -> Any:
106
+ """Read one key from the last-loaded snapshot (loads on first use)."""
107
+ if self._snapshot is None:
108
+ self.load()
109
+ return self._snapshot.get(key, default)
110
+
111
+ def set(self, key: str, value: Any) -> dict:
112
+ """Update one key and persist immediately; returns the new snapshot."""
113
+ if self._snapshot is None:
114
+ self.load()
115
+ cfg = dict(self._snapshot)
116
+ cfg[key] = value
117
+ self.save(cfg)
118
+ return cfg
119
+
120
+ def snapshot(self) -> dict:
121
+ """The last-loaded/saved config dict (loads on first use)."""
122
+ if self._snapshot is None:
123
+ self.load()
124
+ return dict(self._snapshot)
125
+
126
+ # ── normalization steps (extracted verbatim from the CLI loader) ─────────
127
+
128
+ def _infer_local_provider(self, merged: dict, saved: dict) -> None:
129
+ saved_model = merged.get("model", "")
130
+ if "local_provider" not in saved:
131
+ merged["local_provider"] = (
132
+ saved_model.split("/", 1)[0].lower() if "/" in saved_model else "ollama"
133
+ )
134
+ if self.normalize_provider is not None:
135
+ try:
136
+ merged["local_provider"] = (
137
+ self.normalize_provider(merged.get("local_provider")) or "ollama"
138
+ )
139
+ except Exception:
140
+ pass
141
+
142
+ def _reset_stale_model(self, merged: dict) -> None:
143
+ model = merged.get("model", "")
144
+ if any(model.startswith(p) for p in self.stale_model_prefixes):
145
+ merged["model"] = self.defaults.get("model", "")
146
+
147
+ def _ensure_ui_lang(self, merged: dict) -> None:
148
+ if not merged.get("ui_lang"):
149
+ try:
150
+ merged["ui_lang"] = self.detect_lang() if self.detect_lang else "en"
151
+ except Exception:
152
+ merged["ui_lang"] = "en"
153
+
154
+ def _finish_load(self, cfg: dict) -> None:
155
+ self._snapshot = dict(cfg)
156
+ if self.on_loaded is not None:
157
+ self.on_loaded(cfg)