aria-code 0.52.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (478) hide show
  1. aria_code/__init__.py +42 -0
  2. aria_code/_version.py +20 -0
  3. aria_code/adk_apps/__init__.py +1 -0
  4. aria_code/adk_apps/aria_code_review/__init__.py +1 -0
  5. aria_code/adk_apps/aria_code_review/agent.py +32 -0
  6. aria_code/adk_apps/aria_research/__init__.py +1 -0
  7. aria_code/adk_apps/aria_research/agent.py +31 -0
  8. aria_code/agents/__init__.py +34 -0
  9. aria_code/agents/base.py +318 -0
  10. aria_code/agents/code_review.py +200 -0
  11. aria_code/agents/deep/__init__.py +37 -0
  12. aria_code/agents/deep/calibration_loop.py +144 -0
  13. aria_code/agents/deep/critic.py +125 -0
  14. aria_code/agents/deep/deepen.py +193 -0
  15. aria_code/agents/deep/models.py +149 -0
  16. aria_code/agents/deep/pipeline.py +164 -0
  17. aria_code/agents/deep/quant_fusion.py +192 -0
  18. aria_code/agents/deep/themes.py +95 -0
  19. aria_code/agents/deep/tiers.py +106 -0
  20. aria_code/agents/engineering/__init__.py +14 -0
  21. aria_code/agents/engineering/coder.py +222 -0
  22. aria_code/agents/engineering/tester.py +145 -0
  23. aria_code/agents/financial/__init__.py +21 -0
  24. aria_code/agents/financial/cashflow_burn.py +119 -0
  25. aria_code/agents/financial/catalyst.py +279 -0
  26. aria_code/agents/financial/corporate_finance.py +175 -0
  27. aria_code/agents/financial/debate.py +224 -0
  28. aria_code/agents/financial/earnings.py +303 -0
  29. aria_code/agents/financial/fundamental.py +191 -0
  30. aria_code/agents/financial/macro.py +99 -0
  31. aria_code/agents/financial/news.py +207 -0
  32. aria_code/agents/financial/risk.py +165 -0
  33. aria_code/agents/financial/sector.py +279 -0
  34. aria_code/agents/financial/strategist.py +121 -0
  35. aria_code/agents/financial/stripe_revenue.py +127 -0
  36. aria_code/agents/financial/synthesis.py +274 -0
  37. aria_code/agents/financial/technical.py +258 -0
  38. aria_code/agents/orchestrator.py +44 -0
  39. aria_code/agents/portfolio_agent.py +363 -0
  40. aria_code/agents/realty/__init__.py +62 -0
  41. aria_code/agents/realty/asset_diagnosis.py +152 -0
  42. aria_code/agents/realty/business_match.py +166 -0
  43. aria_code/agents/realty/cashflow_verify.py +209 -0
  44. aria_code/agents/realty/contract_rules.py +210 -0
  45. aria_code/agents/realty/energy_anomaly.py +189 -0
  46. aria_code/agents/realty/exit_settlement.py +208 -0
  47. aria_code/agents/realty/fulfillment_risk.py +206 -0
  48. aria_code/agents/realty/ops_optimize.py +160 -0
  49. aria_code/agents/realty/revenue_share.py +215 -0
  50. aria_code/agents/registry.py +158 -0
  51. aria_code/agents/signal_scheme.py +111 -0
  52. aria_code/agents/sports/__init__.py +0 -0
  53. aria_code/agents/sports/football_agent.py +197 -0
  54. aria_code/agents/supervisor.py +113 -0
  55. aria_code/agents/team.py +492 -0
  56. aria_code/agents/warehouse/__init__.py +25 -0
  57. aria_code/agents/warehouse/contracts.py +26 -0
  58. aria_code/agents/warehouse/dashboard.py +155 -0
  59. aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
  60. aria_code/agents/warehouse/inbound_exceptions.py +25 -0
  61. aria_code/agents/warehouse/inventory_health.py +24 -0
  62. aria_code/agents/warehouse/local_run.py +71 -0
  63. aria_code/agents/warehouse/local_snapshot.py +132 -0
  64. aria_code/agents/warehouse/logistics_cost.py +84 -0
  65. aria_code/agents/warehouse/logistics_sync.py +21 -0
  66. aria_code/agents/warehouse/workflow.py +31 -0
  67. aria_code/aliyun_data_client.py +4 -0
  68. aria_code/apps/README.md +12 -0
  69. aria_code/apps/__init__.py +2 -0
  70. aria_code/apps/channels/README.md +15 -0
  71. aria_code/apps/channels/__init__.py +17 -0
  72. aria_code/apps/channels/intake.py +92 -0
  73. aria_code/apps/channels/registry.py +95 -0
  74. aria_code/apps/channels/tradingview.py +94 -0
  75. aria_code/apps/cli/README.md +13 -0
  76. aria_code/apps/cli/__init__.py +2 -0
  77. aria_code/apps/cli/_optional.py +120 -0
  78. aria_code/apps/cli/bootstrap.py +145 -0
  79. aria_code/apps/cli/broker_render.py +206 -0
  80. aria_code/apps/cli/codegen_paths.py +29 -0
  81. aria_code/apps/cli/commands/__init__.py +16 -0
  82. aria_code/apps/cli/commands/_business_render.py +78 -0
  83. aria_code/apps/cli/commands/_ui.py +71 -0
  84. aria_code/apps/cli/commands/analysis_cmds.py +369 -0
  85. aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
  86. aria_code/apps/cli/commands/auth_cmds.py +239 -0
  87. aria_code/apps/cli/commands/backtest_cmds.py +2543 -0
  88. aria_code/apps/cli/commands/broker_cmds.py +1234 -0
  89. aria_code/apps/cli/commands/business_workflow_cmds.py +319 -0
  90. aria_code/apps/cli/commands/canvas_cmds.py +77 -0
  91. aria_code/apps/cli/commands/catalog.py +87 -0
  92. aria_code/apps/cli/commands/core_cmds.py +2520 -0
  93. aria_code/apps/cli/commands/data_cmds.py +457 -0
  94. aria_code/apps/cli/commands/diagnostic_cmds.py +230 -0
  95. aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
  96. aria_code/apps/cli/commands/file_cmds.py +312 -0
  97. aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
  98. aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
  99. aria_code/apps/cli/commands/market.py +434 -0
  100. aria_code/apps/cli/commands/market_cmds.py +1381 -0
  101. aria_code/apps/cli/commands/market_context.py +425 -0
  102. aria_code/apps/cli/commands/market_render.py +7 -0
  103. aria_code/apps/cli/commands/model_cmds.py +2153 -0
  104. aria_code/apps/cli/commands/ops_cmds.py +721 -0
  105. aria_code/apps/cli/commands/orchestrator_cmds.py +179 -0
  106. aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
  107. aria_code/apps/cli/commands/portfolio_cmds.py +1278 -0
  108. aria_code/apps/cli/commands/report.py +437 -0
  109. aria_code/apps/cli/commands/scaffold_templates.py +617 -0
  110. aria_code/apps/cli/commands/session_cmds.py +236 -0
  111. aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
  112. aria_code/apps/cli/commands/team.py +669 -0
  113. aria_code/apps/cli/commands/team_render.py +8 -0
  114. aria_code/apps/cli/commands/ui_cmds.py +401 -0
  115. aria_code/apps/cli/commands/warehouse_cmds.py +79 -0
  116. aria_code/apps/cli/commands/workflow_cmds.py +480 -0
  117. aria_code/apps/cli/commands/workspace_cmds.py +1494 -0
  118. aria_code/apps/cli/config_paths.py +73 -0
  119. aria_code/apps/cli/config_store.py +78 -0
  120. aria_code/apps/cli/constants.py +88 -0
  121. aria_code/apps/cli/context.py +34 -0
  122. aria_code/apps/cli/deterministic.py +191 -0
  123. aria_code/apps/cli/direct.py +48 -0
  124. aria_code/apps/cli/finance_service_catalog.py +111 -0
  125. aria_code/apps/cli/football_reports.py +231 -0
  126. aria_code/apps/cli/github_app_auth.py +135 -0
  127. aria_code/apps/cli/google_login.py +196 -0
  128. aria_code/apps/cli/handlers/__init__.py +11 -0
  129. aria_code/apps/cli/handlers/broker_handlers.py +122 -0
  130. aria_code/apps/cli/handlers/chart_handlers.py +1309 -0
  131. aria_code/apps/cli/handlers/market_handlers.py +2513 -0
  132. aria_code/apps/cli/handlers/realty_handlers.py +114 -0
  133. aria_code/apps/cli/handlers/sports_handlers.py +22 -0
  134. aria_code/apps/cli/handlers/strategy_advice.py +82 -0
  135. aria_code/apps/cli/helpers.py +302 -0
  136. aria_code/apps/cli/hooks.py +180 -0
  137. aria_code/apps/cli/i18n.py +297 -0
  138. aria_code/apps/cli/intent.py +136 -0
  139. aria_code/apps/cli/intent_router.py +276 -0
  140. aria_code/apps/cli/intent_signals.py +344 -0
  141. aria_code/apps/cli/lifecycle_hooks.py +48 -0
  142. aria_code/apps/cli/local_tool_schemas.py +536 -0
  143. aria_code/apps/cli/main.py +29 -0
  144. aria_code/apps/cli/market_metadata.py +135 -0
  145. aria_code/apps/cli/market_universe.py +487 -0
  146. aria_code/apps/cli/message_processing.py +310 -0
  147. aria_code/apps/cli/model_catalog.py +620 -0
  148. aria_code/apps/cli/pdf_report.py +977 -0
  149. aria_code/apps/cli/plan_mode.py +139 -0
  150. aria_code/apps/cli/plotly_html.py +15 -0
  151. aria_code/apps/cli/prediction_feedback.py +202 -0
  152. aria_code/apps/cli/preflight.py +502 -0
  153. aria_code/apps/cli/project_aria.py +60 -0
  154. aria_code/apps/cli/prompt_assembly.py +59 -0
  155. aria_code/apps/cli/prompts/__init__.py +0 -0
  156. aria_code/apps/cli/prompts/coding.py +53 -0
  157. aria_code/apps/cli/prompts/select.py +142 -0
  158. aria_code/apps/cli/prompts/system_prompts.py +552 -0
  159. aria_code/apps/cli/prompts/ui.py +436 -0
  160. aria_code/apps/cli/provider_endpoints.py +237 -0
  161. aria_code/apps/cli/provider_keys.py +73 -0
  162. aria_code/apps/cli/providers/__init__.py +1 -0
  163. aria_code/apps/cli/providers/base.py +521 -0
  164. aria_code/apps/cli/providers/chat_routing.py +128 -0
  165. aria_code/apps/cli/providers/collaboration.py +98 -0
  166. aria_code/apps/cli/providers/llm/__init__.py +1 -0
  167. aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
  168. aria_code/apps/cli/providers/llm/ollama_stream.py +1594 -0
  169. aria_code/apps/cli/providers/llm/response_cache.py +60 -0
  170. aria_code/apps/cli/providers/llm/sse_stream.py +257 -0
  171. aria_code/apps/cli/providers/runtime_bridge.py +353 -0
  172. aria_code/apps/cli/providers/vertexai_stream.py +316 -0
  173. aria_code/apps/cli/runtime_consumer.py +657 -0
  174. aria_code/apps/cli/sandbox.py +32 -0
  175. aria_code/apps/cli/session_export.py +120 -0
  176. aria_code/apps/cli/session_jsonl.py +208 -0
  177. aria_code/apps/cli/session_store.py +112 -0
  178. aria_code/apps/cli/skills_catalog.py +273 -0
  179. aria_code/apps/cli/todo_tracker.py +190 -0
  180. aria_code/apps/cli/tool_executor.py +823 -0
  181. aria_code/apps/cli/tool_registry.py +34 -0
  182. aria_code/apps/cli/tool_scope.py +106 -0
  183. aria_code/apps/cli/tools/__init__.py +40 -0
  184. aria_code/apps/cli/tools/context.py +46 -0
  185. aria_code/apps/cli/tools/file_tools.py +180 -0
  186. aria_code/apps/cli/tools/market_tools.py +620 -0
  187. aria_code/apps/cli/tools/notebook_tools.py +111 -0
  188. aria_code/apps/cli/tools/system_tools.py +726 -0
  189. aria_code/apps/cli/tools/write_tools.py +845 -0
  190. aria_code/apps/cli/tradingview_bridge.py +529 -0
  191. aria_code/apps/cli/turn_planning.py +137 -0
  192. aria_code/apps/cli/update_check.py +153 -0
  193. aria_code/apps/cli/utils/__init__.py +0 -0
  194. aria_code/apps/cli/utils/market_detect.py +1597 -0
  195. aria_code/apps/daemon/README.md +14 -0
  196. aria_code/apps/vscode/README.md +115 -0
  197. aria_code/apps/vscode/package.json +70 -0
  198. aria_code/aria_cli.py +6743 -0
  199. aria_code/aria_daemon.py +1398 -0
  200. aria_code/aria_feishu_bot.py +1423 -0
  201. aria_code/aria_mcp_server.py +7 -0
  202. aria_code/aria_relay_client.py +4 -0
  203. aria_code/aria_relay_server.py +490 -0
  204. aria_code/aria_telegram_bot.py +202 -0
  205. aria_code/ariarc.py +385 -0
  206. aria_code/artifacts.py +581 -0
  207. aria_code/backtest_engine.py +4 -0
  208. aria_code/backtest_report.py +4 -0
  209. aria_code/brokers/__init__.py +78 -0
  210. aria_code/brokers/automation.py +193 -0
  211. aria_code/brokers/base.py +231 -0
  212. aria_code/brokers/capabilities.py +264 -0
  213. aria_code/brokers/cn/__init__.py +10 -0
  214. aria_code/brokers/cn/easytrader_broker.py +193 -0
  215. aria_code/brokers/cn/futu_broker.py +194 -0
  216. aria_code/brokers/cn/longbridge_broker.py +190 -0
  217. aria_code/brokers/cn/tiger_broker.py +196 -0
  218. aria_code/brokers/cn/xtquant_broker.py +225 -0
  219. aria_code/brokers/config.py +414 -0
  220. aria_code/brokers/intl/__init__.py +5 -0
  221. aria_code/brokers/intl/alpaca_broker.py +183 -0
  222. aria_code/brokers/intl/ibkr_broker.py +215 -0
  223. aria_code/brokers/intl/trading212_broker.py +183 -0
  224. aria_code/brokers/intl/webull_broker.py +156 -0
  225. aria_code/brokers/paper_broker.py +266 -0
  226. aria_code/brokers/planning.py +296 -0
  227. aria_code/brokers/registry.py +213 -0
  228. aria_code/brokers/trading.py +267 -0
  229. aria_code/canva_client.py +4 -0
  230. aria_code/change_store.py +127 -0
  231. aria_code/clients/__init__.py +0 -0
  232. aria_code/clients/aliyun_data_client.py +660 -0
  233. aria_code/clients/aria_relay_client.py +182 -0
  234. aria_code/clients/canva_client.py +284 -0
  235. aria_code/clients/figma_client.py +100 -0
  236. aria_code/clients/football_data_client.py +1671 -0
  237. aria_code/clients/kling_video_client.py +182 -0
  238. aria_code/clients/market_data_client.py +1976 -0
  239. aria_code/clients/openai_image_client.py +242 -0
  240. aria_code/clients/runway_video_client.py +157 -0
  241. aria_code/clients/warehouse_erp_client.py +105 -0
  242. aria_code/cloud_memory.py +181 -0
  243. aria_code/command_safety.py +19 -0
  244. aria_code/computer_use_tools.py +4 -0
  245. aria_code/dashboard_generator.py +579 -0
  246. aria_code/data_analysis_tools.py +4 -0
  247. aria_code/data_cleaner.py +483 -0
  248. aria_code/data_service.py +513 -0
  249. aria_code/datasources/__init__.py +23 -0
  250. aria_code/datasources/base.py +166 -0
  251. aria_code/datasources/router.py +253 -0
  252. aria_code/datasources/sources/__init__.py +15 -0
  253. aria_code/datasources/sources/akshare_source.py +269 -0
  254. aria_code/datasources/sources/alpha_vantage_source.py +203 -0
  255. aria_code/datasources/sources/edgar_source.py +219 -0
  256. aria_code/datasources/sources/finnhub_source.py +198 -0
  257. aria_code/datasources/sources/fred_source.py +223 -0
  258. aria_code/datasources/sources/tushare_source.py +143 -0
  259. aria_code/datasources/sources/web_scraper_source.py +278 -0
  260. aria_code/datasources/sources/world_bank_source.py +206 -0
  261. aria_code/datasources/sources/yfinance_source.py +152 -0
  262. aria_code/demo_player.py +207 -0
  263. aria_code/doctor.py +813 -0
  264. aria_code/domain/__init__.py +0 -0
  265. aria_code/domain/backtest_engine.py +354 -0
  266. aria_code/domain/backtest_report.py +472 -0
  267. aria_code/domain/finance_formulas.py +389 -0
  268. aria_code/domain/portfolio_ledger.py +307 -0
  269. aria_code/domain/strategy_vault.py +421 -0
  270. aria_code/evals/__init__.py +37 -0
  271. aria_code/evals/harness.py +598 -0
  272. aria_code/evals/runner.py +238 -0
  273. aria_code/external_agent_runner.py +115 -0
  274. aria_code/figma_client.py +4 -0
  275. aria_code/file_analysis_tools.py +4 -0
  276. aria_code/finance_formulas.py +4 -0
  277. aria_code/football_data_client.py +4 -0
  278. aria_code/image_gen_tools.py +4 -0
  279. aria_code/intent_classifier.py +386 -0
  280. aria_code/kling_video_client.py +4 -0
  281. aria_code/licensing.py +151 -0
  282. aria_code/local_finance_tools.py +4 -0
  283. aria_code/local_image_provider.py +4 -0
  284. aria_code/local_llm_provider.py +4 -0
  285. aria_code/macro_tools.py +4 -0
  286. aria_code/markdown_pdf.py +289 -0
  287. aria_code/market_data_client.py +4 -0
  288. aria_code/mcp_client.py +639 -0
  289. aria_code/memory_manager.py +246 -0
  290. aria_code/model_capability.py +486 -0
  291. aria_code/notification_tools.py +4 -0
  292. aria_code/openai_image_client.py +4 -0
  293. aria_code/packages/__init__.py +23 -0
  294. aria_code/packages/adk_bridge/__init__.py +11 -0
  295. aria_code/packages/adk_bridge/code_review_tools.py +49 -0
  296. aria_code/packages/adk_bridge/market_tools.py +125 -0
  297. aria_code/packages/aria_agents/__init__.py +5 -0
  298. aria_code/packages/aria_agents/manifest.py +78 -0
  299. aria_code/packages/aria_core/__init__.py +34 -0
  300. aria_code/packages/aria_core/architecture.py +269 -0
  301. aria_code/packages/aria_core/export.py +147 -0
  302. aria_code/packages/aria_core/manifest.py +65 -0
  303. aria_code/packages/aria_core/paths.py +57 -0
  304. aria_code/packages/aria_core/secure_file.py +68 -0
  305. aria_code/packages/aria_infra/__init__.py +15 -0
  306. aria_code/packages/aria_infra/arthera.py +52 -0
  307. aria_code/packages/aria_infra/doctor.py +246 -0
  308. aria_code/packages/aria_infra/product.py +40 -0
  309. aria_code/packages/aria_mcp/__init__.py +25 -0
  310. aria_code/packages/aria_mcp/bridge.py +201 -0
  311. aria_code/packages/aria_mcp/circuit.py +82 -0
  312. aria_code/packages/aria_mcp/config.py +111 -0
  313. aria_code/packages/aria_mcp/installer.py +95 -0
  314. aria_code/packages/aria_mcp/server.py +1550 -0
  315. aria_code/packages/aria_mcp/tools.py +81 -0
  316. aria_code/packages/aria_sdk/__init__.py +19 -0
  317. aria_code/packages/aria_sdk/client.py +396 -0
  318. aria_code/packages/aria_sdk/providers.py +70 -0
  319. aria_code/packages/aria_sdk/streaming.py +79 -0
  320. aria_code/packages/aria_sdk/types.py +86 -0
  321. aria_code/packages/aria_services/__init__.py +92 -0
  322. aria_code/packages/aria_services/cache.py +60 -0
  323. aria_code/packages/aria_services/context.py +356 -0
  324. aria_code/packages/aria_services/context_checkpoints.py +157 -0
  325. aria_code/packages/aria_services/data.py +11 -0
  326. aria_code/packages/aria_services/provider_health.py +268 -0
  327. aria_code/packages/aria_services/references.py +247 -0
  328. aria_code/packages/aria_services/registry.py +249 -0
  329. aria_code/packages/aria_services/research_protocol.py +57 -0
  330. aria_code/packages/aria_services/research_quality.py +186 -0
  331. aria_code/packages/aria_services/settings.py +157 -0
  332. aria_code/packages/aria_services/usage.py +151 -0
  333. aria_code/packages/aria_skills/__init__.py +46 -0
  334. aria_code/packages/aria_skills/catalog.py +149 -0
  335. aria_code/packages/aria_skills/loader.py +455 -0
  336. aria_code/packages/aria_skills/registry.py +84 -0
  337. aria_code/packages/aria_tools/__init__.py +5 -0
  338. aria_code/packages/aria_tools/financial/__init__.py +8 -0
  339. aria_code/packages/aria_tools/financial/_paths.py +130 -0
  340. aria_code/packages/aria_tools/financial/compliance.py +31 -0
  341. aria_code/packages/aria_tools/financial/factors.py +33 -0
  342. aria_code/packages/aria_tools/financial/risk_tools.py +33 -0
  343. aria_code/packages/aria_tools/financial/strategy.py +31 -0
  344. aria_code/packages/aria_tools/registry.py +128 -0
  345. aria_code/packages/quant_engine/__init__.py +34 -0
  346. aria_code/packages/quant_engine/_namespace.py +62 -0
  347. aria_code/packages/quant_engine/backtest/__init__.py +14 -0
  348. aria_code/packages/quant_engine/backtest/core.py +53 -0
  349. aria_code/packages/quant_engine/backtest/engine.py +96 -0
  350. aria_code/packages/quant_engine/portfolio/__init__.py +13 -0
  351. aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
  352. aria_code/packages/quant_engine/sports/__init__.py +80 -0
  353. aria_code/packages/quant_engine/sports/calibrator.py +354 -0
  354. aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
  355. aria_code/packages/quant_engine/sports/elo.py +300 -0
  356. aria_code/packages/quant_engine/sports/form.py +188 -0
  357. aria_code/packages/quant_engine/sports/h2h.py +195 -0
  358. aria_code/packages/quant_engine/sports/ml_model.py +377 -0
  359. aria_code/packages/quant_engine/sports/predictor.py +312 -0
  360. aria_code/packages/quant_engine/sports/tracker.py +665 -0
  361. aria_code/packages/quant_engine/stochastic/__init__.py +35 -0
  362. aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
  363. aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
  364. aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
  365. aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
  366. aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
  367. aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
  368. aria_code/packs/__init__.py +85 -0
  369. aria_code/packs/base.py +168 -0
  370. aria_code/packs/finance/__init__.py +250 -0
  371. aria_code/packs/logistics/__init__.py +169 -0
  372. aria_code/packs/payments/__init__.py +139 -0
  373. aria_code/packs/realty/__init__.py +190 -0
  374. aria_code/packs/registry.py +202 -0
  375. aria_code/packs/rules.py +90 -0
  376. aria_code/plan_utils.py +194 -0
  377. aria_code/plugin_loader.py +328 -0
  378. aria_code/portfolio_ledger.py +4 -0
  379. aria_code/preview_server.py +449 -0
  380. aria_code/privacy/__init__.py +5 -0
  381. aria_code/privacy/feedback.py +123 -0
  382. aria_code/project_review.py +278 -0
  383. aria_code/project_review_api.py +235 -0
  384. aria_code/project_review_server.py +18 -0
  385. aria_code/project_tools.py +4 -0
  386. aria_code/providers/__init__.py +30 -0
  387. aria_code/providers/llm/__init__.py +19 -0
  388. aria_code/providers/llm/anthropic.py +184 -0
  389. aria_code/providers/llm/autoconfig.py +213 -0
  390. aria_code/providers/llm/base.py +139 -0
  391. aria_code/providers/llm/custom.py +158 -0
  392. aria_code/providers/llm/ollama.py +129 -0
  393. aria_code/providers/llm/openai_compat.py +313 -0
  394. aria_code/providers/llm/registry.py +444 -0
  395. aria_code/providers/local_image_provider.py +202 -0
  396. aria_code/providers/local_llm_provider.py +589 -0
  397. aria_code/realty_data_tools.py +4 -0
  398. aria_code/report_exporters.py +150 -0
  399. aria_code/report_generator.py +1764 -0
  400. aria_code/runtime/__init__.py +169 -0
  401. aria_code/runtime/acceptance.py +481 -0
  402. aria_code/runtime/agent_loop.py +1520 -0
  403. aria_code/runtime/approval.py +57 -0
  404. aria_code/runtime/budget.py +232 -0
  405. aria_code/runtime/checkpoints.py +432 -0
  406. aria_code/runtime/crash_report.py +174 -0
  407. aria_code/runtime/events.py +113 -0
  408. aria_code/runtime/gateway.py +150 -0
  409. aria_code/runtime/lsp.py +484 -0
  410. aria_code/runtime/orchestrator.py +55 -0
  411. aria_code/runtime/repo_map.py +700 -0
  412. aria_code/runtime/run_state.py +127 -0
  413. aria_code/runtime/run_store.py +450 -0
  414. aria_code/runtime/self_healing.py +422 -0
  415. aria_code/runtime/subagent.py +547 -0
  416. aria_code/runtime/task_ledger.py +86 -0
  417. aria_code/runtime/task_orchestrator.py +90 -0
  418. aria_code/runtime/tool_executor.py +217 -0
  419. aria_code/runtime/tool_policy.py +107 -0
  420. aria_code/runtime/worktrees.py +215 -0
  421. aria_code/runway_video_client.py +4 -0
  422. aria_code/safety/__init__.py +23 -0
  423. aria_code/safety/permissions.py +306 -0
  424. aria_code/safety/service.py +102 -0
  425. aria_code/setup_wizard.py +673 -0
  426. aria_code/spreadsheet_tools.py +4 -0
  427. aria_code/strategy_vault.py +4 -0
  428. aria_code/tools/__init__.py +0 -0
  429. aria_code/tools/artifact_tools.py +252 -0
  430. aria_code/tools/broker_tools.py +92 -0
  431. aria_code/tools/build_quant_engine.py +97 -0
  432. aria_code/tools/code_audit_tools.py +192 -0
  433. aria_code/tools/computer_use_tools.py +504 -0
  434. aria_code/tools/data_analysis_tools.py +809 -0
  435. aria_code/tools/enterprise_finance_tools.py +156 -0
  436. aria_code/tools/extended_tools.py +240 -0
  437. aria_code/tools/file_analysis_tools.py +885 -0
  438. aria_code/tools/image_gen_tools.py +114 -0
  439. aria_code/tools/local_finance_tools.py +3417 -0
  440. aria_code/tools/logistics_tools.py +135 -0
  441. aria_code/tools/macro_tools.py +369 -0
  442. aria_code/tools/notification_tools.py +249 -0
  443. aria_code/tools/project_tools.py +525 -0
  444. aria_code/tools/realty_data_tools.py +659 -0
  445. aria_code/tools/spreadsheet_tools.py +388 -0
  446. aria_code/tools/stripe_tools.py +115 -0
  447. aria_code/ui/__init__.py +105 -0
  448. aria_code/ui/banner.py +508 -0
  449. aria_code/ui/completer.py +501 -0
  450. aria_code/ui/console.py +281 -0
  451. aria_code/ui/image_render.py +243 -0
  452. aria_code/ui/input_box.py +488 -0
  453. aria_code/ui/picker.py +201 -0
  454. aria_code/ui/render/__init__.py +11 -0
  455. aria_code/ui/render/finance.py +1705 -0
  456. aria_code/ui/render/market.py +225 -0
  457. aria_code/ui/render/output.py +775 -0
  458. aria_code/ui/render/responsive.py +75 -0
  459. aria_code/ui/render/team.py +354 -0
  460. aria_code/ui/robot.py +235 -0
  461. aria_code/ui/startup_dashboard.py +109 -0
  462. aria_code/utils/vscode_uri.py +37 -0
  463. aria_code/video_analysis.py +147 -0
  464. aria_code/video_editor.py +271 -0
  465. aria_code/workspace/__init__.py +6 -0
  466. aria_code/workspace/files.py +193 -0
  467. aria_code/workspace/verify.py +113 -0
  468. aria_code/workspace_changes.py +199 -0
  469. aria_code/workspace_context.py +233 -0
  470. aria_code/workspace_service.py +196 -0
  471. aria_code/workspace_tools.py +150 -0
  472. aria_code-0.52.0.dist-info/METADATA +237 -0
  473. aria_code-0.52.0.dist-info/RECORD +478 -0
  474. aria_code-0.52.0.dist-info/WHEEL +5 -0
  475. aria_code-0.52.0.dist-info/entry_points.txt +3 -0
  476. aria_code-0.52.0.dist-info/licenses/LICENSE +201 -0
  477. aria_code-0.52.0.dist-info/licenses/NOTICE +49 -0
  478. aria_code-0.52.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,127 @@
