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.
- aria_bots/__init__.py +7 -0
- aria_bots/channels/README.md +97 -0
- aria_bots/channels/__init__.py +1 -0
- aria_bots/channels/approvals.py +263 -0
- aria_bots/channels/conversation.py +280 -0
- aria_bots/channels/digest.py +186 -0
- aria_bots/channels/registry.py +95 -0
- aria_bots/engine.py +144 -0
- aria_bots/feishu_bot.py +1385 -0
- aria_bots/logistics/__init__.py +1 -0
- aria_bots/logistics/carriers.py +253 -0
- aria_bots/logistics/freight.py +154 -0
- aria_bots/logistics/inventory.py +242 -0
- aria_bots/logistics/mcp_server.py +156 -0
- aria_bots/logistics/records.py +119 -0
- aria_bots/logistics/tenancy.py +152 -0
- aria_bots/market.py +91 -0
- aria_bots/relay_client.py +326 -0
- aria_bots/telegram_bot.py +299 -0
- aria_code/__init__.py +111 -0
- aria_code/_bin/aria-native.exe +0 -0
- aria_code/_bin/aria-runtime.exe +0 -0
- aria_code/_version.py +20 -0
- aria_code/account_memory.py +296 -0
- aria_code/account_session.py +166 -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 +49 -0
- aria_code/agents/base.py +329 -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 +315 -0
- aria_code/agents/financial/fundamental.py +178 -0
- aria_code/agents/financial/macro.py +111 -0
- aria_code/agents/financial/news.py +231 -0
- aria_code/agents/financial/risk.py +146 -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 +277 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +392 -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 +168 -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 +500 -0
- aria_code/agents/video/__init__.py +16 -0
- aria_code/agents/video/director.py +376 -0
- aria_code/agents/video/qa_agent.py +143 -0
- aria_code/agents/video/script_doctor.py +112 -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/navigation.schema.json +110 -0
- aria_code/agents/warehouse/task_agent.py +172 -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 +9 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/approvals.py +11 -0
- aria_code/apps/channels/conversation.py +11 -0
- aria_code/apps/channels/digest.py +11 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +11 -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/app_server.py +656 -0
- aria_code/apps/cli/auto_update.py +125 -0
- aria_code/apps/cli/bootstrap.py +206 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/chat_turn.py +1327 -0
- aria_code/apps/cli/cloud_models.py +231 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +34 -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 +378 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +215 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
- aria_code/apps/cli/commands/broker_cmds.py +1236 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +189 -0
- aria_code/apps/cli/commands/core_cmds.py +2748 -0
- aria_code/apps/cli/commands/data_cmds.py +521 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/engineering_cmds.py +399 -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 +544 -0
- aria_code/apps/cli/commands/market_cmds.py +1419 -0
- aria_code/apps/cli/commands/market_context.py +432 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2374 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
- aria_code/apps/cli/commands/report.py +476 -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 +723 -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 +297 -0
- aria_code/apps/cli/commands/workflow_cmds.py +629 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +158 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/conversation_policy.py +53 -0
- aria_code/apps/cli/deterministic.py +200 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/earnings_view.py +172 -0
- aria_code/apps/cli/endpoint_migration.py +64 -0
- aria_code/apps/cli/exec_events.py +255 -0
- aria_code/apps/cli/file_references.py +155 -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 +239 -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 +1314 -0
- aria_code/apps/cli/handlers/market_handlers.py +2570 -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/headless.py +352 -0
- aria_code/apps/cli/helpers.py +326 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +310 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +305 -0
- aria_code/apps/cli/intent_shadow.py +72 -0
- aria_code/apps/cli/intent_signals.py +347 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +550 -0
- aria_code/apps/cli/main.py +39 -0
- aria_code/apps/cli/market_hours.py +100 -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/mixin_binding.py +32 -0
- aria_code/apps/cli/model_catalog.py +652 -0
- aria_code/apps/cli/model_probe.py +169 -0
- aria_code/apps/cli/model_retirement.py +65 -0
- aria_code/apps/cli/native_branding.py +12 -0
- aria_code/apps/cli/native_bridge.py +118 -0
- aria_code/apps/cli/native_entry.py +58 -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 +78 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +71 -0
- aria_code/apps/cli/prompts/select.py +193 -0
- aria_code/apps/cli/prompts/system_prompts.py +562 -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 +775 -0
- aria_code/apps/cli/providers/chat_routing.py +139 -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 +1574 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
- aria_code/apps/cli/providers/runtime_bridge.py +586 -0
- aria_code/apps/cli/providers/vertexai_stream.py +592 -0
- aria_code/apps/cli/release_download.py +159 -0
- aria_code/apps/cli/review_runner.py +111 -0
- aria_code/apps/cli/runtime_consumer.py +769 -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_refresh.py +123 -0
- aria_code/apps/cli/session_store.py +125 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/task_isolation.py +211 -0
- aria_code/apps/cli/terminal_chrome.py +412 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +932 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +113 -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 +181 -0
- aria_code/apps/cli/tools/market_tools.py +626 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +656 -0
- aria_code/apps/cli/tools/write_tools.py +961 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/transactions.py +269 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +257 -0
- aria_code/apps/cli/updater.py +373 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1663 -0
- aria_code/apps/cli/web_login.py +136 -0
- aria_code/apps/cli/workflow_runner.py +127 -0
- aria_code/apps/cli/workspace_route.py +121 -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 +5244 -0
- aria_code/aria_daemon.py +1372 -0
- aria_code/aria_feishu_bot.py +16 -0
- aria_code/aria_mcp_server.py +61 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_telegram_bot.py +11 -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 +16 -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 +1985 -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 +190 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_server.py +276 -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/sec_statements.py +217 -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/doctor.py +860 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/_finance_core/__init__.py +5 -0
- aria_code/domain/_finance_core/backtest_engine.py +325 -0
- aria_code/domain/_finance_core/performance_stats.py +101 -0
- aria_code/domain/backtest_engine.py +75 -0
- aria_code/domain/backtest_report.py +488 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/performance_stats.py +6 -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/behavior.py +113 -0
- aria_code/evals/gap.py +93 -0
- aria_code/evals/harness.py +949 -0
- aria_code/evals/review_probe.py +132 -0
- aria_code/evals/runner.py +425 -0
- aria_code/evals/scorecard.py +322 -0
- aria_code/evals/summary.py +125 -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 +459 -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 +744 -0
- aria_code/memory_manager.py +273 -0
- aria_code/model_capability.py +512 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +26 -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 +58 -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 +232 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +162 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1705 -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 +393 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +75 -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 +304 -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 +137 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +160 -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 +5 -0
- aria_code/packages/aria_tools/financial/_paths.py +92 -0
- aria_code/packages/aria_tools/registry.py +129 -0
- aria_code/packages/quant_engine/__init__.py +33 -0
- aria_code/packages/quant_engine/_namespace.py +24 -0
- aria_code/packages/quant_engine/backtest/__init__.py +10 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +102 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +76 -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/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +31 -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/healthcare/__init__.py +40 -0
- aria_code/packs/logistics/__init__.py +171 -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/google_video_provider.py +256 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/arthera_gateway.py +90 -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 +137 -0
- aria_code/providers/llm/openai_compat.py +350 -0
- aria_code/providers/llm/registry.py +457 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +549 -0
- aria_code/providers/ollama_models.py +79 -0
- aria_code/providers/openai_params.py +32 -0
- aria_code/providers/tool_messages.py +100 -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/review_service.py +497 -0
- aria_code/runtime/__init__.py +176 -0
- aria_code/runtime/acceptance.py +632 -0
- aria_code/runtime/agent_loop.py +1851 -0
- aria_code/runtime/agent_profiles.py +75 -0
- aria_code/runtime/approval.py +71 -0
- aria_code/runtime/baseline.py +219 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +481 -0
- aria_code/runtime/consult.py +65 -0
- aria_code/runtime/contract.py +282 -0
- aria_code/runtime/delivery.py +432 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +167 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/native_index.py +153 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/processes.py +343 -0
- aria_code/runtime/project_graph.py +614 -0
- aria_code/runtime/repo_map.py +712 -0
- aria_code/runtime/research_bundle.py +105 -0
- aria_code/runtime/review.py +269 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +522 -0
- aria_code/runtime/scoped_runs.py +113 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/semantic_diff.py +171 -0
- aria_code/runtime/subagent.py +721 -0
- aria_code/runtime/symbol_edit.py +117 -0
- aria_code/runtime/task_brief.py +46 -0
- aria_code/runtime/task_context.py +145 -0
- aria_code/runtime/task_ledger.py +108 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/task_outcome.py +33 -0
- aria_code/runtime/task_verification.py +266 -0
- aria_code/runtime/task_worktree.py +448 -0
- aria_code/runtime/tool_executor.py +329 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/transactions.py +216 -0
- aria_code/runtime/windows_job.py +115 -0
- aria_code/runtime/workflows.py +222 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +27 -0
- aria_code/safety/client_isolation.py +178 -0
- aria_code/safety/permissions.py +339 -0
- aria_code/safety/risk.py +699 -0
- aria_code/safety/sandbox.py +148 -0
- aria_code/safety/service.py +102 -0
- aria_code/service_endpoints.py +14 -0
- aria_code/setup_wizard.py +684 -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 +509 -0
- aria_code/tools/data_analysis_tools.py +831 -0
- aria_code/tools/enterprise_finance_tools.py +202 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +880 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3488 -0
- aria_code/tools/logistics_carriers.py +11 -0
- aria_code/tools/logistics_inventory.py +11 -0
- aria_code/tools/logistics_io.py +11 -0
- aria_code/tools/logistics_tenancy.py +11 -0
- aria_code/tools/logistics_tools.py +11 -0
- aria_code/tools/macro_tools.py +412 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +662 -0
- aria_code/tools/spreadsheet_tools.py +399 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/tools/video_tools.py +500 -0
- aria_code/training/__init__.py +1 -0
- aria_code/training/compare.py +117 -0
- aria_code/training/prelude_commands.py +111 -0
- aria_code/training/prelude_eval.py +390 -0
- aria_code/training/prelude_real.py +183 -0
- aria_code/training/prelude_train.py +248 -0
- aria_code/training/sft_dataset.py +230 -0
- aria_code/training/trajectory_dataset.py +263 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +404 -0
- aria_code/ui/completer.py +571 -0
- aria_code/ui/console.py +285 -0
- aria_code/ui/input_box.py +505 -0
- aria_code/ui/mentions.py +206 -0
- aria_code/ui/picker.py +248 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/actions.py +297 -0
- aria_code/ui/render/finance.py +1732 -0
- aria_code/ui/render/market.py +253 -0
- aria_code/ui/render/output.py +954 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +367 -0
- aria_code/ui/robot.py +239 -0
- aria_code/ui/startup_dashboard.py +110 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +518 -0
- aria_code/video_workflow/__init__.py +8 -0
- aria_code/video_workflow/__main__.py +61 -0
- aria_code/video_workflow/engines/__init__.py +18 -0
- aria_code/video_workflow/engines/base.py +69 -0
- aria_code/video_workflow/engines/custom_model.py +204 -0
- aria_code/video_workflow/engines/google_veo.py +94 -0
- aria_code/video_workflow/engines/local_engine.py +71 -0
- aria_code/video_workflow/engines/registry.py +98 -0
- aria_code/video_workflow/google_models.py +278 -0
- aria_code/video_workflow/music/__init__.py +5 -0
- aria_code/video_workflow/music/foley_service.py +132 -0
- aria_code/video_workflow/music/service.py +212 -0
- aria_code/video_workflow/pipeline.py +620 -0
- aria_code/video_workflow/plan.py +202 -0
- aria_code/video_workflow/session.py +297 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +219 -0
- aria_code/workspace/verify.py +167 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.167.0.dist-info/METADATA +330 -0
- aria_code-0.167.0.dist-info/RECORD +601 -0
- aria_code-0.167.0.dist-info/WHEEL +5 -0
- aria_code-0.167.0.dist-info/entry_points.txt +5 -0
- aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
- aria_code-0.167.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"""Project-graph imports and Python definitions from ``aria-native index``.
|
|
2
|
+
|
|
3
|
+
Parsing every Python file twice — ``repo_map`` for its definitions,
|
|
4
|
+
``project_graph`` for its imports, each an ``ast.parse`` and a walk — is most
|
|
5
|
+
of the time a graph build takes. The Rust ``aria-graph`` crate does both in
|
|
6
|
+
parallel; the callers ask it first and keep the Python implementation for
|
|
7
|
+
everything it does not answer:
|
|
8
|
+
|
|
9
|
+
* no binary — ``ARIA_NATIVE_BINARY`` or ``aria-native`` on ``PATH`` — or
|
|
10
|
+
``ARIA_NATIVE_GRAPH=off``;
|
|
11
|
+
* too few files to be worth a process (``_MIN_FILES``);
|
|
12
|
+
* the binary failing, timing out or answering out of shape;
|
|
13
|
+
* a file its parser rejects or cannot read (``fallback``), which Python then
|
|
14
|
+
handles itself.
|
|
15
|
+
|
|
16
|
+
The two implementations must agree; ``tests/test_native_index.py`` holds them
|
|
17
|
+
to it on this repository and on edge cases.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
import os
|
|
24
|
+
import shutil
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from typing import Optional, Sequence
|
|
29
|
+
|
|
30
|
+
_MIN_FILES = 32
|
|
31
|
+
_TIMEOUT = 120
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def native_binary() -> Optional[str]:
|
|
35
|
+
"""``ARIA_NATIVE_BINARY``, else the copy bundled with a native build or a
|
|
36
|
+
platform wheel, else ``PATH``."""
|
|
37
|
+
configured = os.environ.get("ARIA_NATIVE_BINARY", "").strip()
|
|
38
|
+
if configured:
|
|
39
|
+
return configured if Path(configured).is_file() else None
|
|
40
|
+
name = "aria-native.exe" if sys.platform == "win32" else "aria-native"
|
|
41
|
+
if getattr(sys, "frozen", False):
|
|
42
|
+
# go/buildtools puts it in the PyInstaller contents
|
|
43
|
+
# folder (_internal/), which is sys._MEIPASS at run time.
|
|
44
|
+
bundled = Path(getattr(sys, "_MEIPASS", Path(sys.executable).parent)) / name
|
|
45
|
+
if bundled.is_file():
|
|
46
|
+
return str(bundled)
|
|
47
|
+
# A platform wheel (scripts/platform_wheels.py) carries it, with the Go
|
|
48
|
+
# engine beside it, in aria_code/_bin/.
|
|
49
|
+
packaged = Path(__file__).resolve().parents[1] / "_bin" / name
|
|
50
|
+
if packaged.is_file() and (sys.platform == "win32" or os.access(packaged, os.X_OK)):
|
|
51
|
+
return str(packaged)
|
|
52
|
+
return shutil.which("aria-native")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def enabled() -> bool:
|
|
56
|
+
return os.environ.get("ARIA_NATIVE_GRAPH", "").strip().lower() not in ("0", "off", "false", "no")
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _call(command: str, payload: dict, wanted: Sequence[str], key: str,
|
|
60
|
+
minimum: Optional[int]) -> Optional[tuple[dict, list[str]]]:
|
|
61
|
+
"""``(answers by path, paths left to Python)`` from ``aria-native index <command>``, or None."""
|
|
62
|
+
if len(wanted) < (_MIN_FILES if minimum is None else minimum) or not enabled():
|
|
63
|
+
return None
|
|
64
|
+
binary = native_binary()
|
|
65
|
+
if not binary:
|
|
66
|
+
return None
|
|
67
|
+
try:
|
|
68
|
+
done = subprocess.run([binary, "index", command], input=json.dumps(payload), capture_output=True,
|
|
69
|
+
text=True, encoding="utf-8", timeout=_TIMEOUT, check=False)
|
|
70
|
+
if done.returncode != 0:
|
|
71
|
+
return None
|
|
72
|
+
data = json.loads(done.stdout)
|
|
73
|
+
except (OSError, ValueError, subprocess.SubprocessError):
|
|
74
|
+
return None
|
|
75
|
+
answers, fallback = data.get(key), data.get("fallback")
|
|
76
|
+
if not isinstance(answers, dict) or not isinstance(fallback, list):
|
|
77
|
+
return None
|
|
78
|
+
paths = set(wanted)
|
|
79
|
+
if not set(answers) | set(fallback) >= paths: # every file answered one way or the other
|
|
80
|
+
return None
|
|
81
|
+
return ({path: answer for path, answer in answers.items() if path in paths},
|
|
82
|
+
[path for path in fallback if path in paths])
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def resolve_imports(root: Path, files: Sequence[tuple[str, str]], parse: Sequence[str],
|
|
86
|
+
*, minimum: Optional[int] = None) -> Optional[tuple[dict[str, list[str]], list[str]]]:
|
|
87
|
+
"""``(imports by path, paths to parse in Python)``, or None to do it all in Python."""
|
|
88
|
+
payload = {"root": str(root), "files": [list(f) for f in files], "parse": list(parse)}
|
|
89
|
+
result = _call("imports", payload, parse, "imports", minimum)
|
|
90
|
+
if result is None:
|
|
91
|
+
return None
|
|
92
|
+
imports, fallback = result
|
|
93
|
+
return {path: list(found) for path, found in imports.items()}, fallback
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def python_symbols(root: Path, parse: Sequence[str], *, minimum: Optional[int] = None,
|
|
97
|
+
) -> Optional[tuple[dict[str, list[tuple[str, str, int, str]]], list[str]]]:
|
|
98
|
+
"""``(definitions by path as (name, kind, line, parent), paths for Python)``, or None."""
|
|
99
|
+
result = _call("symbols", {"root": str(root), "parse": list(parse)}, parse, "symbols", minimum)
|
|
100
|
+
if result is None:
|
|
101
|
+
return None
|
|
102
|
+
symbols, fallback = result
|
|
103
|
+
try:
|
|
104
|
+
return ({path: [(str(n), str(k), int(line), str(p)) for n, k, line, p in found]
|
|
105
|
+
for path, found in symbols.items()}, fallback)
|
|
106
|
+
except (TypeError, ValueError):
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
UNAVAILABLE = object()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _raw(command: str, payload: dict) -> Optional[dict]:
|
|
114
|
+
"""JSON from ``aria-native index <command>``, or None when it cannot answer."""
|
|
115
|
+
if not enabled():
|
|
116
|
+
return None
|
|
117
|
+
binary = native_binary()
|
|
118
|
+
if not binary:
|
|
119
|
+
return None
|
|
120
|
+
try:
|
|
121
|
+
done = subprocess.run(
|
|
122
|
+
[binary, "index", command], input=json.dumps(payload), capture_output=True,
|
|
123
|
+
text=True, encoding="utf-8", timeout=_TIMEOUT, check=False,
|
|
124
|
+
)
|
|
125
|
+
if done.returncode != 0:
|
|
126
|
+
return None
|
|
127
|
+
data = json.loads(done.stdout)
|
|
128
|
+
except (OSError, ValueError, subprocess.SubprocessError):
|
|
129
|
+
return None
|
|
130
|
+
return data if isinstance(data, dict) else None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def reverse_apply(after: str, diff: str):
|
|
134
|
+
"""The text *diff* was made from, None when it does not fit, or UNAVAILABLE."""
|
|
135
|
+
data = _raw("changes", {"op": "reverse", "after": after, "diff": diff})
|
|
136
|
+
if data is None or "applied" not in data:
|
|
137
|
+
return UNAVAILABLE
|
|
138
|
+
if not data.get("applied"):
|
|
139
|
+
return None
|
|
140
|
+
text = data.get("text")
|
|
141
|
+
return text if isinstance(text, str) else UNAVAILABLE
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def symbol_changes(before: str, after: str, language: str) -> Optional[list[tuple[str, str, str]]]:
|
|
145
|
+
"""``(kind, qualified name, symbol kind)`` rows, or None to decide in Python."""
|
|
146
|
+
data = _raw("changes", {"op": "changes", "before": before, "after": after, "language": language})
|
|
147
|
+
rows = data.get("changes") if isinstance(data, dict) else None
|
|
148
|
+
if not isinstance(rows, list):
|
|
149
|
+
return None
|
|
150
|
+
try:
|
|
151
|
+
return [(str(row["kind"]), str(row["name"]), str(row["symbol_kind"])) for row in rows]
|
|
152
|
+
except (KeyError, TypeError):
|
|
153
|
+
return None
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from typing import List, Dict, Any
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from aria_code.agents.registry import get_registry
|
|
5
|
+
|
|
6
|
+
logger = logging.getLogger(__name__)
|
|
7
|
+
|
|
8
|
+
async def dynamic_agent_orchestration(user_query: str, provider: Any) -> Dict[str, Any]:
|
|
9
|
+
"""
|
|
10
|
+
Dynamic routing to pick the best agents for the user's query.
|
|
11
|
+
"""
|
|
12
|
+
registry = get_registry()
|
|
13
|
+
agents = registry.list()
|
|
14
|
+
|
|
15
|
+
# Filter out orchestrator or core infrastructure agents that shouldn't be dynamically routed
|
|
16
|
+
agents = [a for a in agents if a["name"] not in {"orchestrator", "supervisor"}]
|
|
17
|
+
|
|
18
|
+
agents_info = "\n".join([f"- {a['name']}: {a.get('description', '')}" for a in agents])
|
|
19
|
+
|
|
20
|
+
prompt = (
|
|
21
|
+
f"你是一个高级的智能体编排中心 (Orchestrator)。\n"
|
|
22
|
+
f"用户请求:\n{user_query}\n\n"
|
|
23
|
+
f"可用智能体库:\n{agents_info}\n\n"
|
|
24
|
+
f"请直接输出一段合法的 JSON,选择最合适的智能体来解决用户请求(最多选3个)。\n"
|
|
25
|
+
f"JSON 结构必须是:\n"
|
|
26
|
+
f"{{\n"
|
|
27
|
+
f" \"plan\": \"解释为什么选择这些智能体\",\n"
|
|
28
|
+
f" \"agents\": [\"agent_name_1\", \"agent_name_2\"]\n"
|
|
29
|
+
f"}}\n"
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
# We assume provider is an async function or object that can generate text
|
|
34
|
+
# But we'll just try to use a simple completion if available
|
|
35
|
+
messages = [{"role": "user", "content": prompt}]
|
|
36
|
+
|
|
37
|
+
# Use provider to generate
|
|
38
|
+
full_response = ""
|
|
39
|
+
async for chunk in provider.stream(messages=messages, tools=[]):
|
|
40
|
+
if hasattr(chunk, 'text') and chunk.text:
|
|
41
|
+
full_response += chunk.text
|
|
42
|
+
|
|
43
|
+
# Parse JSON
|
|
44
|
+
start_idx = full_response.find("{")
|
|
45
|
+
end_idx = full_response.rfind("}")
|
|
46
|
+
if start_idx != -1 and end_idx != -1:
|
|
47
|
+
json_str = full_response[start_idx:end_idx+1]
|
|
48
|
+
return json.loads(json_str)
|
|
49
|
+
else:
|
|
50
|
+
return {"plan": "未能解析 JSON", "agents": ["technical"]}
|
|
51
|
+
except Exception as e:
|
|
52
|
+
logger.error(f"Orchestration failed: {e}")
|
|
53
|
+
# Default fallback
|
|
54
|
+
return {"plan": "降级路由", "agents": ["technical", "fundamental"]}
|
|
55
|
+
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
"""Long-running commands: start one, read its output later, send it input, stop it.
|
|
2
|
+
|
|
3
|
+
run_command waited for a command to finish, so a dev server, a test watcher
|
|
4
|
+
or anything interactive either blocked the turn until the timeout or could
|
|
5
|
+
not be used at all: there was no way to start a server and then test against
|
|
6
|
+
it. ``run_command`` with ``background: true`` starts the process here, under
|
|
7
|
+
the same approval and sandbox as any command, and the ``process`` tool reads
|
|
8
|
+
what it has printed since the last look, writes to its stdin, or stops it.
|
|
9
|
+
|
|
10
|
+
Output goes through pipes, not a terminal, so a program that insists on a TTY
|
|
11
|
+
behaves as it does when piped. Every process gets its own process group, so
|
|
12
|
+
stopping it stops what it started; all of them are stopped when Aria exits.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import atexit
|
|
18
|
+
import itertools
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import signal
|
|
22
|
+
import subprocess
|
|
23
|
+
import threading
|
|
24
|
+
import time
|
|
25
|
+
from dataclasses import dataclass, field
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from typing import Sequence
|
|
28
|
+
|
|
29
|
+
MAX_RUNNING = 8
|
|
30
|
+
MAX_BUFFER = 1_000_000 # characters kept per process; older output is dropped
|
|
31
|
+
MAX_READ = 8_000 # characters returned by one read
|
|
32
|
+
MAX_WAIT = 30.0
|
|
33
|
+
|
|
34
|
+
_ids = itertools.count(1)
|
|
35
|
+
_lock = threading.Lock()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class _Process:
|
|
40
|
+
id: str
|
|
41
|
+
command: str
|
|
42
|
+
cwd: str
|
|
43
|
+
popen: subprocess.Popen
|
|
44
|
+
started: float = field(default_factory=time.time)
|
|
45
|
+
text: str = "" # the kept tail of everything printed
|
|
46
|
+
dropped: int = 0 # characters dropped from the front of `text`
|
|
47
|
+
read_upto: int = 0 # absolute position the caller has read to
|
|
48
|
+
done: bool = False # exited, and everything it printed has been read in
|
|
49
|
+
changed: threading.Condition = field(default_factory=threading.Condition)
|
|
50
|
+
stop_lock: threading.Lock = field(default_factory=threading.Lock)
|
|
51
|
+
group_cleaned: bool = False
|
|
52
|
+
windows_job: object | None = None
|
|
53
|
+
|
|
54
|
+
@property
|
|
55
|
+
def running(self) -> bool:
|
|
56
|
+
return _group_running(self)
|
|
57
|
+
|
|
58
|
+
def end(self) -> int:
|
|
59
|
+
return self.dropped + len(self.text)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
_processes: dict[str, _Process] = {}
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def _group_running(proc: _Process) -> bool:
|
|
66
|
+
"""The group may outlive its leader. Zombies have already stopped.
|
|
67
|
+
|
|
68
|
+
Linux CI's PID 1 may not reap orphan zombies promptly; killpg(..., 0)
|
|
69
|
+
alone therefore cannot tell whether a command is still executing.
|
|
70
|
+
"""
|
|
71
|
+
if proc.group_cleaned:
|
|
72
|
+
return False
|
|
73
|
+
proc.popen.poll() # reap our own child even while its children hold stdout
|
|
74
|
+
if os.name == "nt":
|
|
75
|
+
if proc.windows_job is not None:
|
|
76
|
+
active = proc.windows_job.running()
|
|
77
|
+
if not active:
|
|
78
|
+
proc.windows_job.close()
|
|
79
|
+
proc.group_cleaned = True
|
|
80
|
+
return active
|
|
81
|
+
return proc.popen.returncode is None
|
|
82
|
+
try:
|
|
83
|
+
os.killpg(proc.popen.pid, 0)
|
|
84
|
+
except ProcessLookupError:
|
|
85
|
+
proc.group_cleaned = True
|
|
86
|
+
return False
|
|
87
|
+
except PermissionError:
|
|
88
|
+
return True
|
|
89
|
+
if Path("/proc/self/stat").exists():
|
|
90
|
+
seen = False
|
|
91
|
+
for entry in Path("/proc").iterdir():
|
|
92
|
+
if not entry.name.isdecimal():
|
|
93
|
+
continue
|
|
94
|
+
try:
|
|
95
|
+
fields = (entry / "stat").read_text().rsplit(")", 1)[1].split()
|
|
96
|
+
if int(fields[2]) != proc.popen.pid:
|
|
97
|
+
continue
|
|
98
|
+
seen = True
|
|
99
|
+
if fields[0] not in ("Z", "X"):
|
|
100
|
+
return True
|
|
101
|
+
except (OSError, ValueError, IndexError):
|
|
102
|
+
continue
|
|
103
|
+
if seen:
|
|
104
|
+
proc.group_cleaned = True
|
|
105
|
+
return False
|
|
106
|
+
return True
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _wait_group(proc: _Process, seconds: float) -> bool:
|
|
110
|
+
deadline = time.monotonic() + max(0.0, seconds)
|
|
111
|
+
while _group_running(proc):
|
|
112
|
+
remaining = deadline - time.monotonic()
|
|
113
|
+
if remaining <= 0:
|
|
114
|
+
return False
|
|
115
|
+
time.sleep(min(0.05, remaining))
|
|
116
|
+
return True
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _pump(proc: _Process) -> None:
|
|
120
|
+
stream = proc.popen.stdout
|
|
121
|
+
try:
|
|
122
|
+
while True:
|
|
123
|
+
chunk = stream.read1(4096) if hasattr(stream, "read1") else stream.read(4096)
|
|
124
|
+
if not chunk:
|
|
125
|
+
break
|
|
126
|
+
text = chunk.decode("utf-8", errors="replace")
|
|
127
|
+
with proc.changed:
|
|
128
|
+
proc.text += text
|
|
129
|
+
if len(proc.text) > MAX_BUFFER:
|
|
130
|
+
cut = len(proc.text) - MAX_BUFFER
|
|
131
|
+
proc.text = proc.text[cut:]
|
|
132
|
+
proc.dropped += cut
|
|
133
|
+
proc.changed.notify_all()
|
|
134
|
+
except (OSError, ValueError):
|
|
135
|
+
pass
|
|
136
|
+
finally:
|
|
137
|
+
proc.popen.wait()
|
|
138
|
+
stream.close()
|
|
139
|
+
with proc.changed:
|
|
140
|
+
proc.done = True
|
|
141
|
+
proc.changed.notify_all()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def start(argv: str | Sequence[str], *, shell: bool, cwd: str | None, label: str,
|
|
145
|
+
wait: float = 3.0, until: str | None = None) -> dict:
|
|
146
|
+
with _lock:
|
|
147
|
+
running = [p for p in _processes.values() if p.running]
|
|
148
|
+
if len(running) >= MAX_RUNNING:
|
|
149
|
+
ids = ", ".join(p.id for p in running)
|
|
150
|
+
return {"success": False, "error": f"{MAX_RUNNING} background processes are already running "
|
|
151
|
+
f"({ids}). Stop one with the process tool first."}
|
|
152
|
+
popen = subprocess.Popen(
|
|
153
|
+
argv, shell=shell, cwd=cwd, stdin=subprocess.PIPE, stdout=subprocess.PIPE,
|
|
154
|
+
stderr=subprocess.STDOUT, start_new_session=True,
|
|
155
|
+
# The command must not spawn children before the Job owns it.
|
|
156
|
+
creationflags=0x4 if os.name == "nt" else 0, # CREATE_SUSPENDED
|
|
157
|
+
)
|
|
158
|
+
proc = _Process(id=f"p{next(_ids)}", command=label, cwd=cwd or os.getcwd(), popen=popen)
|
|
159
|
+
if os.name == "nt":
|
|
160
|
+
from .windows_job import WindowsJob
|
|
161
|
+
try:
|
|
162
|
+
proc.windows_job = WindowsJob(popen)
|
|
163
|
+
proc.windows_job.resume(popen.pid)
|
|
164
|
+
except OSError as exc:
|
|
165
|
+
if proc.windows_job is not None:
|
|
166
|
+
proc.windows_job.close()
|
|
167
|
+
popen.kill()
|
|
168
|
+
popen.communicate(timeout=5)
|
|
169
|
+
return {"success": False, "error": f"Cannot manage the background process tree: {exc}"}
|
|
170
|
+
_processes[proc.id] = proc
|
|
171
|
+
threading.Thread(target=_pump, args=(proc,), daemon=True, name=f"aria-{proc.id}").start()
|
|
172
|
+
return {"success": True, "data": read(proc.id, wait=wait, until=until)["data"] | {"background": True}}
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _get(process_id: str) -> _Process | None:
|
|
176
|
+
return _processes.get(str(process_id or "").strip())
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def _unknown(process_id: str) -> dict:
|
|
180
|
+
known = ", ".join(_processes) or "none"
|
|
181
|
+
return {"success": False, "error": f"No background process '{process_id}' (known: {known})."}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def read(process_id: str, *, wait: float = 0.0, until: str | None = None) -> dict:
|
|
185
|
+
"""Output printed since the last read.
|
|
186
|
+
|
|
187
|
+
Waits up to ``wait`` seconds, returning early when the process exits or,
|
|
188
|
+
with ``until``, when that regular expression appears in the new output (a
|
|
189
|
+
server's "Listening on"). Returning at the first byte gave half a line
|
|
190
|
+
and a process that had not yet exited.
|
|
191
|
+
"""
|
|
192
|
+
proc = _get(process_id)
|
|
193
|
+
if proc is None:
|
|
194
|
+
return _unknown(process_id)
|
|
195
|
+
pattern = None
|
|
196
|
+
if until:
|
|
197
|
+
try:
|
|
198
|
+
pattern = re.compile(until)
|
|
199
|
+
except re.error as exc:
|
|
200
|
+
return {"success": False, "error": f"Invalid 'until' pattern: {exc}"}
|
|
201
|
+
deadline = time.monotonic() + max(0.0, min(float(wait or 0), MAX_WAIT))
|
|
202
|
+
|
|
203
|
+
def ready() -> bool:
|
|
204
|
+
if proc.done:
|
|
205
|
+
return True
|
|
206
|
+
return bool(pattern and pattern.search(proc.text[max(0, proc.read_upto - proc.dropped):]))
|
|
207
|
+
|
|
208
|
+
with proc.changed:
|
|
209
|
+
while not ready():
|
|
210
|
+
left = deadline - time.monotonic()
|
|
211
|
+
if left <= 0:
|
|
212
|
+
break
|
|
213
|
+
proc.changed.wait(left)
|
|
214
|
+
start_at = max(proc.read_upto, proc.dropped)
|
|
215
|
+
missed = start_at - proc.read_upto
|
|
216
|
+
new = proc.text[start_at - proc.dropped:]
|
|
217
|
+
proc.read_upto = proc.end()
|
|
218
|
+
clipped = len(new) > MAX_READ
|
|
219
|
+
data = {
|
|
220
|
+
"process_id": proc.id,
|
|
221
|
+
"command": proc.command,
|
|
222
|
+
"running": proc.running,
|
|
223
|
+
"exit_code": proc.popen.returncode,
|
|
224
|
+
"output": new[-MAX_READ:],
|
|
225
|
+
}
|
|
226
|
+
if clipped or missed:
|
|
227
|
+
data["output_dropped_chars"] = missed + (len(new) - MAX_READ if clipped else 0)
|
|
228
|
+
if pattern is not None:
|
|
229
|
+
data["matched"] = bool(pattern.search(new))
|
|
230
|
+
return {"success": True, "data": data}
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def write(process_id: str, text: str, *, wait: float = 1.0) -> dict:
|
|
234
|
+
proc = _get(process_id)
|
|
235
|
+
if proc is None:
|
|
236
|
+
return _unknown(process_id)
|
|
237
|
+
if not proc.running:
|
|
238
|
+
return {"success": False, "error": f"{proc.id} has exited (code {proc.popen.returncode})."}
|
|
239
|
+
try:
|
|
240
|
+
proc.popen.stdin.write(str(text).encode("utf-8"))
|
|
241
|
+
proc.popen.stdin.flush()
|
|
242
|
+
except (BrokenPipeError, OSError) as exc:
|
|
243
|
+
return {"success": False, "error": f"Could not write to {proc.id}: {exc}"}
|
|
244
|
+
return read(proc.id, wait=wait)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def stop(process_id: str, *, grace: float = 3.0) -> dict:
|
|
248
|
+
proc = _get(process_id)
|
|
249
|
+
if proc is None:
|
|
250
|
+
return _unknown(process_id)
|
|
251
|
+
error = ""
|
|
252
|
+
with proc.stop_lock:
|
|
253
|
+
if os.name == "nt" and proc.running:
|
|
254
|
+
try:
|
|
255
|
+
proc.windows_job.terminate()
|
|
256
|
+
_wait_group(proc, 2.0)
|
|
257
|
+
except OSError as exc:
|
|
258
|
+
error = str(exc)
|
|
259
|
+
elif os.name != "nt":
|
|
260
|
+
for sig, pause in ((signal.SIGTERM, min(max(float(grace), 0), 30)),
|
|
261
|
+
(signal.SIGKILL, 2.0)):
|
|
262
|
+
if not proc.running:
|
|
263
|
+
break
|
|
264
|
+
try:
|
|
265
|
+
os.killpg(proc.popen.pid, sig)
|
|
266
|
+
except ProcessLookupError:
|
|
267
|
+
proc.group_cleaned = True
|
|
268
|
+
break
|
|
269
|
+
except PermissionError as exc:
|
|
270
|
+
error = str(exc)
|
|
271
|
+
break
|
|
272
|
+
if _wait_group(proc, pause):
|
|
273
|
+
break
|
|
274
|
+
if proc.popen.poll() is not None and proc.popen.stdin:
|
|
275
|
+
proc.popen.stdin.close()
|
|
276
|
+
result = read(proc.id, wait=0.5)
|
|
277
|
+
result["data"]["stopped"] = not proc.running
|
|
278
|
+
if error or proc.running:
|
|
279
|
+
result.update(success=False, error=error or "The process group did not stop within the deadline.")
|
|
280
|
+
return result
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def listing() -> dict:
|
|
284
|
+
rows = [{
|
|
285
|
+
"process_id": p.id, "command": p.command, "cwd": p.cwd, "running": p.running,
|
|
286
|
+
"exit_code": p.popen.returncode, "seconds": round(time.time() - p.started, 1),
|
|
287
|
+
"unread_chars": max(0, p.end() - max(p.read_upto, p.dropped)),
|
|
288
|
+
} for p in _processes.values()]
|
|
289
|
+
return {"success": True, "data": {"processes": rows}}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def stop_all() -> None:
|
|
293
|
+
for proc in list(_processes.values()):
|
|
294
|
+
if proc.running:
|
|
295
|
+
stop(proc.id, grace=1.0)
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
atexit.register(stop_all)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def tool_process(params: dict) -> dict:
|
|
302
|
+
"""The ``process`` tool: output | input | stop | list."""
|
|
303
|
+
action = str(params.get("action") or "output").strip().lower()
|
|
304
|
+
pid = params.get("process_id", "")
|
|
305
|
+
if action == "list":
|
|
306
|
+
return listing()
|
|
307
|
+
if action == "output":
|
|
308
|
+
return read(pid, wait=params.get("wait_seconds", 2), until=params.get("until"))
|
|
309
|
+
if action == "input":
|
|
310
|
+
if "text" not in params:
|
|
311
|
+
return {"success": False, "error": "input needs 'text' (include \\n to press Enter)."}
|
|
312
|
+
return write(pid, params["text"], wait=params.get("wait_seconds", 1))
|
|
313
|
+
if action == "stop":
|
|
314
|
+
return stop(pid)
|
|
315
|
+
return {"success": False, "error": f"Unknown action '{action}': use output, input, stop or list."}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
PROCESS_SCHEMA = {
|
|
319
|
+
"type": "function",
|
|
320
|
+
"function": {
|
|
321
|
+
"name": "process",
|
|
322
|
+
"description": (
|
|
323
|
+
"Manage a command started with run_command background=true (a dev server, a watcher, an "
|
|
324
|
+
"interactive program). action=output returns what it printed since the last read, "
|
|
325
|
+
"waiting up to wait_seconds, or until a regex `until` appears; action=input writes `text` "
|
|
326
|
+
"to its stdin (include \\n for Enter); action=stop ends it and everything it started; "
|
|
327
|
+
"action=list shows all background processes. Stop servers when you are done with them."
|
|
328
|
+
),
|
|
329
|
+
"parameters": {
|
|
330
|
+
"type": "object",
|
|
331
|
+
"properties": {
|
|
332
|
+
"action": {"type": "string", "enum": ["output", "input", "stop", "list"]},
|
|
333
|
+
"process_id": {"type": "string", "description": "The id run_command returned, e.g. p1"},
|
|
334
|
+
"text": {"type": "string", "description": "For input: the text to send"},
|
|
335
|
+
"wait_seconds": {"type": "number", "description": "How long to wait for output (max 30)"},
|
|
336
|
+
"until": {"type": "string", "description": "For output: a regex to wait for, e.g. 'Listening on'"},
|
|
337
|
+
},
|
|
338
|
+
"required": ["action"],
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
__all__ = ["start", "read", "write", "stop", "listing", "stop_all", "tool_process", "PROCESS_SCHEMA"]
|