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.
- aria_code/__init__.py +42 -0
- aria_code/_version.py +20 -0
- aria_code/adk_apps/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/agent.py +32 -0
- aria_code/adk_apps/aria_research/__init__.py +1 -0
- aria_code/adk_apps/aria_research/agent.py +31 -0
- aria_code/agents/__init__.py +34 -0
- aria_code/agents/base.py +318 -0
- aria_code/agents/code_review.py +200 -0
- aria_code/agents/deep/__init__.py +37 -0
- aria_code/agents/deep/calibration_loop.py +144 -0
- aria_code/agents/deep/critic.py +125 -0
- aria_code/agents/deep/deepen.py +193 -0
- aria_code/agents/deep/models.py +149 -0
- aria_code/agents/deep/pipeline.py +164 -0
- aria_code/agents/deep/quant_fusion.py +192 -0
- aria_code/agents/deep/themes.py +95 -0
- aria_code/agents/deep/tiers.py +106 -0
- aria_code/agents/engineering/__init__.py +14 -0
- aria_code/agents/engineering/coder.py +222 -0
- aria_code/agents/engineering/tester.py +145 -0
- aria_code/agents/financial/__init__.py +21 -0
- aria_code/agents/financial/cashflow_burn.py +119 -0
- aria_code/agents/financial/catalyst.py +279 -0
- aria_code/agents/financial/corporate_finance.py +175 -0
- aria_code/agents/financial/debate.py +224 -0
- aria_code/agents/financial/earnings.py +303 -0
- aria_code/agents/financial/fundamental.py +191 -0
- aria_code/agents/financial/macro.py +99 -0
- aria_code/agents/financial/news.py +207 -0
- aria_code/agents/financial/risk.py +165 -0
- aria_code/agents/financial/sector.py +279 -0
- aria_code/agents/financial/strategist.py +121 -0
- aria_code/agents/financial/stripe_revenue.py +127 -0
- aria_code/agents/financial/synthesis.py +274 -0
- aria_code/agents/financial/technical.py +258 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +363 -0
- aria_code/agents/realty/__init__.py +62 -0
- aria_code/agents/realty/asset_diagnosis.py +152 -0
- aria_code/agents/realty/business_match.py +166 -0
- aria_code/agents/realty/cashflow_verify.py +209 -0
- aria_code/agents/realty/contract_rules.py +210 -0
- aria_code/agents/realty/energy_anomaly.py +189 -0
- aria_code/agents/realty/exit_settlement.py +208 -0
- aria_code/agents/realty/fulfillment_risk.py +206 -0
- aria_code/agents/realty/ops_optimize.py +160 -0
- aria_code/agents/realty/revenue_share.py +215 -0
- aria_code/agents/registry.py +158 -0
- aria_code/agents/signal_scheme.py +111 -0
- aria_code/agents/sports/__init__.py +0 -0
- aria_code/agents/sports/football_agent.py +197 -0
- aria_code/agents/supervisor.py +113 -0
- aria_code/agents/team.py +492 -0
- aria_code/agents/warehouse/__init__.py +25 -0
- aria_code/agents/warehouse/contracts.py +26 -0
- aria_code/agents/warehouse/dashboard.py +155 -0
- aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
- aria_code/agents/warehouse/inbound_exceptions.py +25 -0
- aria_code/agents/warehouse/inventory_health.py +24 -0
- aria_code/agents/warehouse/local_run.py +71 -0
- aria_code/agents/warehouse/local_snapshot.py +132 -0
- aria_code/agents/warehouse/logistics_cost.py +84 -0
- aria_code/agents/warehouse/logistics_sync.py +21 -0
- aria_code/agents/warehouse/workflow.py +31 -0
- aria_code/aliyun_data_client.py +4 -0
- aria_code/apps/README.md +12 -0
- aria_code/apps/__init__.py +2 -0
- aria_code/apps/channels/README.md +15 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +95 -0
- aria_code/apps/channels/tradingview.py +94 -0
- aria_code/apps/cli/README.md +13 -0
- aria_code/apps/cli/__init__.py +2 -0
- aria_code/apps/cli/_optional.py +120 -0
- aria_code/apps/cli/bootstrap.py +145 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +16 -0
- aria_code/apps/cli/commands/_business_render.py +78 -0
- aria_code/apps/cli/commands/_ui.py +71 -0
- aria_code/apps/cli/commands/analysis_cmds.py +369 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +239 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2543 -0
- aria_code/apps/cli/commands/broker_cmds.py +1234 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +319 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +87 -0
- aria_code/apps/cli/commands/core_cmds.py +2520 -0
- aria_code/apps/cli/commands/data_cmds.py +457 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +230 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/file_cmds.py +312 -0
- aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
- aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
- aria_code/apps/cli/commands/market.py +434 -0
- aria_code/apps/cli/commands/market_cmds.py +1381 -0
- aria_code/apps/cli/commands/market_context.py +425 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2153 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +179 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1278 -0
- aria_code/apps/cli/commands/report.py +437 -0
- aria_code/apps/cli/commands/scaffold_templates.py +617 -0
- aria_code/apps/cli/commands/session_cmds.py +236 -0
- aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
- aria_code/apps/cli/commands/team.py +669 -0
- aria_code/apps/cli/commands/team_render.py +8 -0
- aria_code/apps/cli/commands/ui_cmds.py +401 -0
- aria_code/apps/cli/commands/warehouse_cmds.py +79 -0
- aria_code/apps/cli/commands/workflow_cmds.py +480 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1494 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +78 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/deterministic.py +191 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/finance_service_catalog.py +111 -0
- aria_code/apps/cli/football_reports.py +231 -0
- aria_code/apps/cli/github_app_auth.py +135 -0
- aria_code/apps/cli/google_login.py +196 -0
- aria_code/apps/cli/handlers/__init__.py +11 -0
- aria_code/apps/cli/handlers/broker_handlers.py +122 -0
- aria_code/apps/cli/handlers/chart_handlers.py +1309 -0
- aria_code/apps/cli/handlers/market_handlers.py +2513 -0
- aria_code/apps/cli/handlers/realty_handlers.py +114 -0
- aria_code/apps/cli/handlers/sports_handlers.py +22 -0
- aria_code/apps/cli/handlers/strategy_advice.py +82 -0
- aria_code/apps/cli/helpers.py +302 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +297 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +276 -0
- aria_code/apps/cli/intent_signals.py +344 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +536 -0
- aria_code/apps/cli/main.py +29 -0
- aria_code/apps/cli/market_metadata.py +135 -0
- aria_code/apps/cli/market_universe.py +487 -0
- aria_code/apps/cli/message_processing.py +310 -0
- aria_code/apps/cli/model_catalog.py +620 -0
- aria_code/apps/cli/pdf_report.py +977 -0
- aria_code/apps/cli/plan_mode.py +139 -0
- aria_code/apps/cli/plotly_html.py +15 -0
- aria_code/apps/cli/prediction_feedback.py +202 -0
- aria_code/apps/cli/preflight.py +502 -0
- aria_code/apps/cli/project_aria.py +60 -0
- aria_code/apps/cli/prompt_assembly.py +59 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +53 -0
- aria_code/apps/cli/prompts/select.py +142 -0
- aria_code/apps/cli/prompts/system_prompts.py +552 -0
- aria_code/apps/cli/prompts/ui.py +436 -0
- aria_code/apps/cli/provider_endpoints.py +237 -0
- aria_code/apps/cli/provider_keys.py +73 -0
- aria_code/apps/cli/providers/__init__.py +1 -0
- aria_code/apps/cli/providers/base.py +521 -0
- aria_code/apps/cli/providers/chat_routing.py +128 -0
- aria_code/apps/cli/providers/collaboration.py +98 -0
- aria_code/apps/cli/providers/llm/__init__.py +1 -0
- aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
- aria_code/apps/cli/providers/llm/ollama_stream.py +1594 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +257 -0
- aria_code/apps/cli/providers/runtime_bridge.py +353 -0
- aria_code/apps/cli/providers/vertexai_stream.py +316 -0
- aria_code/apps/cli/runtime_consumer.py +657 -0
- aria_code/apps/cli/sandbox.py +32 -0
- aria_code/apps/cli/session_export.py +120 -0
- aria_code/apps/cli/session_jsonl.py +208 -0
- aria_code/apps/cli/session_store.py +112 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +823 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +106 -0
- aria_code/apps/cli/tools/__init__.py +40 -0
- aria_code/apps/cli/tools/context.py +46 -0
- aria_code/apps/cli/tools/file_tools.py +180 -0
- aria_code/apps/cli/tools/market_tools.py +620 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +726 -0
- aria_code/apps/cli/tools/write_tools.py +845 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +153 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1597 -0
- aria_code/apps/daemon/README.md +14 -0
- aria_code/apps/vscode/README.md +115 -0
- aria_code/apps/vscode/package.json +70 -0
- aria_code/aria_cli.py +6743 -0
- aria_code/aria_daemon.py +1398 -0
- aria_code/aria_feishu_bot.py +1423 -0
- aria_code/aria_mcp_server.py +7 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_relay_server.py +490 -0
- aria_code/aria_telegram_bot.py +202 -0
- aria_code/ariarc.py +385 -0
- aria_code/artifacts.py +581 -0
- aria_code/backtest_engine.py +4 -0
- aria_code/backtest_report.py +4 -0
- aria_code/brokers/__init__.py +78 -0
- aria_code/brokers/automation.py +193 -0
- aria_code/brokers/base.py +231 -0
- aria_code/brokers/capabilities.py +264 -0
- aria_code/brokers/cn/__init__.py +10 -0
- aria_code/brokers/cn/easytrader_broker.py +193 -0
- aria_code/brokers/cn/futu_broker.py +194 -0
- aria_code/brokers/cn/longbridge_broker.py +190 -0
- aria_code/brokers/cn/tiger_broker.py +196 -0
- aria_code/brokers/cn/xtquant_broker.py +225 -0
- aria_code/brokers/config.py +414 -0
- aria_code/brokers/intl/__init__.py +5 -0
- aria_code/brokers/intl/alpaca_broker.py +183 -0
- aria_code/brokers/intl/ibkr_broker.py +215 -0
- aria_code/brokers/intl/trading212_broker.py +183 -0
- aria_code/brokers/intl/webull_broker.py +156 -0
- aria_code/brokers/paper_broker.py +266 -0
- aria_code/brokers/planning.py +296 -0
- aria_code/brokers/registry.py +213 -0
- aria_code/brokers/trading.py +267 -0
- aria_code/canva_client.py +4 -0
- aria_code/change_store.py +127 -0
- aria_code/clients/__init__.py +0 -0
- aria_code/clients/aliyun_data_client.py +660 -0
- aria_code/clients/aria_relay_client.py +182 -0
- aria_code/clients/canva_client.py +284 -0
- aria_code/clients/figma_client.py +100 -0
- aria_code/clients/football_data_client.py +1671 -0
- aria_code/clients/kling_video_client.py +182 -0
- aria_code/clients/market_data_client.py +1976 -0
- aria_code/clients/openai_image_client.py +242 -0
- aria_code/clients/runway_video_client.py +157 -0
- aria_code/clients/warehouse_erp_client.py +105 -0
- aria_code/cloud_memory.py +181 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_tools.py +4 -0
- aria_code/dashboard_generator.py +579 -0
- aria_code/data_analysis_tools.py +4 -0
- aria_code/data_cleaner.py +483 -0
- aria_code/data_service.py +513 -0
- aria_code/datasources/__init__.py +23 -0
- aria_code/datasources/base.py +166 -0
- aria_code/datasources/router.py +253 -0
- aria_code/datasources/sources/__init__.py +15 -0
- aria_code/datasources/sources/akshare_source.py +269 -0
- aria_code/datasources/sources/alpha_vantage_source.py +203 -0
- aria_code/datasources/sources/edgar_source.py +219 -0
- aria_code/datasources/sources/finnhub_source.py +198 -0
- aria_code/datasources/sources/fred_source.py +223 -0
- aria_code/datasources/sources/tushare_source.py +143 -0
- aria_code/datasources/sources/web_scraper_source.py +278 -0
- aria_code/datasources/sources/world_bank_source.py +206 -0
- aria_code/datasources/sources/yfinance_source.py +152 -0
- aria_code/demo_player.py +207 -0
- aria_code/doctor.py +813 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/backtest_engine.py +354 -0
- aria_code/domain/backtest_report.py +472 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/portfolio_ledger.py +307 -0
- aria_code/domain/strategy_vault.py +421 -0
- aria_code/evals/__init__.py +37 -0
- aria_code/evals/harness.py +598 -0
- aria_code/evals/runner.py +238 -0
- aria_code/external_agent_runner.py +115 -0
- aria_code/figma_client.py +4 -0
- aria_code/file_analysis_tools.py +4 -0
- aria_code/finance_formulas.py +4 -0
- aria_code/football_data_client.py +4 -0
- aria_code/image_gen_tools.py +4 -0
- aria_code/intent_classifier.py +386 -0
- aria_code/kling_video_client.py +4 -0
- aria_code/licensing.py +151 -0
- aria_code/local_finance_tools.py +4 -0
- aria_code/local_image_provider.py +4 -0
- aria_code/local_llm_provider.py +4 -0
- aria_code/macro_tools.py +4 -0
- aria_code/markdown_pdf.py +289 -0
- aria_code/market_data_client.py +4 -0
- aria_code/mcp_client.py +639 -0
- aria_code/memory_manager.py +246 -0
- aria_code/model_capability.py +486 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +23 -0
- aria_code/packages/adk_bridge/__init__.py +11 -0
- aria_code/packages/adk_bridge/code_review_tools.py +49 -0
- aria_code/packages/adk_bridge/market_tools.py +125 -0
- aria_code/packages/aria_agents/__init__.py +5 -0
- aria_code/packages/aria_agents/manifest.py +78 -0
- aria_code/packages/aria_core/__init__.py +34 -0
- aria_code/packages/aria_core/architecture.py +269 -0
- aria_code/packages/aria_core/export.py +147 -0
- aria_code/packages/aria_core/manifest.py +65 -0
- aria_code/packages/aria_core/paths.py +57 -0
- aria_code/packages/aria_core/secure_file.py +68 -0
- aria_code/packages/aria_infra/__init__.py +15 -0
- aria_code/packages/aria_infra/arthera.py +52 -0
- aria_code/packages/aria_infra/doctor.py +246 -0
- aria_code/packages/aria_infra/product.py +40 -0
- aria_code/packages/aria_mcp/__init__.py +25 -0
- aria_code/packages/aria_mcp/bridge.py +201 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +111 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1550 -0
- aria_code/packages/aria_mcp/tools.py +81 -0
- aria_code/packages/aria_sdk/__init__.py +19 -0
- aria_code/packages/aria_sdk/client.py +396 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +79 -0
- aria_code/packages/aria_sdk/types.py +86 -0
- aria_code/packages/aria_services/__init__.py +92 -0
- aria_code/packages/aria_services/cache.py +60 -0
- aria_code/packages/aria_services/context.py +356 -0
- aria_code/packages/aria_services/context_checkpoints.py +157 -0
- aria_code/packages/aria_services/data.py +11 -0
- aria_code/packages/aria_services/provider_health.py +268 -0
- aria_code/packages/aria_services/references.py +247 -0
- aria_code/packages/aria_services/registry.py +249 -0
- aria_code/packages/aria_services/research_protocol.py +57 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +157 -0
- aria_code/packages/aria_services/usage.py +151 -0
- aria_code/packages/aria_skills/__init__.py +46 -0
- aria_code/packages/aria_skills/catalog.py +149 -0
- aria_code/packages/aria_skills/loader.py +455 -0
- aria_code/packages/aria_skills/registry.py +84 -0
- aria_code/packages/aria_tools/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/__init__.py +8 -0
- aria_code/packages/aria_tools/financial/_paths.py +130 -0
- aria_code/packages/aria_tools/financial/compliance.py +31 -0
- aria_code/packages/aria_tools/financial/factors.py +33 -0
- aria_code/packages/aria_tools/financial/risk_tools.py +33 -0
- aria_code/packages/aria_tools/financial/strategy.py +31 -0
- aria_code/packages/aria_tools/registry.py +128 -0
- aria_code/packages/quant_engine/__init__.py +34 -0
- aria_code/packages/quant_engine/_namespace.py +62 -0
- aria_code/packages/quant_engine/backtest/__init__.py +14 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +96 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +13 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +80 -0
- aria_code/packages/quant_engine/sports/calibrator.py +354 -0
- aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
- aria_code/packages/quant_engine/sports/elo.py +300 -0
- aria_code/packages/quant_engine/sports/form.py +188 -0
- aria_code/packages/quant_engine/sports/h2h.py +195 -0
- aria_code/packages/quant_engine/sports/ml_model.py +377 -0
- aria_code/packages/quant_engine/sports/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +35 -0
- aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
- aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
- aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- aria_code/packs/__init__.py +85 -0
- aria_code/packs/base.py +168 -0
- aria_code/packs/finance/__init__.py +250 -0
- aria_code/packs/logistics/__init__.py +169 -0
- aria_code/packs/payments/__init__.py +139 -0
- aria_code/packs/realty/__init__.py +190 -0
- aria_code/packs/registry.py +202 -0
- aria_code/packs/rules.py +90 -0
- aria_code/plan_utils.py +194 -0
- aria_code/plugin_loader.py +328 -0
- aria_code/portfolio_ledger.py +4 -0
- aria_code/preview_server.py +449 -0
- aria_code/privacy/__init__.py +5 -0
- aria_code/privacy/feedback.py +123 -0
- aria_code/project_review.py +278 -0
- aria_code/project_review_api.py +235 -0
- aria_code/project_review_server.py +18 -0
- aria_code/project_tools.py +4 -0
- aria_code/providers/__init__.py +30 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/autoconfig.py +213 -0
- aria_code/providers/llm/base.py +139 -0
- aria_code/providers/llm/custom.py +158 -0
- aria_code/providers/llm/ollama.py +129 -0
- aria_code/providers/llm/openai_compat.py +313 -0
- aria_code/providers/llm/registry.py +444 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +589 -0
- aria_code/realty_data_tools.py +4 -0
- aria_code/report_exporters.py +150 -0
- aria_code/report_generator.py +1764 -0
- aria_code/runtime/__init__.py +169 -0
- aria_code/runtime/acceptance.py +481 -0
- aria_code/runtime/agent_loop.py +1520 -0
- aria_code/runtime/approval.py +57 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +432 -0
- aria_code/runtime/crash_report.py +174 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +150 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/repo_map.py +700 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +450 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/subagent.py +547 -0
- aria_code/runtime/task_ledger.py +86 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/tool_executor.py +217 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +23 -0
- aria_code/safety/permissions.py +306 -0
- aria_code/safety/service.py +102 -0
- aria_code/setup_wizard.py +673 -0
- aria_code/spreadsheet_tools.py +4 -0
- aria_code/strategy_vault.py +4 -0
- aria_code/tools/__init__.py +0 -0
- aria_code/tools/artifact_tools.py +252 -0
- aria_code/tools/broker_tools.py +92 -0
- aria_code/tools/build_quant_engine.py +97 -0
- aria_code/tools/code_audit_tools.py +192 -0
- aria_code/tools/computer_use_tools.py +504 -0
- aria_code/tools/data_analysis_tools.py +809 -0
- aria_code/tools/enterprise_finance_tools.py +156 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +885 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3417 -0
- aria_code/tools/logistics_tools.py +135 -0
- aria_code/tools/macro_tools.py +369 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +659 -0
- aria_code/tools/spreadsheet_tools.py +388 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +508 -0
- aria_code/ui/completer.py +501 -0
- aria_code/ui/console.py +281 -0
- aria_code/ui/image_render.py +243 -0
- aria_code/ui/input_box.py +488 -0
- aria_code/ui/picker.py +201 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/finance.py +1705 -0
- aria_code/ui/render/market.py +225 -0
- aria_code/ui/render/output.py +775 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +354 -0
- aria_code/ui/robot.py +235 -0
- aria_code/ui/startup_dashboard.py +109 -0
- aria_code/utils/vscode_uri.py +37 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +271 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +193 -0
- aria_code/workspace/verify.py +113 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_context.py +233 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.52.0.dist-info/METADATA +237 -0
- aria_code-0.52.0.dist-info/RECORD +478 -0
- aria_code-0.52.0.dist-info/WHEEL +5 -0
- aria_code-0.52.0.dist-info/entry_points.txt +3 -0
- aria_code-0.52.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.52.0.dist-info/licenses/NOTICE +49 -0
- 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
|