1
+ """State machine for durable agent runs.
2
+
3
+ The model may suggest what to do next, but the runtime owns lifecycle state.
4
+ Keeping transitions explicit prevents a failed or interrupted turn from being
5
+ reported as successful merely because the generated text says it is done.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from enum import Enum
11
+ from typing import Dict, FrozenSet
12
+
13
+
14
+ class RunStatus(str, Enum):
15
+ QUEUED = "queued"
16
+ PLANNING = "planning"
17
+ RUNNING = "running"
18
+ WAITING_APPROVAL = "waiting_approval"
19
+ WAITING_USER = "waiting_user"
20
+ VERIFYING = "verifying"
21
+ SUCCEEDED = "succeeded"
22
+ FAILED = "failed"
23
+ CANCELLED = "cancelled"
24
+ INTERRUPTED = "interrupted"
25
+
26
+
27
+ TERMINAL_RUN_STATUSES: FrozenSet[RunStatus] = frozenset({
28
+ RunStatus.SUCCEEDED,
29
+ RunStatus.FAILED,
30
+ RunStatus.CANCELLED,
31
+ })
32
+
33
+ ACTIVE_RUN_STATUSES: FrozenSet[RunStatus] = frozenset({
34
+ RunStatus.QUEUED,
35
+ RunStatus.PLANNING,
36
+ RunStatus.RUNNING,
37
+ RunStatus.WAITING_APPROVAL,
38
+ RunStatus.WAITING_USER,
39
+ RunStatus.VERIFYING,
40
+ })
41
+
42
+ _TRANSITIONS: Dict[RunStatus, FrozenSet[RunStatus]] = {
43
+ RunStatus.QUEUED: frozenset({
44
+ RunStatus.PLANNING,
45
+ RunStatus.RUNNING,
46
+ RunStatus.FAILED,
47
+ RunStatus.CANCELLED,
48
+ RunStatus.INTERRUPTED,
49
+ }),
50
+ RunStatus.PLANNING: frozenset({
51
+ RunStatus.RUNNING,
52
+ RunStatus.WAITING_APPROVAL,
53
+ RunStatus.WAITING_USER,
54
+ RunStatus.FAILED,
55
+ RunStatus.CANCELLED,
56
+ RunStatus.INTERRUPTED,
57
+ }),
58
+ RunStatus.RUNNING: frozenset({
59
+ RunStatus.WAITING_APPROVAL,
60
+ RunStatus.WAITING_USER,
61
+ RunStatus.VERIFYING,
62
+ RunStatus.FAILED,
63
+ RunStatus.CANCELLED,
64
+ RunStatus.INTERRUPTED,
65
+ }),
66
+ RunStatus.WAITING_APPROVAL: frozenset({
67
+ RunStatus.RUNNING,
68
+ RunStatus.FAILED,
69
+ RunStatus.CANCELLED,
70
+ RunStatus.INTERRUPTED,
71
+ }),
72
+ RunStatus.WAITING_USER: frozenset({
73
+ RunStatus.RUNNING,
74
+ RunStatus.FAILED,
75
+ RunStatus.CANCELLED,
76
+ RunStatus.INTERRUPTED,
77
+ }),
78
+ RunStatus.VERIFYING: frozenset({
79
+ RunStatus.RUNNING,
80
+ RunStatus.SUCCEEDED,
81
+ RunStatus.FAILED,
82
+ RunStatus.CANCELLED,
83
+ RunStatus.INTERRUPTED,
84
+ }),
85
+ RunStatus.INTERRUPTED: frozenset({
86
+ RunStatus.PLANNING,
87
+ RunStatus.RUNNING,
88
+ RunStatus.FAILED,
89
+ RunStatus.CANCELLED,
90
+ }),
91
+ RunStatus.SUCCEEDED: frozenset(),
92
+ RunStatus.FAILED: frozenset(),
93
+ RunStatus.CANCELLED: frozenset(),
94
+ }
95
+
96
+
97
+ class InvalidRunTransition(ValueError):
98
+ """Raised when code attempts an unsupported lifecycle transition."""
99
+
100
+
101
+ def normalize_run_status(status: RunStatus | str) -> RunStatus:
102
+ if isinstance(status, RunStatus):
103
+ return status
104
+ try:
105
+ return RunStatus(str(status))
106
+ except ValueError as exc:
107
+ raise InvalidRunTransition(f"Unknown run status: {status!r}") from exc
108
+
109
+
110
+ def can_transition(current: RunStatus | str, target: RunStatus | str) -> bool:
111
+ source = normalize_run_status(current)
112
+ destination = normalize_run_status(target)
113
+ return source == destination or destination in _TRANSITIONS[source]
114
+
115
+
116
+ def require_transition(current: RunStatus | str, target: RunStatus | str) -> None:
117
+ source = normalize_run_status(current)
118
+ destination = normalize_run_status(target)
119
+ if not can_transition(source, destination):
120
+ raise InvalidRunTransition(
121
+ f"Invalid run transition: {source.value} -> {destination.value}"
122
+ )
123
+
124
+
125
+ def is_terminal(status: RunStatus | str) -> bool:
126
+ return normalize_run_status(status) in TERMINAL_RUN_STATUSES
127
+
@@ -0,0 +1,450 @@
1
+ """SQLite-backed run and event persistence for the Aria agent runtime."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import socket
8
+ import sqlite3
9
+ import time
10
+ import uuid
11
+ from dataclasses import dataclass, field
12
+ from pathlib import Path
13
+ from typing import Any, Dict, Iterable, Optional
14
+
15
+ from .run_state import (
16
+ ACTIVE_RUN_STATUSES,
17
+ RunStatus,
18
+ is_terminal,
19
+ normalize_run_status,
20
+ require_transition,
21
+ )
22
+ from aria_code.packages.aria_core.paths import aria_home
23
+
24
+
25
+ _SECRET_KEYS = {
26
+ "api_key",
27
+ "access_token",
28
+ "auth_token",
29
+ "authorization",
30
+ "cookie",
31
+ "password",
32
+ "private_key",
33
+ "secret",
34
+ "token",
35
+ }
36
+
37
+
38
+ def _default_database_path() -> Path:
39
+ configured = os.getenv("ARIA_HOME")
40
+ if configured:
41
+ root = Path(configured).expanduser()
42
+ else:
43
+ legacy = aria_home()
44
+ root = legacy if legacy.exists() else Path.home() / ".aria-code"
45
+ return root / "runtime" / "runs.sqlite3"
46
+
47
+
48
+ def _redact(value: Any) -> Any:
49
+ if isinstance(value, dict):
50
+ result = {}
51
+ for key, item in value.items():
52
+ normalized = str(key).lower().replace("-", "_")
53
+ result[key] = "[REDACTED]" if normalized in _SECRET_KEYS else _redact(item)
54
+ return result
55
+ if isinstance(value, (list, tuple)):
56
+ return [_redact(item) for item in value]
57
+ return value
58
+
59
+
60
+ def _json_dumps(value: Any) -> str:
61
+ return json.dumps(_redact(value), ensure_ascii=False, default=str, separators=(",", ":"))
62
+
63
+
64
+ def _json_loads(value: str | None) -> Dict[str, Any]:
65
+ if not value:
66
+ return {}
67
+ try:
68
+ loaded = json.loads(value)
69
+ return loaded if isinstance(loaded, dict) else {"value": loaded}
70
+ except (TypeError, json.JSONDecodeError):
71
+ return {}
72
+
73
+
74
+ @dataclass(frozen=True)
75
+ class RunRecord:
76
+ run_id: str
77
+ session_id: str
78
+ status: RunStatus
79
+ prompt: str
80
+ workspace: str
81
+ provider: str
82
+ parent_run_id: Optional[str]
83
+ owner_pid: int
84
+ owner_host: str
85
+ created_at: float
86
+ updated_at: float
87
+ started_at: Optional[float]
88
+ finished_at: Optional[float]
89
+ error: str
90
+ metadata: Dict[str, Any] = field(default_factory=dict)
91
+
92
+
93
+ @dataclass(frozen=True)
94
+ class RunEventRecord:
95
+ event_id: str
96
+ run_id: str
97
+ sequence: int
98
+ event_type: str
99
+ timestamp: float
100
+ data: Dict[str, Any] = field(default_factory=dict)
101
+
102
+
103
+ class RunNotFoundError(KeyError):
104
+ """Raised when a run identifier is not present in the durable store."""
105
+
106
+
107
+ class RunStore:
108
+ """Durable source of truth for runs and their append-only event streams."""
109
+
110
+ def __init__(self, database_path: Path | str | None = None) -> None:
111
+ self.database_path = Path(database_path or _default_database_path()).expanduser()
112
+ self.database_path.parent.mkdir(parents=True, exist_ok=True)
113
+ self._initialize()
114
+
115
+ def _connect(self) -> sqlite3.Connection:
116
+ connection = sqlite3.connect(str(self.database_path), timeout=10.0)
117
+ connection.row_factory = sqlite3.Row
118
+ connection.execute("PRAGMA foreign_keys = ON")
119
+ connection.execute("PRAGMA busy_timeout = 10000")
120
+ return connection
121
+
122
+ def _initialize(self) -> None:
123
+ with self._connect() as connection:
124
+ connection.execute("PRAGMA journal_mode = WAL")
125
+ connection.execute("PRAGMA synchronous = NORMAL")
126
+ connection.executescript(
127
+ """
128
+ CREATE TABLE IF NOT EXISTS runs (
129
+ run_id TEXT PRIMARY KEY,
130
+ session_id TEXT NOT NULL,
131
+ parent_run_id TEXT,
132
+ status TEXT NOT NULL,
133
+ prompt TEXT NOT NULL,
134
+ workspace TEXT NOT NULL,
135
+ provider TEXT NOT NULL DEFAULT '',
136
+ owner_pid INTEGER NOT NULL,
137
+ owner_host TEXT NOT NULL,
138
+ created_at REAL NOT NULL,
139
+ updated_at REAL NOT NULL,
140
+ started_at REAL,
141
+ finished_at REAL,
142
+ error TEXT NOT NULL DEFAULT '',
143
+ metadata_json TEXT NOT NULL DEFAULT '{}',
144
+ FOREIGN KEY(parent_run_id) REFERENCES runs(run_id)
145
+ );
146
+
147
+ CREATE TABLE IF NOT EXISTS run_events (
148
+ event_id TEXT PRIMARY KEY,
149
+ run_id TEXT NOT NULL,
150
+ sequence INTEGER NOT NULL,
151
+ event_type TEXT NOT NULL,
152
+ timestamp REAL NOT NULL,
153
+ data_json TEXT NOT NULL DEFAULT '{}',
154
+ UNIQUE(run_id, sequence),
155
+ FOREIGN KEY(run_id) REFERENCES runs(run_id) ON DELETE CASCADE
156
+ );
157
+
158
+ CREATE INDEX IF NOT EXISTS idx_runs_session_updated
159
+ ON runs(session_id, updated_at DESC);
160
+ CREATE INDEX IF NOT EXISTS idx_runs_status_updated
161
+ ON runs(status, updated_at DESC);
162
+ CREATE INDEX IF NOT EXISTS idx_run_events_run_sequence
163
+ ON run_events(run_id, sequence);
164
+ """
165
+ )
166
+
167
+ @staticmethod
168
+ def _row_to_run(row: sqlite3.Row) -> RunRecord:
169
+ return RunRecord(
170
+ run_id=row["run_id"],
171
+ session_id=row["session_id"],
172
+ status=RunStatus(row["status"]),
173
+ prompt=row["prompt"],
174
+ workspace=row["workspace"],
175
+ provider=row["provider"],
176
+ parent_run_id=row["parent_run_id"],
177
+ owner_pid=int(row["owner_pid"]),
178
+ owner_host=row["owner_host"],
179
+ created_at=float(row["created_at"]),
180
+ updated_at=float(row["updated_at"]),
181
+ started_at=float(row["started_at"]) if row["started_at"] is not None else None,
182
+ finished_at=float(row["finished_at"]) if row["finished_at"] is not None else None,
183
+ error=row["error"],
184
+ metadata=_json_loads(row["metadata_json"]),
185
+ )
186
+
187
+ @staticmethod
188
+ def _row_to_event(row: sqlite3.Row) -> RunEventRecord:
189
+ return RunEventRecord(
190
+ event_id=row["event_id"],
191
+ run_id=row["run_id"],
192
+ sequence=int(row["sequence"]),
193
+ event_type=row["event_type"],
194
+ timestamp=float(row["timestamp"]),
195
+ data=_json_loads(row["data_json"]),
196
+ )
197
+
198
+ @staticmethod
199
+ def _append_event(
200
+ connection: sqlite3.Connection,
201
+ run_id: str,
202
+ event_type: str,
203
+ data: Dict[str, Any] | None = None,
204
+ *,
205
+ event_id: str | None = None,
206
+ timestamp: float | None = None,
207
+ ) -> RunEventRecord:
208
+ sequence = int(connection.execute(
209
+ "SELECT COALESCE(MAX(sequence), 0) + 1 FROM run_events WHERE run_id = ?",
210
+ (run_id,),
211
+ ).fetchone()[0])
212
+ record = RunEventRecord(
213
+ event_id=event_id or uuid.uuid4().hex,
214
+ run_id=run_id,
215
+ sequence=sequence,
216
+ event_type=event_type,
217
+ timestamp=timestamp or time.time(),
218
+ data=dict(data or {}),
219
+ )
220
+ connection.execute(
221
+ """INSERT INTO run_events
222
+ (event_id, run_id, sequence, event_type, timestamp, data_json)
223
+ VALUES (?, ?, ?, ?, ?, ?)""",
224
+ (
225
+ record.event_id,
226
+ record.run_id,
227
+ record.sequence,
228
+ record.event_type,
229
+ record.timestamp,
230
+ _json_dumps(record.data),
231
+ ),
232
+ )
233
+ connection.execute(
234
+ "UPDATE runs SET updated_at = ? WHERE run_id = ?",
235
+ (record.timestamp, run_id),
236
+ )
237
+ return record
238
+
239
+ def create_run(
240
+ self,
241
+ *,
242
+ session_id: str,
243
+ prompt: str,
244
+ workspace: str,
245
+ provider: str = "",
246
+ parent_run_id: str | None = None,
247
+ metadata: Dict[str, Any] | None = None,
248
+ run_id: str | None = None,
249
+ owner_pid: int | None = None,
250
+ owner_host: str | None = None,
251
+ ) -> RunRecord:
252
+ now = time.time()
253
+ identifier = run_id or uuid.uuid4().hex[:16]
254
+ pid = int(owner_pid if owner_pid is not None else os.getpid())
255
+ host = owner_host or socket.gethostname()
256
+ with self._connect() as connection:
257
+ connection.execute("BEGIN IMMEDIATE")
258
+ connection.execute(
259
+ """INSERT INTO runs
260
+ (run_id, session_id, parent_run_id, status, prompt, workspace,
261
+ provider, owner_pid, owner_host, created_at, updated_at,
262
+ started_at, finished_at, error, metadata_json)
263
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NULL, NULL, '', ?)""",
264
+ (
265
+ identifier,
266
+ session_id,
267
+ parent_run_id,
268
+ RunStatus.QUEUED.value,
269
+ prompt,
270
+ workspace,
271
+ provider,
272
+ pid,
273
+ host,
274
+ now,
275
+ now,
276
+ _json_dumps(metadata or {}),
277
+ ),
278
+ )
279
+ self._append_event(connection, identifier, "run_created", {
280
+ "status": RunStatus.QUEUED.value,
281
+ "provider": provider,
282
+ "workspace": workspace,
283
+ }, timestamp=now)
284
+ record = self.get_run(identifier)
285
+ assert record is not None
286
+ return record
287
+
288
+ def get_run(self, run_id: str) -> Optional[RunRecord]:
289
+ with self._connect() as connection:
290
+ row = connection.execute(
291
+ "SELECT * FROM runs WHERE run_id = ?", (run_id,)
292
+ ).fetchone()
293
+ return self._row_to_run(row) if row is not None else None
294
+
295
+ def transition(
296
+ self,
297
+ run_id: str,
298
+ target: RunStatus | str,
299
+ *,
300
+ reason: str = "",
301
+ error: str | None = None,
302
+ provider: str | None = None,
303
+ data: Dict[str, Any] | None = None,
304
+ ) -> RunRecord:
305
+ destination = normalize_run_status(target)
306
+ now = time.time()
307
+ with self._connect() as connection:
308
+ connection.execute("BEGIN IMMEDIATE")
309
+ row = connection.execute(
310
+ "SELECT * FROM runs WHERE run_id = ?", (run_id,)
311
+ ).fetchone()
312
+ if row is None:
313
+ raise RunNotFoundError(run_id)
314
+ current = RunStatus(row["status"])
315
+ require_transition(current, destination)
316
+ if current == destination:
317
+ return self._row_to_run(row)
318
+
319
+ started_at = row["started_at"]
320
+ if started_at is None and destination in {RunStatus.PLANNING, RunStatus.RUNNING}:
321
+ started_at = now
322
+ finished_at = now if is_terminal(destination) else None
323
+ if error is None and current is RunStatus.INTERRUPTED and destination in {
324
+ RunStatus.PLANNING,
325
+ RunStatus.RUNNING,
326
+ }:
327
+ next_error = ""
328
+ else:
329
+ next_error = row["error"] if error is None else str(error)
330
+ next_provider = row["provider"] if provider is None else str(provider)
331
+ connection.execute(
332
+ """UPDATE runs
333
+ SET status = ?, provider = ?, updated_at = ?, started_at = ?,
334
+ finished_at = ?, error = ?
335
+ WHERE run_id = ?""",
336
+ (
337
+ destination.value,
338
+ next_provider,
339
+ now,
340
+ started_at,
341
+ finished_at,
342
+ next_error,
343
+ run_id,
344
+ ),
345
+ )
346
+ event_data = {
347
+ "from": current.value,
348
+ "to": destination.value,
349
+ "reason": reason,
350
+ }
351
+ if data:
352
+ event_data.update(data)
353
+ self._append_event(connection, run_id, "run_state_changed", event_data, timestamp=now)
354
+ updated = connection.execute(
355
+ "SELECT * FROM runs WHERE run_id = ?", (run_id,)
356
+ ).fetchone()
357
+ return self._row_to_run(updated)
358
+
359
+ def append_event(
360
+ self,
361
+ run_id: str,
362
+ event_type: str,
363
+ data: Dict[str, Any] | None = None,
364
+ *,
365
+ event_id: str | None = None,
366
+ timestamp: float | None = None,
367
+ ) -> RunEventRecord:
368
+ with self._connect() as connection:
369
+ connection.execute("BEGIN IMMEDIATE")
370
+ exists = connection.execute(
371
+ "SELECT 1 FROM runs WHERE run_id = ?", (run_id,)
372
+ ).fetchone()
373
+ if exists is None:
374
+ raise RunNotFoundError(run_id)
375
+ return self._append_event(
376
+ connection,
377
+ run_id,
378
+ event_type,
379
+ data,
380
+ event_id=event_id,
381
+ timestamp=timestamp,
382
+ )
383
+
384
+ def events(self, run_id: str) -> list[RunEventRecord]:
385
+ with self._connect() as connection:
386
+ rows = connection.execute(
387
+ "SELECT * FROM run_events WHERE run_id = ? ORDER BY sequence",
388
+ (run_id,),
389
+ ).fetchall()
390
+ return [self._row_to_event(row) for row in rows]
391
+
392
+ def list_runs(
393
+ self,
394
+ *,
395
+ session_id: str | None = None,
396
+ statuses: Iterable[RunStatus | str] | None = None,
397
+ limit: int = 50,
398
+ ) -> list[RunRecord]:
399
+ clauses = []
400
+ values: list[Any] = []
401
+ if session_id:
402
+ clauses.append("session_id = ?")
403
+ values.append(session_id)
404
+ normalized = [normalize_run_status(status).value for status in (statuses or [])]
405
+ if normalized:
406
+ clauses.append(f"status IN ({','.join('?' for _ in normalized)})")
407
+ values.extend(normalized)
408
+ where = f" WHERE {' AND '.join(clauses)}" if clauses else ""
409
+ values.append(max(1, int(limit)))
410
+ with self._connect() as connection:
411
+ rows = connection.execute(
412
+ f"SELECT * FROM runs{where} ORDER BY updated_at DESC LIMIT ?",
413
+ values,
414
+ ).fetchall()
415
+ return [self._row_to_run(row) for row in rows]
416
+
417
+ @staticmethod
418
+ def _pid_is_alive(pid: int) -> bool:
419
+ if pid <= 0:
420
+ return False
421
+ try:
422
+ os.kill(pid, 0)
423
+ except ProcessLookupError:
424
+ return False
425
+ except PermissionError:
426
+ return True
427
+ return True
428
+
429
+ def recover_orphaned_runs(self) -> list[str]:
430
+ """Mark active runs from dead local processes as interrupted."""
431
+ host = socket.gethostname()
432
+ active_values = [status.value for status in ACTIVE_RUN_STATUSES]
433
+ placeholders = ",".join("?" for _ in active_values)
434
+ with self._connect() as connection:
435
+ rows = connection.execute(
436
+ f"SELECT run_id, owner_pid FROM runs "
437
+ f"WHERE owner_host = ? AND status IN ({placeholders})",
438
+ [host, *active_values],
439
+ ).fetchall()
440
+ recovered = []
441
+ for row in rows:
442
+ if not self._pid_is_alive(int(row["owner_pid"])):
443
+ self.transition(
444
+ row["run_id"],
445
+ RunStatus.INTERRUPTED,
446
+ reason="owner_process_missing",
447
+ error="The process ended before the run reached a terminal state.",
448
+ )
449
+ recovered.append(row["run_id"])
450
+ return recovered