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,32 @@
1
+ """Sandbox execution module for Aria Code."""
2
+ import subprocess
3
+ import os
4
+
5
+ def run_in_docker_sandbox(command: str, cwd: str = None, timeout: int = 120, image: str = "python:3.11-slim") -> subprocess.CompletedProcess:
6
+ """Run a shell command securely inside an isolated Docker container.
7
+
8
+ This mounts the current workspace so scripts can operate on files,
9
+ but prevents access to the host's system files and network interfaces
10
+ if configured with --network none (optional).
11
+ """
12
+ workspace = cwd or os.getcwd()
13
+
14
+ # We use docker run with a volume mount to the workspace.
15
+ # --rm: remove container after exit
16
+ # -v: mount workspace to /workspace
17
+ # -w: set working directory to /workspace
18
+ docker_cmd = [
19
+ "docker", "run", "--rm",
20
+ "-v", f"{workspace}:/workspace",
21
+ "-w", "/workspace",
22
+ # "--network", "none", # Uncomment to completely isolate network
23
+ image,
24
+ "sh", "-c", command
25
+ ]
26
+
27
+ return subprocess.run(
28
+ docker_cmd,
29
+ capture_output=True,
30
+ text=True,
31
+ timeout=timeout
32
+ )
@@ -0,0 +1,120 @@
1
+ """Session export helpers for Aria Code."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ from datetime import datetime
7
+ from typing import Any, Optional, Sequence
8
+
9
+ from aria_code.artifacts import artifact_summary as build_artifact_summary
10
+ from aria_code.apps.cli.config_paths import config_snapshot
11
+ from aria_code.packages.aria_core import build_session_diagnostic_bundle
12
+
13
+
14
+ def _safe_title_from_conversation(conversation: Sequence[dict]) -> str:
15
+ for msg in conversation:
16
+ if msg.get("role") == "user":
17
+ return str(msg.get("content", ""))[:60]
18
+ return "Aria Code Session"
19
+
20
+
21
+ def build_session_export_payload(
22
+ fmt: str,
23
+ conversation: Sequence[dict],
24
+ *,
25
+ session_id: str = "",
26
+ config: Optional[dict] = None,
27
+ paths: Optional[dict] = None,
28
+ trace: Any = None,
29
+ provider_health: Optional[list] = None,
30
+ doctor_report: Any = None,
31
+ mcp_status: Optional[list] = None,
32
+ context_health: Optional[dict] = None,
33
+ ) -> tuple[str, str, str]:
34
+ """Return (content, extension, suggested_filename_prefix)."""
35
+ fmt = (fmt or "json").lower().strip()
36
+
37
+ if fmt == "json":
38
+ content = json.dumps(list(conversation), indent=2, ensure_ascii=False)
39
+ return content, "json", "aria_code_chat"
40
+
41
+ if fmt == "csv":
42
+ lines = ["role,content"]
43
+ for msg in conversation:
44
+ escaped = str(msg.get("content", "")).replace('"', '""').replace("\n", " ")
45
+ lines.append(f'{msg.get("role", "user")},"{escaped}"')
46
+ return "\n".join(lines), "csv", "aria_code_chat"
47
+
48
+ if fmt == "md":
49
+ lines = [f"# Aria Code Chat Export — {datetime.now().strftime('%Y-%m-%d %H:%M')}\n"]
50
+ for msg in conversation:
51
+ prefix = "**You:**" if msg.get("role") == "user" else "**Aria:**"
52
+ lines.append(f"{prefix}\n{msg.get('content', '')}\n")
53
+ return "\n".join(lines), "md", "aria_code_chat"
54
+
55
+ if fmt == "sft":
56
+ pairs = []
57
+ conv = list(conversation)
58
+ i = 0
59
+ while i < len(conv) - 1:
60
+ if conv[i].get("role") == "user" and conv[i + 1].get("role") == "assistant":
61
+ user_text = str(conv[i].get("content", "")).strip()
62
+ assistant_text = str(conv[i + 1].get("content", "")).strip()
63
+ if len(user_text) > 10 and len(assistant_text) > 20 and not user_text.startswith("Tool results:"):
64
+ pairs.append({
65
+ "instruction": user_text,
66
+ "input": "",
67
+ "output": assistant_text,
68
+ "source": "aria_cli_export",
69
+ "timestamp": datetime.now().strftime("%Y-%m-%d"),
70
+ })
71
+ i += 2
72
+ else:
73
+ i += 1
74
+ if not pairs:
75
+ raise ValueError("No user→assistant pairs to export")
76
+ return json.dumps(pairs, indent=2, ensure_ascii=False), "json", "aria_sft"
77
+
78
+ if fmt == "ipynb":
79
+ cells = [{
80
+ "cell_type": "markdown",
81
+ "metadata": {},
82
+ "source": [f"# Aria Code Chat Export — {datetime.now().strftime('%Y-%m-%d %H:%M')}\n\n", "*(This notebook contains the conversation history. Math formulas and LaTeX will render natively here.)*"]
83
+ }]
84
+ for msg in conversation:
85
+ if msg.get("role") not in ("user", "assistant"):
86
+ continue
87
+ prefix = "**You:**\n\n" if msg.get("role") == "user" else "**Aria:**\n\n"
88
+ cells.append({
89
+ "cell_type": "markdown",
90
+ "metadata": {},
91
+ "source": [prefix + str(msg.get("content", ""))]
92
+ })
93
+
94
+ notebook = {
95
+ "cells": cells,
96
+ "metadata": {
97
+ "kernelspec": {"display_name": "Python 3", "language": "python", "name": "python3"},
98
+ "language_info": {"name": "python"}
99
+ },
100
+ "nbformat": 4,
101
+ "nbformat_minor": 4
102
+ }
103
+ return json.dumps(notebook, indent=2, ensure_ascii=False), "ipynb", "aria_code_chat"
104
+
105
+ if fmt == "bundle":
106
+ bundle = build_session_diagnostic_bundle(
107
+ session_id=session_id,
108
+ conversation=conversation,
109
+ config=config,
110
+ paths=paths or config_snapshot(),
111
+ trace=trace,
112
+ provider_health=provider_health,
113
+ artifact_summary=build_artifact_summary(),
114
+ doctor_report=doctor_report,
115
+ mcp_status=mcp_status,
116
+ context_health=context_health,
117
+ )
118
+ return json.dumps(bundle, indent=2, ensure_ascii=False), "json", "aria_bundle"
119
+
120
+ raise ValueError("Unsupported export format")
@@ -0,0 +1,208 @@
1
+ """JSONL-backed session persistence for Aria Code.
2
+
3
+ Why JSONL instead of JSON?
4
+ • Append-per-turn — no need to rewrite the whole file on every message
5
+ • Crash-safe — partial writes leave previous turns intact
6
+ • Streamable — readers can tail -f a live session
7
+
8
+ File layout: ~/.arthera/sessions/<session_id>.jsonl
9
+ Each line is one JSON object:
10
+ {"type": "meta", "id": "...", "title": "...", "created_at": "..."}
11
+ {"type": "message", "role": "user", "content": "...", "ts": "..."}
12
+ {"type": "message", "role": "assistant", "content": "...", "ts": "..."}
13
+ {"type": "meta", "updated_at": "..."} ← appended on each save
14
+
15
+ Reading: scan all lines, reconstruct conversation from "message" entries.
16
+ Last "meta" wins for title / timestamps.
17
+ """
18
+ from __future__ import annotations
19
+
20
+ import json
21
+ from datetime import datetime, timezone
22
+ from pathlib import Path
23
+ from typing import Optional
24
+ from aria_code.packages.aria_core.paths import aria_home
25
+
26
+ _SESSIONS_DIR = aria_home() / "sessions"
27
+
28
+
29
+ def _now() -> str:
30
+ return datetime.now(timezone.utc).isoformat()
31
+
32
+
33
+ def _session_path(session_id: str) -> Path:
34
+ return _SESSIONS_DIR / f"{session_id}.jsonl"
35
+
36
+
37
+ class JsonlSessionStore:
38
+ """Read/write JSONL session files."""
39
+
40
+ def __init__(self, sessions_dir: Optional[Path] = None) -> None:
41
+ self.root = sessions_dir or _SESSIONS_DIR
42
+ self.root.mkdir(parents=True, exist_ok=True)
43
+
44
+ def _path(self, session_id: str) -> Path:
45
+ return self.root / f"{session_id}.jsonl"
46
+
47
+ # ── Write ─────────────────────────────────────────────────────────────────
48
+
49
+ def init_session(self, session_id: str, title: str = "") -> None:
50
+ """Write the opening meta line. Call once at session start."""
51
+ p = self._path(session_id)
52
+ if p.exists():
53
+ return
54
+ line = json.dumps({
55
+ "type": "meta",
56
+ "id": session_id,
57
+ "title": title or "",
58
+ "created_at": _now(),
59
+ }, ensure_ascii=False)
60
+ p.write_text(line + "\n", encoding="utf-8")
61
+
62
+ def append_message(self, session_id: str, role: str, content: str) -> None:
63
+ """Append one message turn. Thread-safe for single-process use."""
64
+ p = self._path(session_id)
65
+ line = json.dumps({
66
+ "type": "message",
67
+ "role": role,
68
+ "content": content,
69
+ "ts": _now(),
70
+ }, ensure_ascii=False)
71
+ with p.open("a", encoding="utf-8") as f:
72
+ f.write(line + "\n")
73
+
74
+ def flush_meta(self, session_id: str, title: str = "", extra: Optional[dict] = None) -> None:
75
+ """Append an updated meta line (title, timestamps)."""
76
+ p = self._path(session_id)
77
+ meta: dict = {"type": "meta", "updated_at": _now()}
78
+ if title:
79
+ meta["title"] = title
80
+ if extra:
81
+ meta.update(extra)
82
+ with p.open("a", encoding="utf-8") as f:
83
+ f.write(json.dumps(meta, ensure_ascii=False) + "\n")
84
+
85
+ def save_conversation(
86
+ self,
87
+ session_id: str,
88
+ conversation: list[dict],
89
+ title: str = "",
90
+ ) -> None:
91
+ """Full rewrite — used when bulk-importing a JSON session into JSONL."""
92
+ p = self._path(session_id)
93
+ lines = []
94
+ lines.append(json.dumps({
95
+ "type": "meta",
96
+ "id": session_id,
97
+ "title": title or "",
98
+ "created_at": _now(),
99
+ "updated_at": _now(),
100
+ }, ensure_ascii=False))
101
+ for msg in conversation:
102
+ lines.append(json.dumps({
103
+ "type": "message",
104
+ "role": msg.get("role", "user"),
105
+ "content": str(msg.get("content", "")),
106
+ "ts": _now(),
107
+ }, ensure_ascii=False))
108
+ p.write_text("\n".join(lines) + "\n", encoding="utf-8")
109
+
110
+ # ── Read ──────────────────────────────────────────────────────────────────
111
+
112
+ def load_session(self, session_id: str) -> Optional[dict]:
113
+ """Load a session; returns None if not found."""
114
+ p = self._path(session_id)
115
+ if not p.exists():
116
+ return None
117
+
118
+ messages: list[dict] = []
119
+ meta: dict = {"id": session_id}
120
+
121
+ for raw in p.read_text(encoding="utf-8").splitlines():
122
+ raw = raw.strip()
123
+ if not raw:
124
+ continue
125
+ try:
126
+ obj = json.loads(raw)
127
+ except json.JSONDecodeError:
128
+ continue
129
+
130
+ t = obj.get("type")
131
+ if t == "meta":
132
+ meta.update({k: v for k, v in obj.items() if k != "type"})
133
+ elif t == "message":
134
+ messages.append({
135
+ "role": obj.get("role", "user"),
136
+ "content": obj.get("content", ""),
137
+ })
138
+
139
+ return {
140
+ "id": session_id,
141
+ "messages": messages,
142
+ "metadata": {
143
+ "title": meta.get("title", "Untitled"),
144
+ "created_at": meta.get("created_at", ""),
145
+ "updated_at": meta.get("updated_at", ""),
146
+ },
147
+ }
148
+
149
+ def list_sessions(self, limit: int = 20) -> list[dict]:
150
+ """Return recent sessions sorted by mtime, newest first."""
151
+ sessions = []
152
+ for p in sorted(
153
+ self.root.glob("*.jsonl"),
154
+ key=lambda x: x.stat().st_mtime,
155
+ reverse=True,
156
+ ):
157
+ session_id = p.stem
158
+ meta: dict = {}
159
+ msg_count = 0
160
+ try:
161
+ for raw in p.read_text(encoding="utf-8").splitlines():
162
+ raw = raw.strip()
163
+ if not raw:
164
+ continue
165
+ obj = json.loads(raw)
166
+ if obj.get("type") == "meta":
167
+ meta.update(obj)
168
+ elif obj.get("type") == "message":
169
+ msg_count += 1
170
+ except Exception:
171
+ continue
172
+
173
+ sessions.append({
174
+ "id": session_id,
175
+ "title": meta.get("title", "Untitled"),
176
+ "messages": msg_count,
177
+ "updated": meta.get("updated_at", ""),
178
+ "created": meta.get("created_at", ""),
179
+ })
180
+ if len(sessions) >= limit:
181
+ break
182
+
183
+ return sessions
184
+
185
+ def delete_session(self, session_id: str) -> bool:
186
+ p = self._path(session_id)
187
+ if p.exists():
188
+ p.unlink()
189
+ return True
190
+ return False
191
+
192
+ def search_sessions(self, keyword: str, limit: int = 10) -> list[dict]:
193
+ """Full-text search across all session JSONL files."""
194
+ kw = keyword.lower()
195
+ matches = []
196
+ for p in self.root.glob("*.jsonl"):
197
+ try:
198
+ text = p.read_text(encoding="utf-8")
199
+ if kw not in text.lower():
200
+ continue
201
+ result = self.load_session(p.stem)
202
+ if result:
203
+ matches.append(result)
204
+ except Exception:
205
+ continue
206
+ if len(matches) >= limit:
207
+ break
208
+ return matches
@@ -0,0 +1,123 @@
1
+ """Keep a Firebase sign-in alive the way Claude Code and Codex keep theirs.
2
+
3
+ A Firebase ID token lasts an hour. /login stores it with a refresh token, but
4
+ nothing used the refresh token, so an hour after signing in every gateway call
5
+ failed and the user was told to /login again. Claude Code and Codex do not ask:
6
+ they exchange the refresh token for a new access token shortly before the old
7
+ one expires and carry on.
8
+
9
+ This does the same against Firebase's Secure Token API, which accepts the
10
+ project's public web API key (the one the web and desktop clients ship; see
11
+ google_login.FIREBASE_WEB_API_KEY) and the user's refresh token. The result is
12
+ written back to config.json with owner-only permissions, so every process that
13
+ reads the session, the gateway provider and the Commerce plugin among them,
14
+ sees the fresh token.
15
+
16
+ Failure is deliberately quiet: if the refresh cannot happen (offline, token
17
+ revoked) the stored token is returned unchanged, the request fails with the
18
+ gateway's own 401, and /login remains the way back in.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import json
24
+ import urllib.parse
25
+ import urllib.request
26
+ from datetime import datetime, timedelta, timezone
27
+ from pathlib import Path
28
+ from typing import Any, Callable, Dict, Optional
29
+
30
+ SECURE_TOKEN_URL = "https://securetoken.googleapis.com/v1/token"
31
+
32
+ #: Refresh when the token has this long or less left, so a request that starts
33
+ #: just before expiry is not sent with a token that dies in flight.
34
+ REFRESH_WITHIN_SECONDS = 300
35
+
36
+
37
+ def _parse_expiry(value: Any) -> Optional[datetime]:
38
+ if not value:
39
+ return None
40
+ try:
41
+ return datetime.fromisoformat(str(value).replace("Z", "+00:00"))
42
+ except ValueError:
43
+ return None
44
+
45
+
46
+ def needs_refresh(config: Dict[str, Any], now: Optional[datetime] = None) -> bool:
47
+ """True when the stored token is expired or about to be, and can be renewed."""
48
+ if not config.get("auth_token") or not config.get("refresh_token"):
49
+ return False
50
+ expires = _parse_expiry(config.get("token_expires_at"))
51
+ if expires is None:
52
+ # A login from before expiry was recorded: nothing to compare against,
53
+ # so leave it alone rather than refresh on every call.
54
+ return False
55
+ now = now or datetime.now(timezone.utc)
56
+ return expires - now <= timedelta(seconds=REFRESH_WITHIN_SECONDS)
57
+
58
+
59
+ def exchange_refresh_token(refresh_token: str, api_key: str, *, timeout: int = 15,
60
+ opener: Callable = urllib.request.urlopen) -> Dict[str, Any]:
61
+ """Trade a refresh token for a new ID token. Raises on any failure."""
62
+ body = urllib.parse.urlencode({"grant_type": "refresh_token", "refresh_token": refresh_token}).encode()
63
+ request = urllib.request.Request(
64
+ f"{SECURE_TOKEN_URL}?key={urllib.parse.quote(api_key)}", data=body,
65
+ headers={"Content-Type": "application/x-www-form-urlencoded"}, method="POST",
66
+ )
67
+ with opener(request, timeout=timeout) as response:
68
+ return json.loads(response.read().decode("utf-8"))
69
+
70
+
71
+ def refresh_config_file(path: Path, *, now: Optional[datetime] = None,
72
+ exchange: Optional[Callable[[str, str], Dict[str, Any]]] = None) -> Optional[str]:
73
+ """Renew the session stored in `path` if it is due; return the token to use.
74
+
75
+ Returns the current (possibly just refreshed) ID token, or None when there
76
+ is no session. Never raises: a failed refresh returns the stored token.
77
+ """
78
+ try:
79
+ config = json.loads(Path(path).read_text(encoding="utf-8"))
80
+ except (OSError, ValueError):
81
+ return None
82
+ token = config.get("auth_token")
83
+ token = str(token) if token else None
84
+ if not token or not needs_refresh(config, now):
85
+ return token
86
+
87
+ from .google_login import FIREBASE_WEB_API_KEY
88
+
89
+ try:
90
+ result = (exchange or exchange_refresh_token)(str(config["refresh_token"]), FIREBASE_WEB_API_KEY)
91
+ new_token = result.get("id_token") or result.get("idToken")
92
+ if not new_token:
93
+ return token
94
+ # Do not let a slow refresh for account A replace a later login to B.
95
+ target = Path(path)
96
+ current = json.loads(target.read_text(encoding="utf-8"))
97
+ keys = ("user_id", "auth_token", "refresh_token")
98
+ if any(current.get(key) != config.get(key) for key in keys):
99
+ return None
100
+ if config.get("user_id") and result.get("user_id") and result["user_id"] != config["user_id"]:
101
+ return None
102
+ config["auth_token"] = new_token
103
+ if result.get("refresh_token") or result.get("refreshToken"):
104
+ config["refresh_token"] = result.get("refresh_token") or result.get("refreshToken")
105
+ seconds = int(result.get("expires_in") or result.get("expiresIn") or 3600)
106
+ stamp = (now or datetime.now(timezone.utc)) + timedelta(seconds=seconds)
107
+ config["token_expires_at"] = stamp.isoformat().replace("+00:00", "Z")
108
+ from aria_code.packages.aria_core.secure_file import write_secret_json
109
+
110
+ # Replace the file in one step: this is the whole config, and a crash
111
+ # halfway through an in-place write would lose every setting in it.
112
+ target = Path(path)
113
+ scratch = target.with_name(target.name + ".refresh")
114
+ write_secret_json(scratch, config)
115
+ import os
116
+
117
+ os.replace(scratch, target)
118
+ return str(new_token)
119
+ except Exception:
120
+ return token
121
+
122
+
123
+ __all__ = ["needs_refresh", "refresh_config_file", "exchange_refresh_token", "REFRESH_WITHIN_SECONDS"]
@@ -0,0 +1,125 @@
1
+ """JSON-backed session persistence for Aria Code."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import json
7
+ import tempfile
8
+ from datetime import datetime
9
+ from pathlib import Path
10
+ from typing import Optional
11
+ from aria_code.apps.cli.config_paths import resolve_config_dir
12
+
13
+
14
+ class SessionManager:
15
+ """Manage chat sessions with local file persistence."""
16
+
17
+ def __init__(self, sessions_dir: Optional[Path] = None):
18
+ self.root = sessions_dir or (resolve_config_dir() / "sessions")
19
+ self.root.mkdir(parents=True, exist_ok=True)
20
+
21
+ def _path(self, session_id: str) -> Path:
22
+ return self.root / f"{session_id}.json"
23
+
24
+ def save_session(self, session_id: str, conversation: list, metadata: dict = None):
25
+ meta = dict(metadata or {})
26
+ if not meta.get("created_at"):
27
+ meta["created_at"] = datetime.now().isoformat()
28
+ for msg in conversation:
29
+ if msg.get("role") == "user":
30
+ meta.setdefault("title", str(msg.get("content", ""))[:60])
31
+ break
32
+ data = {
33
+ "id": session_id,
34
+ "messages": conversation,
35
+ "metadata": meta,
36
+ "updated_at": datetime.now().isoformat(),
37
+ }
38
+ path = self._path(session_id)
39
+ # A killed terminal must leave either the prior session or the new
40
+ # one, never a half-written JSON file that cannot be resumed.
41
+ temporary = None
42
+ try:
43
+ with tempfile.NamedTemporaryFile(mode="w", encoding="utf-8", dir=self.root,
44
+ prefix=".session-", suffix=".tmp", delete=False) as f:
45
+ temporary = f.name
46
+ json.dump(data, f, indent=2, ensure_ascii=False)
47
+ f.flush()
48
+ os.fsync(f.fileno())
49
+ os.replace(temporary, path)
50
+ finally:
51
+ if temporary and os.path.exists(temporary):
52
+ os.unlink(temporary)
53
+
54
+ def load_session(self, session_id: str) -> Optional[dict]:
55
+ path = self._path(session_id)
56
+ if path.exists():
57
+ with open(path, encoding="utf-8") as f:
58
+ return json.load(f)
59
+ return None
60
+
61
+ def list_sessions(self, limit: int = 20) -> list:
62
+ sessions = []
63
+ for path in sorted(self.root.glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True):
64
+ try:
65
+ with open(path, encoding="utf-8") as f:
66
+ data = json.load(f)
67
+ sessions.append({
68
+ "id": data.get("id", path.stem),
69
+ "title": data.get("metadata", {}).get("title", "Untitled"),
70
+ "messages": len(data.get("messages", [])),
71
+ "updated": data.get("updated_at", ""),
72
+ })
73
+ except Exception:
74
+ continue
75
+ if len(sessions) >= limit:
76
+ break
77
+ return sessions
78
+
79
+ def search_sessions(self, query: str, limit: int = 20) -> list:
80
+ """Full-text search through session message content."""
81
+ q = query.lower()
82
+ results = []
83
+ for path in sorted(self.root.glob("*.json"), key=lambda p: p.stat().st_mtime, reverse=True):
84
+ try:
85
+ with open(path, encoding="utf-8") as f:
86
+ data = json.load(f)
87
+ messages = data.get("messages", [])
88
+ hits = []
89
+ for msg in messages:
90
+ content = msg.get("content", "")
91
+ if isinstance(content, str):
92
+ if q in content.lower():
93
+ idx = content.lower().index(q)
94
+ start = max(0, idx - 20)
95
+ end = min(len(content), idx + len(q) + 80)
96
+ hits.append(content[start:end])
97
+ elif isinstance(content, list):
98
+ for block in content:
99
+ if isinstance(block, dict):
100
+ text = block.get("text", "")
101
+ if text and q in text.lower():
102
+ idx = text.lower().index(q)
103
+ start = max(0, idx - 20)
104
+ end = min(len(text), idx + len(q) + 80)
105
+ hits.append(text[start:end])
106
+ if hits:
107
+ results.append({
108
+ "id": data.get("id", path.stem),
109
+ "title": data.get("metadata", {}).get("title", "Untitled"),
110
+ "updated": data.get("updated_at", ""),
111
+ "match_count": len(hits),
112
+ "preview": hits[0],
113
+ })
114
+ except Exception:
115
+ continue
116
+ if len(results) >= limit:
117
+ break
118
+ return sorted(results, key=lambda r: r["match_count"], reverse=True)
119
+
120
+ def delete_session(self, session_id: str) -> bool:
121
+ path = self._path(session_id)
122
+ if path.exists():
123
+ path.unlink()
124
+ return True
125
+ return False