aria-code 0.167.0__py3-none-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (601) hide show
  1. aria_bots/__init__.py +7 -0
  2. aria_bots/channels/README.md +97 -0
  3. aria_bots/channels/__init__.py +1 -0
  4. aria_bots/channels/approvals.py +263 -0
  5. aria_bots/channels/conversation.py +280 -0
  6. aria_bots/channels/digest.py +186 -0
  7. aria_bots/channels/registry.py +95 -0
  8. aria_bots/engine.py +144 -0
  9. aria_bots/feishu_bot.py +1385 -0
  10. aria_bots/logistics/__init__.py +1 -0
  11. aria_bots/logistics/carriers.py +253 -0
  12. aria_bots/logistics/freight.py +154 -0
  13. aria_bots/logistics/inventory.py +242 -0
  14. aria_bots/logistics/mcp_server.py +156 -0
  15. aria_bots/logistics/records.py +119 -0
  16. aria_bots/logistics/tenancy.py +152 -0
  17. aria_bots/market.py +91 -0
  18. aria_bots/relay_client.py +326 -0
  19. aria_bots/telegram_bot.py +299 -0
  20. aria_code/__init__.py +111 -0
  21. aria_code/_bin/aria-native.exe +0 -0
  22. aria_code/_bin/aria-runtime.exe +0 -0
  23. aria_code/_version.py +20 -0
  24. aria_code/account_memory.py +296 -0
  25. aria_code/account_session.py +166 -0
  26. aria_code/adk_apps/__init__.py +1 -0
  27. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  28. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  29. aria_code/adk_apps/aria_research/__init__.py +1 -0
  30. aria_code/adk_apps/aria_research/agent.py +31 -0
  31. aria_code/agents/__init__.py +49 -0
  32. aria_code/agents/base.py +329 -0
  33. aria_code/agents/code_review.py +200 -0
  34. aria_code/agents/deep/__init__.py +37 -0
  35. aria_code/agents/deep/calibration_loop.py +144 -0
  36. aria_code/agents/deep/critic.py +125 -0
  37. aria_code/agents/deep/deepen.py +193 -0
  38. aria_code/agents/deep/models.py +149 -0
  39. aria_code/agents/deep/pipeline.py +164 -0
  40. aria_code/agents/deep/quant_fusion.py +192 -0
  41. aria_code/agents/deep/themes.py +95 -0
  42. aria_code/agents/deep/tiers.py +106 -0
  43. aria_code/agents/engineering/__init__.py +14 -0
  44. aria_code/agents/engineering/coder.py +222 -0
  45. aria_code/agents/engineering/tester.py +145 -0
  46. aria_code/agents/financial/__init__.py +21 -0
  47. aria_code/agents/financial/cashflow_burn.py +119 -0
  48. aria_code/agents/financial/catalyst.py +279 -0
  49. aria_code/agents/financial/corporate_finance.py +175 -0
  50. aria_code/agents/financial/debate.py +224 -0
  51. aria_code/agents/financial/earnings.py +315 -0
  52. aria_code/agents/financial/fundamental.py +178 -0
  53. aria_code/agents/financial/macro.py +111 -0
  54. aria_code/agents/financial/news.py +231 -0
  55. aria_code/agents/financial/risk.py +146 -0
  56. aria_code/agents/financial/sector.py +279 -0
  57. aria_code/agents/financial/strategist.py +121 -0
  58. aria_code/agents/financial/stripe_revenue.py +127 -0
  59. aria_code/agents/financial/synthesis.py +274 -0
  60. aria_code/agents/financial/technical.py +277 -0
  61. aria_code/agents/orchestrator.py +44 -0
  62. aria_code/agents/portfolio_agent.py +392 -0
  63. aria_code/agents/realty/__init__.py +62 -0
  64. aria_code/agents/realty/asset_diagnosis.py +152 -0
  65. aria_code/agents/realty/business_match.py +166 -0
  66. aria_code/agents/realty/cashflow_verify.py +209 -0
  67. aria_code/agents/realty/contract_rules.py +210 -0
  68. aria_code/agents/realty/energy_anomaly.py +189 -0
  69. aria_code/agents/realty/exit_settlement.py +208 -0
  70. aria_code/agents/realty/fulfillment_risk.py +206 -0
  71. aria_code/agents/realty/ops_optimize.py +160 -0
  72. aria_code/agents/realty/revenue_share.py +215 -0
  73. aria_code/agents/registry.py +168 -0
  74. aria_code/agents/signal_scheme.py +111 -0
  75. aria_code/agents/sports/__init__.py +0 -0
  76. aria_code/agents/sports/football_agent.py +197 -0
  77. aria_code/agents/supervisor.py +113 -0
  78. aria_code/agents/team.py +500 -0
  79. aria_code/agents/video/__init__.py +16 -0
  80. aria_code/agents/video/director.py +376 -0
  81. aria_code/agents/video/qa_agent.py +143 -0
  82. aria_code/agents/video/script_doctor.py +112 -0
  83. aria_code/agents/warehouse/__init__.py +25 -0
  84. aria_code/agents/warehouse/contracts.py +26 -0
  85. aria_code/agents/warehouse/dashboard.py +155 -0
  86. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  87. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  88. aria_code/agents/warehouse/inventory_health.py +24 -0
  89. aria_code/agents/warehouse/local_run.py +71 -0
  90. aria_code/agents/warehouse/local_snapshot.py +132 -0
  91. aria_code/agents/warehouse/logistics_cost.py +84 -0
  92. aria_code/agents/warehouse/logistics_sync.py +21 -0
  93. aria_code/agents/warehouse/navigation.schema.json +110 -0
  94. aria_code/agents/warehouse/task_agent.py +172 -0
  95. aria_code/agents/warehouse/workflow.py +31 -0
  96. aria_code/aliyun_data_client.py +4 -0
  97. aria_code/apps/README.md +12 -0
  98. aria_code/apps/__init__.py +2 -0
  99. aria_code/apps/channels/README.md +9 -0
  100. aria_code/apps/channels/__init__.py +17 -0
  101. aria_code/apps/channels/approvals.py +11 -0
  102. aria_code/apps/channels/conversation.py +11 -0
  103. aria_code/apps/channels/digest.py +11 -0
  104. aria_code/apps/channels/intake.py +92 -0
  105. aria_code/apps/channels/registry.py +11 -0
  106. aria_code/apps/channels/tradingview.py +94 -0
  107. aria_code/apps/cli/README.md +13 -0
  108. aria_code/apps/cli/__init__.py +2 -0
  109. aria_code/apps/cli/_optional.py +120 -0
  110. aria_code/apps/cli/app_server.py +656 -0
  111. aria_code/apps/cli/auto_update.py +125 -0
  112. aria_code/apps/cli/bootstrap.py +206 -0
  113. aria_code/apps/cli/broker_render.py +206 -0
  114. aria_code/apps/cli/chat_turn.py +1327 -0
  115. aria_code/apps/cli/cloud_models.py +231 -0
  116. aria_code/apps/cli/codegen_paths.py +29 -0
  117. aria_code/apps/cli/commands/__init__.py +34 -0
  118. aria_code/apps/cli/commands/_business_render.py +78 -0
  119. aria_code/apps/cli/commands/_ui.py +71 -0
  120. aria_code/apps/cli/commands/analysis_cmds.py +378 -0
  121. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  122. aria_code/apps/cli/commands/auth_cmds.py +215 -0
  123. aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
  124. aria_code/apps/cli/commands/broker_cmds.py +1236 -0
  125. aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
  126. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  127. aria_code/apps/cli/commands/catalog.py +189 -0
  128. aria_code/apps/cli/commands/core_cmds.py +2748 -0
  129. aria_code/apps/cli/commands/data_cmds.py +521 -0
  130. aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
  131. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  132. aria_code/apps/cli/commands/engineering_cmds.py +399 -0
  133. aria_code/apps/cli/commands/file_cmds.py +312 -0
  134. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  135. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  136. aria_code/apps/cli/commands/market.py +544 -0
  137. aria_code/apps/cli/commands/market_cmds.py +1419 -0
  138. aria_code/apps/cli/commands/market_context.py +432 -0
  139. aria_code/apps/cli/commands/market_render.py +7 -0
  140. aria_code/apps/cli/commands/model_cmds.py +2374 -0
  141. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  142. aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
  143. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  144. aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
  145. aria_code/apps/cli/commands/report.py +476 -0
  146. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  147. aria_code/apps/cli/commands/session_cmds.py +236 -0
  148. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  149. aria_code/apps/cli/commands/team.py +723 -0
  150. aria_code/apps/cli/commands/team_render.py +8 -0
  151. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  152. aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
  153. aria_code/apps/cli/commands/workflow_cmds.py +629 -0
  154. aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
  155. aria_code/apps/cli/config_paths.py +73 -0
  156. aria_code/apps/cli/config_store.py +158 -0
  157. aria_code/apps/cli/constants.py +88 -0
  158. aria_code/apps/cli/context.py +34 -0
  159. aria_code/apps/cli/conversation_policy.py +53 -0
  160. aria_code/apps/cli/deterministic.py +200 -0
  161. aria_code/apps/cli/direct.py +48 -0
  162. aria_code/apps/cli/earnings_view.py +172 -0
  163. aria_code/apps/cli/endpoint_migration.py +64 -0
  164. aria_code/apps/cli/exec_events.py +255 -0
  165. aria_code/apps/cli/file_references.py +155 -0
  166. aria_code/apps/cli/finance_service_catalog.py +111 -0
  167. aria_code/apps/cli/football_reports.py +231 -0
  168. aria_code/apps/cli/github_app_auth.py +135 -0
  169. aria_code/apps/cli/google_login.py +239 -0
  170. aria_code/apps/cli/handlers/__init__.py +11 -0
  171. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  172. aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
  173. aria_code/apps/cli/handlers/market_handlers.py +2570 -0
  174. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  175. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  176. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  177. aria_code/apps/cli/headless.py +352 -0
  178. aria_code/apps/cli/helpers.py +326 -0
  179. aria_code/apps/cli/hooks.py +180 -0
  180. aria_code/apps/cli/i18n.py +310 -0
  181. aria_code/apps/cli/intent.py +136 -0
  182. aria_code/apps/cli/intent_router.py +305 -0
  183. aria_code/apps/cli/intent_shadow.py +72 -0
  184. aria_code/apps/cli/intent_signals.py +347 -0
  185. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  186. aria_code/apps/cli/local_tool_schemas.py +550 -0
  187. aria_code/apps/cli/main.py +39 -0
  188. aria_code/apps/cli/market_hours.py +100 -0
  189. aria_code/apps/cli/market_metadata.py +135 -0
  190. aria_code/apps/cli/market_universe.py +487 -0
  191. aria_code/apps/cli/message_processing.py +310 -0
  192. aria_code/apps/cli/mixin_binding.py +32 -0
  193. aria_code/apps/cli/model_catalog.py +652 -0
  194. aria_code/apps/cli/model_probe.py +169 -0
  195. aria_code/apps/cli/model_retirement.py +65 -0
  196. aria_code/apps/cli/native_branding.py +12 -0
  197. aria_code/apps/cli/native_bridge.py +118 -0
  198. aria_code/apps/cli/native_entry.py +58 -0
  199. aria_code/apps/cli/pdf_report.py +977 -0
  200. aria_code/apps/cli/plan_mode.py +139 -0
  201. aria_code/apps/cli/plotly_html.py +15 -0
  202. aria_code/apps/cli/prediction_feedback.py +202 -0
  203. aria_code/apps/cli/preflight.py +502 -0
  204. aria_code/apps/cli/project_aria.py +60 -0
  205. aria_code/apps/cli/prompt_assembly.py +78 -0
  206. aria_code/apps/cli/prompts/__init__.py +0 -0
  207. aria_code/apps/cli/prompts/coding.py +71 -0
  208. aria_code/apps/cli/prompts/select.py +193 -0
  209. aria_code/apps/cli/prompts/system_prompts.py +562 -0
  210. aria_code/apps/cli/prompts/ui.py +436 -0
  211. aria_code/apps/cli/provider_endpoints.py +237 -0
  212. aria_code/apps/cli/provider_keys.py +73 -0
  213. aria_code/apps/cli/providers/__init__.py +1 -0
  214. aria_code/apps/cli/providers/base.py +775 -0
  215. aria_code/apps/cli/providers/chat_routing.py +139 -0
  216. aria_code/apps/cli/providers/collaboration.py +98 -0
  217. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  218. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  219. aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
  220. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  221. aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
  222. aria_code/apps/cli/providers/runtime_bridge.py +586 -0
  223. aria_code/apps/cli/providers/vertexai_stream.py +592 -0
  224. aria_code/apps/cli/release_download.py +159 -0
  225. aria_code/apps/cli/review_runner.py +111 -0
  226. aria_code/apps/cli/runtime_consumer.py +769 -0
  227. aria_code/apps/cli/sandbox.py +32 -0
  228. aria_code/apps/cli/session_export.py +120 -0
  229. aria_code/apps/cli/session_jsonl.py +208 -0
  230. aria_code/apps/cli/session_refresh.py +123 -0
  231. aria_code/apps/cli/session_store.py +125 -0
  232. aria_code/apps/cli/skills_catalog.py +273 -0
  233. aria_code/apps/cli/task_isolation.py +211 -0
  234. aria_code/apps/cli/terminal_chrome.py +412 -0
  235. aria_code/apps/cli/todo_tracker.py +190 -0
  236. aria_code/apps/cli/tool_executor.py +932 -0
  237. aria_code/apps/cli/tool_registry.py +34 -0
  238. aria_code/apps/cli/tool_scope.py +113 -0
  239. aria_code/apps/cli/tools/__init__.py +40 -0
  240. aria_code/apps/cli/tools/context.py +46 -0
  241. aria_code/apps/cli/tools/file_tools.py +181 -0
  242. aria_code/apps/cli/tools/market_tools.py +626 -0
  243. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  244. aria_code/apps/cli/tools/system_tools.py +656 -0
  245. aria_code/apps/cli/tools/write_tools.py +961 -0
  246. aria_code/apps/cli/tradingview_bridge.py +529 -0
  247. aria_code/apps/cli/transactions.py +269 -0
  248. aria_code/apps/cli/turn_planning.py +137 -0
  249. aria_code/apps/cli/update_check.py +257 -0
  250. aria_code/apps/cli/updater.py +373 -0
  251. aria_code/apps/cli/utils/__init__.py +0 -0
  252. aria_code/apps/cli/utils/market_detect.py +1663 -0
  253. aria_code/apps/cli/web_login.py +136 -0
  254. aria_code/apps/cli/workflow_runner.py +127 -0
  255. aria_code/apps/cli/workspace_route.py +121 -0
  256. aria_code/apps/daemon/README.md +14 -0
  257. aria_code/apps/vscode/README.md +115 -0
  258. aria_code/apps/vscode/package.json +70 -0
  259. aria_code/aria_cli.py +5244 -0
  260. aria_code/aria_daemon.py +1372 -0
  261. aria_code/aria_feishu_bot.py +16 -0
  262. aria_code/aria_mcp_server.py +61 -0
  263. aria_code/aria_relay_client.py +4 -0
  264. aria_code/aria_telegram_bot.py +11 -0
  265. aria_code/ariarc.py +385 -0
  266. aria_code/artifacts.py +581 -0
  267. aria_code/backtest_engine.py +4 -0
  268. aria_code/backtest_report.py +4 -0
  269. aria_code/brokers/__init__.py +78 -0
  270. aria_code/brokers/automation.py +193 -0
  271. aria_code/brokers/base.py +231 -0
  272. aria_code/brokers/capabilities.py +264 -0
  273. aria_code/brokers/cn/__init__.py +10 -0
  274. aria_code/brokers/cn/easytrader_broker.py +193 -0
  275. aria_code/brokers/cn/futu_broker.py +194 -0
  276. aria_code/brokers/cn/longbridge_broker.py +190 -0
  277. aria_code/brokers/cn/tiger_broker.py +196 -0
  278. aria_code/brokers/cn/xtquant_broker.py +225 -0
  279. aria_code/brokers/config.py +414 -0
  280. aria_code/brokers/intl/__init__.py +5 -0
  281. aria_code/brokers/intl/alpaca_broker.py +183 -0
  282. aria_code/brokers/intl/ibkr_broker.py +215 -0
  283. aria_code/brokers/intl/trading212_broker.py +183 -0
  284. aria_code/brokers/intl/webull_broker.py +156 -0
  285. aria_code/brokers/paper_broker.py +266 -0
  286. aria_code/brokers/planning.py +296 -0
  287. aria_code/brokers/registry.py +213 -0
  288. aria_code/brokers/trading.py +267 -0
  289. aria_code/canva_client.py +4 -0
  290. aria_code/change_store.py +127 -0
  291. aria_code/clients/__init__.py +0 -0
  292. aria_code/clients/aliyun_data_client.py +660 -0
  293. aria_code/clients/aria_relay_client.py +16 -0
  294. aria_code/clients/canva_client.py +284 -0
  295. aria_code/clients/figma_client.py +100 -0
  296. aria_code/clients/football_data_client.py +1671 -0
  297. aria_code/clients/kling_video_client.py +182 -0
  298. aria_code/clients/market_data_client.py +1985 -0
  299. aria_code/clients/openai_image_client.py +242 -0
  300. aria_code/clients/runway_video_client.py +157 -0
  301. aria_code/clients/warehouse_erp_client.py +105 -0
  302. aria_code/cloud_memory.py +190 -0
  303. aria_code/command_safety.py +19 -0
  304. aria_code/computer_use_server.py +276 -0
  305. aria_code/computer_use_tools.py +4 -0
  306. aria_code/dashboard_generator.py +579 -0
  307. aria_code/data_analysis_tools.py +4 -0
  308. aria_code/data_cleaner.py +483 -0
  309. aria_code/data_service.py +513 -0
  310. aria_code/datasources/__init__.py +23 -0
  311. aria_code/datasources/base.py +166 -0
  312. aria_code/datasources/router.py +253 -0
  313. aria_code/datasources/sec_statements.py +217 -0
  314. aria_code/datasources/sources/__init__.py +15 -0
  315. aria_code/datasources/sources/akshare_source.py +269 -0
  316. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  317. aria_code/datasources/sources/edgar_source.py +219 -0
  318. aria_code/datasources/sources/finnhub_source.py +198 -0
  319. aria_code/datasources/sources/fred_source.py +223 -0
  320. aria_code/datasources/sources/tushare_source.py +143 -0
  321. aria_code/datasources/sources/web_scraper_source.py +278 -0
  322. aria_code/datasources/sources/world_bank_source.py +206 -0
  323. aria_code/datasources/sources/yfinance_source.py +152 -0
  324. aria_code/doctor.py +860 -0
  325. aria_code/domain/__init__.py +0 -0
  326. aria_code/domain/_finance_core/__init__.py +5 -0
  327. aria_code/domain/_finance_core/backtest_engine.py +325 -0
  328. aria_code/domain/_finance_core/performance_stats.py +101 -0
  329. aria_code/domain/backtest_engine.py +75 -0
  330. aria_code/domain/backtest_report.py +488 -0
  331. aria_code/domain/finance_formulas.py +389 -0
  332. aria_code/domain/performance_stats.py +6 -0
  333. aria_code/domain/portfolio_ledger.py +307 -0
  334. aria_code/domain/strategy_vault.py +421 -0
  335. aria_code/evals/__init__.py +37 -0
  336. aria_code/evals/behavior.py +113 -0
  337. aria_code/evals/gap.py +93 -0
  338. aria_code/evals/harness.py +949 -0
  339. aria_code/evals/review_probe.py +132 -0
  340. aria_code/evals/runner.py +425 -0
  341. aria_code/evals/scorecard.py +322 -0
  342. aria_code/evals/summary.py +125 -0
  343. aria_code/external_agent_runner.py +115 -0
  344. aria_code/figma_client.py +4 -0
  345. aria_code/file_analysis_tools.py +4 -0
  346. aria_code/finance_formulas.py +4 -0
  347. aria_code/football_data_client.py +4 -0
  348. aria_code/image_gen_tools.py +4 -0
  349. aria_code/intent_classifier.py +459 -0
  350. aria_code/kling_video_client.py +4 -0
  351. aria_code/licensing.py +151 -0
  352. aria_code/local_finance_tools.py +4 -0
  353. aria_code/local_image_provider.py +4 -0
  354. aria_code/local_llm_provider.py +4 -0
  355. aria_code/macro_tools.py +4 -0
  356. aria_code/markdown_pdf.py +289 -0
  357. aria_code/market_data_client.py +4 -0
  358. aria_code/mcp_client.py +744 -0
  359. aria_code/memory_manager.py +273 -0
  360. aria_code/model_capability.py +512 -0
  361. aria_code/notification_tools.py +4 -0
  362. aria_code/openai_image_client.py +4 -0
  363. aria_code/packages/__init__.py +26 -0
  364. aria_code/packages/adk_bridge/__init__.py +11 -0
  365. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  366. aria_code/packages/adk_bridge/market_tools.py +125 -0
  367. aria_code/packages/aria_agents/__init__.py +5 -0
  368. aria_code/packages/aria_agents/manifest.py +78 -0
  369. aria_code/packages/aria_core/__init__.py +34 -0
  370. aria_code/packages/aria_core/architecture.py +269 -0
  371. aria_code/packages/aria_core/export.py +147 -0
  372. aria_code/packages/aria_core/manifest.py +65 -0
  373. aria_code/packages/aria_core/paths.py +57 -0
  374. aria_code/packages/aria_core/secure_file.py +68 -0
  375. aria_code/packages/aria_infra/__init__.py +15 -0
  376. aria_code/packages/aria_infra/arthera.py +58 -0
  377. aria_code/packages/aria_infra/doctor.py +246 -0
  378. aria_code/packages/aria_infra/product.py +40 -0
  379. aria_code/packages/aria_mcp/__init__.py +25 -0
  380. aria_code/packages/aria_mcp/bridge.py +232 -0
  381. aria_code/packages/aria_mcp/circuit.py +82 -0
  382. aria_code/packages/aria_mcp/config.py +162 -0
  383. aria_code/packages/aria_mcp/installer.py +95 -0
  384. aria_code/packages/aria_mcp/server.py +1705 -0
  385. aria_code/packages/aria_mcp/tools.py +81 -0
  386. aria_code/packages/aria_sdk/__init__.py +19 -0
  387. aria_code/packages/aria_sdk/client.py +393 -0
  388. aria_code/packages/aria_sdk/providers.py +70 -0
  389. aria_code/packages/aria_sdk/streaming.py +75 -0
  390. aria_code/packages/aria_sdk/types.py +86 -0
  391. aria_code/packages/aria_services/__init__.py +92 -0
  392. aria_code/packages/aria_services/cache.py +60 -0
  393. aria_code/packages/aria_services/context.py +356 -0
  394. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  395. aria_code/packages/aria_services/data.py +11 -0
  396. aria_code/packages/aria_services/provider_health.py +304 -0
  397. aria_code/packages/aria_services/references.py +247 -0
  398. aria_code/packages/aria_services/registry.py +249 -0
  399. aria_code/packages/aria_services/research_protocol.py +137 -0
  400. aria_code/packages/aria_services/research_quality.py +186 -0
  401. aria_code/packages/aria_services/settings.py +160 -0
  402. aria_code/packages/aria_services/usage.py +151 -0
  403. aria_code/packages/aria_skills/__init__.py +46 -0
  404. aria_code/packages/aria_skills/catalog.py +149 -0
  405. aria_code/packages/aria_skills/loader.py +455 -0
  406. aria_code/packages/aria_skills/registry.py +84 -0
  407. aria_code/packages/aria_tools/__init__.py +5 -0
  408. aria_code/packages/aria_tools/financial/__init__.py +5 -0
  409. aria_code/packages/aria_tools/financial/_paths.py +92 -0
  410. aria_code/packages/aria_tools/registry.py +129 -0
  411. aria_code/packages/quant_engine/__init__.py +33 -0
  412. aria_code/packages/quant_engine/_namespace.py +24 -0
  413. aria_code/packages/quant_engine/backtest/__init__.py +10 -0
  414. aria_code/packages/quant_engine/backtest/core.py +53 -0
  415. aria_code/packages/quant_engine/backtest/engine.py +102 -0
  416. aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
  417. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  418. aria_code/packages/quant_engine/sports/__init__.py +76 -0
  419. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  420. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  421. aria_code/packages/quant_engine/sports/elo.py +300 -0
  422. aria_code/packages/quant_engine/sports/form.py +188 -0
  423. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  424. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  425. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  426. aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
  427. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  428. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  429. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  430. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  431. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  432. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  433. aria_code/packs/__init__.py +85 -0
  434. aria_code/packs/base.py +168 -0
  435. aria_code/packs/finance/__init__.py +250 -0
  436. aria_code/packs/healthcare/__init__.py +40 -0
  437. aria_code/packs/logistics/__init__.py +171 -0
  438. aria_code/packs/payments/__init__.py +139 -0
  439. aria_code/packs/realty/__init__.py +190 -0
  440. aria_code/packs/registry.py +202 -0
  441. aria_code/packs/rules.py +90 -0
  442. aria_code/plan_utils.py +194 -0
  443. aria_code/plugin_loader.py +328 -0
  444. aria_code/portfolio_ledger.py +4 -0
  445. aria_code/preview_server.py +449 -0
  446. aria_code/privacy/__init__.py +5 -0
  447. aria_code/privacy/feedback.py +123 -0
  448. aria_code/project_review.py +278 -0
  449. aria_code/project_review_api.py +235 -0
  450. aria_code/project_review_server.py +18 -0
  451. aria_code/project_tools.py +4 -0
  452. aria_code/providers/__init__.py +30 -0
  453. aria_code/providers/google_video_provider.py +256 -0
  454. aria_code/providers/llm/__init__.py +19 -0
  455. aria_code/providers/llm/anthropic.py +184 -0
  456. aria_code/providers/llm/arthera_gateway.py +90 -0
  457. aria_code/providers/llm/autoconfig.py +213 -0
  458. aria_code/providers/llm/base.py +139 -0
  459. aria_code/providers/llm/custom.py +158 -0
  460. aria_code/providers/llm/ollama.py +137 -0
  461. aria_code/providers/llm/openai_compat.py +350 -0
  462. aria_code/providers/llm/registry.py +457 -0
  463. aria_code/providers/local_image_provider.py +202 -0
  464. aria_code/providers/local_llm_provider.py +549 -0
  465. aria_code/providers/ollama_models.py +79 -0
  466. aria_code/providers/openai_params.py +32 -0
  467. aria_code/providers/tool_messages.py +100 -0
  468. aria_code/realty_data_tools.py +4 -0
  469. aria_code/report_exporters.py +150 -0
  470. aria_code/report_generator.py +1764 -0
  471. aria_code/review_service.py +497 -0
  472. aria_code/runtime/__init__.py +176 -0
  473. aria_code/runtime/acceptance.py +632 -0
  474. aria_code/runtime/agent_loop.py +1851 -0
  475. aria_code/runtime/agent_profiles.py +75 -0
  476. aria_code/runtime/approval.py +71 -0
  477. aria_code/runtime/baseline.py +219 -0
  478. aria_code/runtime/budget.py +232 -0
  479. aria_code/runtime/checkpoints.py +481 -0
  480. aria_code/runtime/consult.py +65 -0
  481. aria_code/runtime/contract.py +282 -0
  482. aria_code/runtime/delivery.py +432 -0
  483. aria_code/runtime/events.py +113 -0
  484. aria_code/runtime/gateway.py +167 -0
  485. aria_code/runtime/lsp.py +484 -0
  486. aria_code/runtime/native_index.py +153 -0
  487. aria_code/runtime/orchestrator.py +55 -0
  488. aria_code/runtime/processes.py +343 -0
  489. aria_code/runtime/project_graph.py +614 -0
  490. aria_code/runtime/repo_map.py +712 -0
  491. aria_code/runtime/research_bundle.py +105 -0
  492. aria_code/runtime/review.py +269 -0
  493. aria_code/runtime/run_state.py +127 -0
  494. aria_code/runtime/run_store.py +522 -0
  495. aria_code/runtime/scoped_runs.py +113 -0
  496. aria_code/runtime/self_healing.py +422 -0
  497. aria_code/runtime/semantic_diff.py +171 -0
  498. aria_code/runtime/subagent.py +721 -0
  499. aria_code/runtime/symbol_edit.py +117 -0
  500. aria_code/runtime/task_brief.py +46 -0
  501. aria_code/runtime/task_context.py +145 -0
  502. aria_code/runtime/task_ledger.py +108 -0
  503. aria_code/runtime/task_orchestrator.py +90 -0
  504. aria_code/runtime/task_outcome.py +33 -0
  505. aria_code/runtime/task_verification.py +266 -0
  506. aria_code/runtime/task_worktree.py +448 -0
  507. aria_code/runtime/tool_executor.py +329 -0
  508. aria_code/runtime/tool_policy.py +107 -0
  509. aria_code/runtime/transactions.py +216 -0
  510. aria_code/runtime/windows_job.py +115 -0
  511. aria_code/runtime/workflows.py +222 -0
  512. aria_code/runtime/worktrees.py +215 -0
  513. aria_code/runway_video_client.py +4 -0
  514. aria_code/safety/__init__.py +27 -0
  515. aria_code/safety/client_isolation.py +178 -0
  516. aria_code/safety/permissions.py +339 -0
  517. aria_code/safety/risk.py +699 -0
  518. aria_code/safety/sandbox.py +148 -0
  519. aria_code/safety/service.py +102 -0
  520. aria_code/service_endpoints.py +14 -0
  521. aria_code/setup_wizard.py +684 -0
  522. aria_code/spreadsheet_tools.py +4 -0
  523. aria_code/strategy_vault.py +4 -0
  524. aria_code/tools/__init__.py +0 -0
  525. aria_code/tools/artifact_tools.py +252 -0
  526. aria_code/tools/broker_tools.py +92 -0
  527. aria_code/tools/build_quant_engine.py +97 -0
  528. aria_code/tools/code_audit_tools.py +192 -0
  529. aria_code/tools/computer_use_tools.py +509 -0
  530. aria_code/tools/data_analysis_tools.py +831 -0
  531. aria_code/tools/enterprise_finance_tools.py +202 -0
  532. aria_code/tools/extended_tools.py +240 -0
  533. aria_code/tools/file_analysis_tools.py +880 -0
  534. aria_code/tools/image_gen_tools.py +114 -0
  535. aria_code/tools/local_finance_tools.py +3488 -0
  536. aria_code/tools/logistics_carriers.py +11 -0
  537. aria_code/tools/logistics_inventory.py +11 -0
  538. aria_code/tools/logistics_io.py +11 -0
  539. aria_code/tools/logistics_tenancy.py +11 -0
  540. aria_code/tools/logistics_tools.py +11 -0
  541. aria_code/tools/macro_tools.py +412 -0
  542. aria_code/tools/notification_tools.py +249 -0
  543. aria_code/tools/project_tools.py +525 -0
  544. aria_code/tools/realty_data_tools.py +662 -0
  545. aria_code/tools/spreadsheet_tools.py +399 -0
  546. aria_code/tools/stripe_tools.py +115 -0
  547. aria_code/tools/video_tools.py +500 -0
  548. aria_code/training/__init__.py +1 -0
  549. aria_code/training/compare.py +117 -0
  550. aria_code/training/prelude_commands.py +111 -0
  551. aria_code/training/prelude_eval.py +390 -0
  552. aria_code/training/prelude_real.py +183 -0
  553. aria_code/training/prelude_train.py +248 -0
  554. aria_code/training/sft_dataset.py +230 -0
  555. aria_code/training/trajectory_dataset.py +263 -0
  556. aria_code/ui/__init__.py +105 -0
  557. aria_code/ui/banner.py +404 -0
  558. aria_code/ui/completer.py +571 -0
  559. aria_code/ui/console.py +285 -0
  560. aria_code/ui/input_box.py +505 -0
  561. aria_code/ui/mentions.py +206 -0
  562. aria_code/ui/picker.py +248 -0
  563. aria_code/ui/render/__init__.py +11 -0
  564. aria_code/ui/render/actions.py +297 -0
  565. aria_code/ui/render/finance.py +1732 -0
  566. aria_code/ui/render/market.py +253 -0
  567. aria_code/ui/render/output.py +954 -0
  568. aria_code/ui/render/responsive.py +75 -0
  569. aria_code/ui/render/team.py +367 -0
  570. aria_code/ui/robot.py +239 -0
  571. aria_code/ui/startup_dashboard.py +110 -0
  572. aria_code/video_analysis.py +147 -0
  573. aria_code/video_editor.py +518 -0
  574. aria_code/video_workflow/__init__.py +8 -0
  575. aria_code/video_workflow/__main__.py +61 -0
  576. aria_code/video_workflow/engines/__init__.py +18 -0
  577. aria_code/video_workflow/engines/base.py +69 -0
  578. aria_code/video_workflow/engines/custom_model.py +204 -0
  579. aria_code/video_workflow/engines/google_veo.py +94 -0
  580. aria_code/video_workflow/engines/local_engine.py +71 -0
  581. aria_code/video_workflow/engines/registry.py +98 -0
  582. aria_code/video_workflow/google_models.py +278 -0
  583. aria_code/video_workflow/music/__init__.py +5 -0
  584. aria_code/video_workflow/music/foley_service.py +132 -0
  585. aria_code/video_workflow/music/service.py +212 -0
  586. aria_code/video_workflow/pipeline.py +620 -0
  587. aria_code/video_workflow/plan.py +202 -0
  588. aria_code/video_workflow/session.py +297 -0
  589. aria_code/workspace/__init__.py +6 -0
  590. aria_code/workspace/files.py +219 -0
  591. aria_code/workspace/verify.py +167 -0
  592. aria_code/workspace_changes.py +199 -0
  593. aria_code/workspace_service.py +196 -0
  594. aria_code/workspace_tools.py +150 -0
  595. aria_code-0.167.0.dist-info/METADATA +330 -0
  596. aria_code-0.167.0.dist-info/RECORD +601 -0
  597. aria_code-0.167.0.dist-info/WHEEL +5 -0
  598. aria_code-0.167.0.dist-info/entry_points.txt +5 -0
  599. aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
  600. aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
  601. aria_code-0.167.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,449 @@
1
+ """preview_server.py — local, opt-in live-preview server for generated
2
+ artifacts (the aria-code half of the "Canvas/Artifacts" feature).
3
+
4
+ Off by default: nothing in this module runs until a human types `/canvas`
5
+ at the terminal (see apps/cli/commands/canvas_cmds.py). Binds to 127.0.0.1
6
+ only — never 0.0.0.0 — since this exists purely to open a local browser tab
7
+ next to the terminal, not to serve anything over the network.
8
+
9
+ Wire-up: artifacts.write_artifact_metadata() calls notify_new_version()
10
+ on the active session (a no-op when no session is running, which is the
11
+ overwhelmingly common case — every headless MCP tool call goes through that
12
+ function without a preview session ever existing). Versions are grouped by
13
+ a thread_id derived from the artifact's category+topic, so re-running the
14
+ same report for the same symbol appends to one thread's history instead of
15
+ starting a new one each time.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import asyncio
21
+ import json
22
+ import logging
23
+ import mimetypes
24
+ import os
25
+ import time
26
+ import webbrowser
27
+ from dataclasses import dataclass, field
28
+ from datetime import datetime
29
+ from pathlib import Path
30
+ from typing import Any, Dict, List, Optional
31
+
32
+ logger = logging.getLogger("aria_preview_server")
33
+
34
+ _DEFAULT_HOST = "127.0.0.1"
35
+ _PORT_CANDIDATES = list(range(8765, 8775))
36
+
37
+ _active_session: Optional["PreviewSession"] = None
38
+
39
+
40
+ def discovery_path() -> Path:
41
+ """Where a running canvas advertises its URL.
42
+
43
+ The port is picked from a range at bind time, so a *separate* process
44
+ (notably Arthera's Electron terminal, which embeds this server's output
45
+ in its artifact preview panel) cannot guess it. This file is the
46
+ handshake: written on start, removed on stop.
47
+ """
48
+ from aria_code.brokers.config import BROKERS_CONFIG_PATH # same ~/.aria-code|.arthera home
49
+
50
+ return BROKERS_CONFIG_PATH.parent / "canvas.json"
51
+
52
+
53
+ def _write_discovery(url: str) -> None:
54
+ try:
55
+ path = discovery_path()
56
+ path.parent.mkdir(parents=True, exist_ok=True)
57
+ path.write_text(
58
+ json.dumps({"url": url, "pid": os.getpid(), "started_at": time.time()}, indent=2),
59
+ encoding="utf-8",
60
+ )
61
+ except Exception as exc: # discovery is a convenience, never fatal
62
+ logger.warning("Could not write canvas discovery file: %s", exc)
63
+
64
+
65
+ def _clear_discovery() -> None:
66
+ try:
67
+ discovery_path().unlink(missing_ok=True)
68
+ except Exception as exc:
69
+ logger.warning("Could not remove canvas discovery file: %s", exc)
70
+
71
+
72
+ def read_discovery() -> Optional[Dict[str, Any]]:
73
+ """Read another process's advertised canvas URL, or None.
74
+
75
+ Returns None for a stale file — one whose PID is no longer alive, which
76
+ is what a crash or `kill -9` leaves behind. Without that check a consumer
77
+ would keep pointing an iframe at a dead port indefinitely.
78
+ """
79
+ try:
80
+ path = discovery_path()
81
+ if not path.exists():
82
+ return None
83
+ data = json.loads(path.read_text(encoding="utf-8"))
84
+ pid = data.get("pid")
85
+ if isinstance(pid, int):
86
+ try:
87
+ os.kill(pid, 0) # signal 0 = liveness probe, does not kill
88
+ except ProcessLookupError:
89
+ return None
90
+ except PermissionError:
91
+ pass # alive, owned by another user
92
+ return data
93
+ except Exception:
94
+ return None
95
+
96
+
97
+ def get_active_session() -> Optional["PreviewSession"]:
98
+ return _active_session
99
+
100
+
101
+ def thread_id_for(record: Any) -> str:
102
+ """Group versions of "the same logical artifact" together — same
103
+ category+topic (e.g. a market report for AAPL) is one thread across
104
+ repeated runs, not a new thread every time."""
105
+ return f"{record.category}:{record.topic}"
106
+
107
+
108
+ @dataclass
109
+ class _Version:
110
+ index: int
111
+ path: Path
112
+ created_at: str
113
+ kind: str
114
+
115
+
116
+ @dataclass
117
+ class _Thread:
118
+ id: str
119
+ versions: List[_Version] = field(default_factory=list)
120
+ updated_at: float = 0.0
121
+
122
+
123
+ class PreviewSession:
124
+ """One running local preview server. Create via `start()`, not directly."""
125
+
126
+ def __init__(self) -> None:
127
+ self.threads: Dict[str, _Thread] = {}
128
+ self.active_thread_id: Optional[str] = None
129
+ self.url: str = ""
130
+ self._runner = None
131
+ self._sse_clients: List[Any] = []
132
+
133
+ # -- lifecycle ----------------------------------------------------------
134
+
135
+ async def start(self, host: str = _DEFAULT_HOST) -> str:
136
+ from aiohttp import web
137
+
138
+ app = web.Application()
139
+ app.router.add_get("/", self._handle_index)
140
+ app.router.add_get("/events", self._handle_events)
141
+ app.router.add_get("/state", self._handle_state)
142
+ app.router.add_get("/artifact/{thread_id:.*}", self._handle_artifact)
143
+
144
+ runner = web.AppRunner(app)
145
+ await runner.setup()
146
+
147
+ last_error: Optional[Exception] = None
148
+ for port in _PORT_CANDIDATES:
149
+ site = web.TCPSite(runner, host, port)
150
+ try:
151
+ await site.start()
152
+ except OSError as exc:
153
+ last_error = exc
154
+ continue
155
+ self._runner = runner
156
+ self.url = f"http://{host}:{port}/"
157
+ return self.url
158
+ await runner.cleanup()
159
+ raise RuntimeError(
160
+ f"Could not bind any port in {_PORT_CANDIDATES[0]}-{_PORT_CANDIDATES[-1]} "
161
+ f"for the preview server: {last_error}"
162
+ )
163
+
164
+ async def stop(self) -> None:
165
+ if self._runner is not None:
166
+ await self._runner.cleanup()
167
+ self._runner = None
168
+ self.url = ""
169
+ self.threads.clear()
170
+ self.active_thread_id = None
171
+ self._sse_clients.clear()
172
+
173
+ # -- artifact notification ----------------------------------------------
174
+
175
+ async def notify_new_version(self, thread_id: str, record: Any) -> None:
176
+ thread = self.threads.setdefault(thread_id, _Thread(id=thread_id))
177
+ version = _Version(
178
+ index=len(thread.versions),
179
+ path=Path(record.path),
180
+ created_at=datetime.now().isoformat(timespec="seconds"),
181
+ kind=_kind_for_suffix(Path(record.path).suffix),
182
+ )
183
+ thread.versions.append(version)
184
+ thread.updated_at = time.time()
185
+ self.active_thread_id = thread_id
186
+ await self._broadcast({
187
+ "type": "new-version",
188
+ "thread_id": thread_id,
189
+ "version_index": version.index,
190
+ "url": f"/artifact/{thread_id}?v={version.index}",
191
+ })
192
+
193
+ async def _broadcast(self, payload: Dict[str, Any]) -> None:
194
+ if not self._sse_clients:
195
+ return
196
+ data = f"data: {json.dumps(payload, ensure_ascii=False)}\n\n".encode("utf-8")
197
+ dead = []
198
+ for resp in self._sse_clients:
199
+ try:
200
+ await resp.write(data)
201
+ except Exception:
202
+ dead.append(resp)
203
+ for resp in dead:
204
+ self._sse_clients.remove(resp)
205
+
206
+ # -- HTTP handlers --------------------------------------------------------
207
+
208
+ async def _handle_index(self, request):
209
+ from aiohttp import web
210
+ return web.Response(text=_SHELL_HTML, content_type="text/html")
211
+
212
+ async def _handle_events(self, request):
213
+ from aiohttp import web
214
+
215
+ resp = web.StreamResponse(
216
+ status=200,
217
+ headers={
218
+ "Content-Type": "text/event-stream",
219
+ "Cache-Control": "no-cache",
220
+ "Connection": "keep-alive",
221
+ },
222
+ )
223
+ await resp.prepare(request)
224
+ self._sse_clients.append(resp)
225
+ try:
226
+ # Keep the connection open; aiohttp closes it when the client
227
+ # disconnects, which surfaces here as a ConnectionResetError on
228
+ # the next write() from _broadcast (handled there, not here).
229
+ while True:
230
+ await asyncio.sleep(15)
231
+ try:
232
+ await resp.write(b": ping\n\n")
233
+ except Exception:
234
+ break
235
+ finally:
236
+ if resp in self._sse_clients:
237
+ self._sse_clients.remove(resp)
238
+ return resp
239
+
240
+ async def _handle_state(self, request):
241
+ from aiohttp import web
242
+
243
+ threads_payload = {
244
+ tid: {
245
+ "versions": [
246
+ {"index": v.index, "created_at": v.created_at, "kind": v.kind}
247
+ for v in thread.versions
248
+ ],
249
+ }
250
+ for tid, thread in self.threads.items()
251
+ }
252
+ return web.json_response({
253
+ "threads": threads_payload,
254
+ "active_thread_id": self.active_thread_id,
255
+ })
256
+
257
+ async def _handle_artifact(self, request):
258
+ from aiohttp import web
259
+
260
+ thread_id = request.match_info["thread_id"]
261
+ thread = self.threads.get(thread_id)
262
+ if thread is None or not thread.versions:
263
+ return web.Response(status=404, text=f"Unknown thread: {thread_id}")
264
+
265
+ v_param = request.query.get("v")
266
+ try:
267
+ idx = int(v_param) if v_param is not None else len(thread.versions) - 1
268
+ except ValueError:
269
+ idx = len(thread.versions) - 1
270
+ idx = max(0, min(idx, len(thread.versions) - 1))
271
+ version = thread.versions[idx]
272
+
273
+ if not version.path.exists():
274
+ return web.Response(status=404, text=f"Artifact file missing: {version.path}")
275
+ content_type = mimetypes.guess_type(str(version.path))[0] or "application/octet-stream"
276
+ return web.Response(
277
+ body=version.path.read_bytes(),
278
+ content_type=content_type,
279
+ headers=_ARTIFACT_HEADERS,
280
+ )
281
+
282
+
283
+ # Sent with every artifact response. The iframe sandbox already gives the
284
+ # document an opaque origin; this is the second half — it stops the page from
285
+ # reaching the network at all.
286
+ #
287
+ # `connect-src 'none'` is the important line: without it, model-generated
288
+ # JavaScript in an artifact can POST the contents of the page (which may hold
289
+ # whatever data the agent just analysed) to any host on the internet, and an
290
+ # opaque origin does nothing to prevent that. Google Fonts is the one exception
291
+ # because a dashboard that silently loses its typeface looks broken rather than
292
+ # secured; nothing else is reachable.
293
+ _ARTIFACT_HEADERS = {
294
+ "Content-Security-Policy": "; ".join((
295
+ "default-src 'none'",
296
+ "script-src 'unsafe-inline' 'unsafe-eval'",
297
+ "style-src 'unsafe-inline' https://fonts.googleapis.com",
298
+ "font-src https://fonts.gstatic.com data:",
299
+ "img-src data: blob:",
300
+ "media-src data: blob:",
301
+ "connect-src 'none'",
302
+ "form-action 'none'",
303
+ "base-uri 'none'",
304
+ "frame-ancestors 'self'",
305
+ )),
306
+ "X-Content-Type-Options": "nosniff",
307
+ "Referrer-Policy": "no-referrer",
308
+ # A generated report is a snapshot of one moment; a cached copy shown after
309
+ # the next version lands is worse than no preview.
310
+ "Cache-Control": "no-store",
311
+ }
312
+
313
+
314
+ def _kind_for_suffix(suffix: str) -> str:
315
+ suffix = suffix.lower()
316
+ if suffix in (".html", ".htm"):
317
+ return "html"
318
+ if suffix in (".png", ".jpg", ".jpeg", ".gif", ".webp", ".svg"):
319
+ return "image"
320
+ if suffix in (".md", ".markdown"):
321
+ return "markdown"
322
+ return "file"
323
+
324
+
325
+ async def start_session() -> "PreviewSession":
326
+ """Start (or return the already-running) preview session. Idempotent —
327
+ calling /canvas twice does not open a second server."""
328
+ global _active_session
329
+ if _active_session is not None and _active_session.url:
330
+ return _active_session
331
+ session = PreviewSession()
332
+ await session.start()
333
+ _active_session = session
334
+ _write_discovery(session.url)
335
+ return session
336
+
337
+
338
+ async def stop_session() -> None:
339
+ global _active_session
340
+ if _active_session is not None:
341
+ await _active_session.stop()
342
+ _active_session = None
343
+ _clear_discovery()
344
+
345
+
346
+ def open_in_browser(url: str) -> None:
347
+ try:
348
+ webbrowser.open(url)
349
+ except Exception as exc:
350
+ logger.warning("Could not auto-open browser for %s: %s", url, exc)
351
+
352
+
353
+ _SHELL_HTML = """<!doctype html>
354
+ <html>
355
+ <head>
356
+ <meta charset="utf-8">
357
+ <title>aria-code canvas</title>
358
+ <style>
359
+ :root { color-scheme: dark; }
360
+ * { box-sizing: border-box; }
361
+ body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
362
+ background: #0d1117; color: #c9d1d9; height: 100vh; display: flex; flex-direction: column; }
363
+ header { display: flex; align-items: center; gap: 12px; padding: 10px 16px;
364
+ border-bottom: 1px solid #21262d; background: #161b22; }
365
+ header .title { font-weight: 600; font-size: 14px; }
366
+ header .empty { color: #8b949e; font-size: 13px; }
367
+ .stepper { display: flex; align-items: center; gap: 6px; margin-left: auto; }
368
+ .stepper button { background: #21262d; color: #c9d1d9; border: 1px solid #30363d;
369
+ border-radius: 6px; padding: 4px 10px; cursor: pointer; font-size: 13px; }
370
+ .stepper button:disabled { opacity: 0.4; cursor: default; }
371
+ .stepper span { font-size: 12px; color: #8b949e; min-width: 48px; text-align: center; }
372
+ main { flex: 1; position: relative; }
373
+ iframe, img { width: 100%; height: 100%; border: none; display: block; object-fit: contain; background: #fff; }
374
+ .placeholder { display: flex; align-items: center; justify-content: center;
375
+ height: 100%; color: #8b949e; font-size: 14px; }
376
+ </style>
377
+ </head>
378
+ <body>
379
+ <header>
380
+ <span class="title">aria-code canvas</span>
381
+ <span class="empty" id="status">waiting for the first artifact&hellip;</span>
382
+ <div class="stepper" id="stepper" style="display:none">
383
+ <button id="prev">&lsaquo;</button>
384
+ <span id="pos"></span>
385
+ <button id="next">&rsaquo;</button>
386
+ </div>
387
+ </header>
388
+ <main id="main"><div class="placeholder">Run a report/chart command in aria-code — it will show up here automatically.</div></main>
389
+ <script>
390
+ let state = { threads: {}, active_thread_id: null };
391
+ let activeIndex = 0;
392
+
393
+ function render() {
394
+ const main = document.getElementById('main');
395
+ const status = document.getElementById('status');
396
+ const stepper = document.getElementById('stepper');
397
+ const thread = state.active_thread_id ? state.threads[state.active_thread_id] : null;
398
+ if (!thread || thread.versions.length === 0) {
399
+ main.innerHTML = '<div class="placeholder">Run a report/chart command in aria-code — it will show up here automatically.</div>';
400
+ status.textContent = 'waiting for the first artifact\\u2026';
401
+ stepper.style.display = 'none';
402
+ return;
403
+ }
404
+ status.textContent = state.active_thread_id;
405
+ stepper.style.display = 'flex';
406
+ activeIndex = Math.max(0, Math.min(activeIndex, thread.versions.length - 1));
407
+ document.getElementById('pos').textContent = (activeIndex + 1) + ' / ' + thread.versions.length;
408
+ document.getElementById('prev').disabled = activeIndex <= 0;
409
+ document.getElementById('next').disabled = activeIndex >= thread.versions.length - 1;
410
+ const version = thread.versions[activeIndex];
411
+ const url = '/artifact/' + encodeURIComponent(state.active_thread_id) + '?v=' + version.index + '&_=' + Date.now();
412
+ if (version.kind === 'image') {
413
+ main.innerHTML = '<img src="' + url + '">';
414
+ } else {
415
+ // allow-scripts WITHOUT allow-same-origin. The two together are not
416
+ // additive — they cancel the sandbox: the framed document would run in
417
+ // this page's origin and could read /state, open /events, and reach the
418
+ // parent DOM. Artifact HTML is model-generated, so it gets an opaque
419
+ // origin and nothing else.
420
+ main.innerHTML = '<iframe src="' + url + '" sandbox="allow-scripts"></iframe>';
421
+ }
422
+ }
423
+
424
+ async function refreshState(jumpToLatest) {
425
+ const resp = await fetch('/state');
426
+ state = await resp.json();
427
+ if (jumpToLatest && state.active_thread_id) {
428
+ const thread = state.threads[state.active_thread_id];
429
+ activeIndex = thread ? thread.versions.length - 1 : 0;
430
+ }
431
+ render();
432
+ }
433
+
434
+ document.getElementById('prev').onclick = () => { activeIndex--; render(); };
435
+ document.getElementById('next').onclick = () => { activeIndex++; render(); };
436
+
437
+ const es = new EventSource('/events');
438
+ es.onmessage = (event) => {
439
+ const msg = JSON.parse(event.data);
440
+ if (msg.type === 'new-version') {
441
+ refreshState(true);
442
+ }
443
+ };
444
+
445
+ refreshState(true);
446
+ </script>
447
+ </body>
448
+ </html>
449
+ """
@@ -0,0 +1,5 @@
1
+ """Privacy and feedback helpers for Aria Code."""
2
+
3
+ from .feedback import FeedbackRecord, FeedbackStore, PrivacySettings
4
+
5
+ __all__ = ["FeedbackRecord", "FeedbackStore", "PrivacySettings"]
@@ -0,0 +1,123 @@
1
+ """Local-first feedback storage and privacy settings."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import shutil
7
+ from dataclasses import asdict, dataclass
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+ from typing import Any, Dict, Iterable, List
11
+
12
+
13
+ @dataclass(frozen=True)
14
+ class PrivacySettings:
15
+ """User-controlled data sharing settings."""
16
+
17
+ data_sharing: bool = False
18
+ feedback_upload: bool = False
19
+
20
+ @classmethod
21
+ def from_config(cls, config: Dict[str, Any]) -> "PrivacySettings":
22
+ return cls(
23
+ data_sharing=bool(config.get("data_sharing", False)),
24
+ feedback_upload=bool(config.get("feedback_upload", False)),
25
+ )
26
+
27
+ def apply_to_config(self, config: Dict[str, Any]) -> Dict[str, Any]:
28
+ config["data_sharing"] = self.data_sharing
29
+ config["feedback_upload"] = self.feedback_upload
30
+ return config
31
+
32
+
33
+ @dataclass(frozen=True)
34
+ class FeedbackRecord:
35
+ """One local feedback event for a model response."""
36
+
37
+ rating: str
38
+ message: str
39
+ comment: str = ""
40
+ model: str = ""
41
+ session_id: str = ""
42
+ timestamp: str = ""
43
+ message_index: int | None = None
44
+ shared: bool = False
45
+
46
+ @classmethod
47
+ def create(
48
+ cls,
49
+ *,
50
+ rating: str,
51
+ message: str,
52
+ comment: str = "",
53
+ model: str = "",
54
+ session_id: str = "",
55
+ message_index: int | None = None,
56
+ shared: bool = False,
57
+ ) -> "FeedbackRecord":
58
+ return cls(
59
+ rating=rating,
60
+ message=message,
61
+ comment=comment,
62
+ model=model,
63
+ session_id=session_id,
64
+ timestamp=datetime.now().isoformat(),
65
+ message_index=message_index,
66
+ shared=shared,
67
+ )
68
+
69
+ def to_json(self) -> str:
70
+ return json.dumps(asdict(self), ensure_ascii=False)
71
+
72
+
73
+ class FeedbackStore:
74
+ """Append-only local feedback store with explicit export/delete actions."""
75
+
76
+ def __init__(self, config_dir: str | Path) -> None:
77
+ self.config_dir = Path(config_dir).expanduser()
78
+ self.feedback_dir = self.config_dir / "feedback"
79
+ self.feedback_file = self.feedback_dir / "feedback.jsonl"
80
+ self.export_dir = self.feedback_dir / "exports"
81
+
82
+ def append(self, record: FeedbackRecord) -> Path:
83
+ self.feedback_dir.mkdir(parents=True, exist_ok=True)
84
+ with self.feedback_file.open("a", encoding="utf-8") as handle:
85
+ handle.write(record.to_json() + "\n")
86
+ return self.feedback_file
87
+
88
+ def iter_records(self) -> Iterable[Dict[str, Any]]:
89
+ if not self.feedback_file.exists():
90
+ return []
91
+ rows: List[Dict[str, Any]] = []
92
+ with self.feedback_file.open("r", encoding="utf-8") as handle:
93
+ for line in handle:
94
+ line = line.strip()
95
+ if not line:
96
+ continue
97
+ try:
98
+ rows.append(json.loads(line))
99
+ except json.JSONDecodeError:
100
+ continue
101
+ return rows
102
+
103
+ def count(self) -> int:
104
+ return sum(1 for _ in self.iter_records())
105
+
106
+ def export_jsonl(self, destination: str | Path | None = None) -> Path:
107
+ self.export_dir.mkdir(parents=True, exist_ok=True)
108
+ if destination is None:
109
+ stamp = datetime.now().strftime("%Y%m%d_%H%M%S")
110
+ destination = self.export_dir / f"feedback_export_{stamp}.jsonl"
111
+ dest = Path(destination).expanduser()
112
+ dest.parent.mkdir(parents=True, exist_ok=True)
113
+ if self.feedback_file.exists():
114
+ shutil.copyfile(self.feedback_file, dest)
115
+ else:
116
+ dest.write_text("", encoding="utf-8")
117
+ return dest
118
+
119
+ def delete_all(self) -> int:
120
+ count = self.count()
121
+ if self.feedback_file.exists():
122
+ self.feedback_file.unlink()
123
+ return count