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,157 @@
|
|
|
1
|
+
"""Compaction checkpoints — persist the pre-compaction conversation.
|
|
2
|
+
|
|
3
|
+
Resolves the context layer's long-standing blocker (artifact schema and
|
|
4
|
+
retention policy, decided 2026-07): every compaction first appends a
|
|
5
|
+
full-snapshot checkpoint, so the conversation state that compaction is about
|
|
6
|
+
to destroy becomes restorable instead of gone.
|
|
7
|
+
|
|
8
|
+
Schema ``aria.context_checkpoint.v1`` — one JSON object per line:
|
|
9
|
+
schema, ts, session_id, seq, kind ("compact" | "hard" | "fallback"),
|
|
10
|
+
message_count, truncated, envelope (optional compaction metadata),
|
|
11
|
+
messages (the full pre-compaction conversation; oldest messages dropped
|
|
12
|
+
first if the serialized snapshot would exceed the per-record byte cap).
|
|
13
|
+
|
|
14
|
+
Storage ``<root>/<session_id>.jsonl`` (append-only, crash-safe, greppable —
|
|
15
|
+
same conventions as the session JSONL store and the durable run store).
|
|
16
|
+
|
|
17
|
+
Retention (enforced on every write, config-overridable by the caller):
|
|
18
|
+
keep the newest ``keep_per_session`` records per session, and delete
|
|
19
|
+
sibling session files untouched for ``max_age_days``.
|
|
20
|
+
|
|
21
|
+
Pure besides filesystem writes under ``root``; clock injectable for tests.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import json
|
|
27
|
+
import time
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from typing import Any, Callable, Dict, List, Optional
|
|
30
|
+
from aria_code.packages.aria_core.paths import aria_home
|
|
31
|
+
|
|
32
|
+
CHECKPOINT_SCHEMA = "aria.context_checkpoint.v1"
|
|
33
|
+
DEFAULT_KEEP_PER_SESSION = 5
|
|
34
|
+
DEFAULT_MAX_AGE_DAYS = 30
|
|
35
|
+
DEFAULT_MAX_SNAPSHOT_BYTES = 2 * 1024 * 1024
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ContextCheckpointStore:
|
|
39
|
+
def __init__(
|
|
40
|
+
self,
|
|
41
|
+
root: Path,
|
|
42
|
+
*,
|
|
43
|
+
keep_per_session: int = DEFAULT_KEEP_PER_SESSION,
|
|
44
|
+
max_age_days: int = DEFAULT_MAX_AGE_DAYS,
|
|
45
|
+
max_snapshot_bytes: int = DEFAULT_MAX_SNAPSHOT_BYTES,
|
|
46
|
+
clock: Callable[[], float] = time.time,
|
|
47
|
+
) -> None:
|
|
48
|
+
self.root = Path(root)
|
|
49
|
+
self.keep_per_session = max(1, int(keep_per_session))
|
|
50
|
+
self.max_age_days = max(1, int(max_age_days))
|
|
51
|
+
self.max_snapshot_bytes = max(64 * 1024, int(max_snapshot_bytes))
|
|
52
|
+
self.clock = clock
|
|
53
|
+
|
|
54
|
+
# ── write path ───────────────────────────────────────────────────────────
|
|
55
|
+
|
|
56
|
+
def record(
|
|
57
|
+
self,
|
|
58
|
+
session_id: str,
|
|
59
|
+
messages: List[dict],
|
|
60
|
+
*,
|
|
61
|
+
kind: str = "compact",
|
|
62
|
+
envelope: Optional[Dict[str, Any]] = None,
|
|
63
|
+
) -> Dict[str, Any]:
|
|
64
|
+
"""Append one checkpoint for *session_id*; returns the written record."""
|
|
65
|
+
self.root.mkdir(parents=True, exist_ok=True)
|
|
66
|
+
existing = self.list(session_id)
|
|
67
|
+
seq = (existing[-1]["seq"] + 1) if existing else 1
|
|
68
|
+
|
|
69
|
+
snapshot, truncated = self._bounded_snapshot(list(messages or []))
|
|
70
|
+
record = {
|
|
71
|
+
"schema": CHECKPOINT_SCHEMA,
|
|
72
|
+
"ts": self.clock(),
|
|
73
|
+
"session_id": str(session_id),
|
|
74
|
+
"seq": seq,
|
|
75
|
+
"kind": kind,
|
|
76
|
+
"message_count": len(messages or []),
|
|
77
|
+
"truncated": truncated,
|
|
78
|
+
"envelope": dict(envelope) if envelope else {},
|
|
79
|
+
"messages": snapshot,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
kept = (existing + [record])[-self.keep_per_session:]
|
|
83
|
+
path = self._path(session_id)
|
|
84
|
+
tmp = path.with_suffix(".jsonl.tmp")
|
|
85
|
+
tmp.write_text(
|
|
86
|
+
"".join(json.dumps(r, ensure_ascii=False, default=str) + "\n" for r in kept),
|
|
87
|
+
encoding="utf-8",
|
|
88
|
+
)
|
|
89
|
+
tmp.replace(path)
|
|
90
|
+
self._expire_old_files()
|
|
91
|
+
return record
|
|
92
|
+
|
|
93
|
+
def _bounded_snapshot(self, messages: List[dict]) -> tuple:
|
|
94
|
+
"""Drop oldest messages until the serialized snapshot fits the cap."""
|
|
95
|
+
truncated = False
|
|
96
|
+
while messages:
|
|
97
|
+
blob = json.dumps(messages, ensure_ascii=False, default=str)
|
|
98
|
+
if len(blob.encode("utf-8")) <= self.max_snapshot_bytes:
|
|
99
|
+
return messages, truncated
|
|
100
|
+
drop = max(1, len(messages) // 10)
|
|
101
|
+
messages = messages[drop:]
|
|
102
|
+
truncated = True
|
|
103
|
+
return [], truncated
|
|
104
|
+
|
|
105
|
+
def _expire_old_files(self) -> None:
|
|
106
|
+
cutoff = self.clock() - self.max_age_days * 86400
|
|
107
|
+
try:
|
|
108
|
+
for f in self.root.glob("*.jsonl"):
|
|
109
|
+
if f.stat().st_mtime < cutoff:
|
|
110
|
+
f.unlink(missing_ok=True)
|
|
111
|
+
except Exception:
|
|
112
|
+
pass # retention is best-effort; never break the write path
|
|
113
|
+
|
|
114
|
+
# ── read path ────────────────────────────────────────────────────────────
|
|
115
|
+
|
|
116
|
+
def list(self, session_id: str) -> List[Dict[str, Any]]:
|
|
117
|
+
path = self._path(session_id)
|
|
118
|
+
if not path.exists():
|
|
119
|
+
return []
|
|
120
|
+
records: List[Dict[str, Any]] = []
|
|
121
|
+
for line in path.read_text(encoding="utf-8").splitlines():
|
|
122
|
+
line = line.strip()
|
|
123
|
+
if not line:
|
|
124
|
+
continue
|
|
125
|
+
try:
|
|
126
|
+
obj = json.loads(line)
|
|
127
|
+
except json.JSONDecodeError:
|
|
128
|
+
continue
|
|
129
|
+
if isinstance(obj, dict) and obj.get("schema") == CHECKPOINT_SCHEMA:
|
|
130
|
+
records.append(obj)
|
|
131
|
+
return records
|
|
132
|
+
|
|
133
|
+
def latest(self, session_id: str) -> Optional[Dict[str, Any]]:
|
|
134
|
+
records = self.list(session_id)
|
|
135
|
+
return records[-1] if records else None
|
|
136
|
+
|
|
137
|
+
@staticmethod
|
|
138
|
+
def restore_messages(record: Dict[str, Any]) -> List[dict]:
|
|
139
|
+
"""The conversation as it stood before that compaction."""
|
|
140
|
+
msgs = record.get("messages")
|
|
141
|
+
return [dict(m) for m in msgs] if isinstance(msgs, list) else []
|
|
142
|
+
|
|
143
|
+
def _path(self, session_id: str) -> Path:
|
|
144
|
+
safe = "".join(c if c.isalnum() or c in "-_" else "_" for c in str(session_id)) or "session"
|
|
145
|
+
return self.root / f"{safe}.jsonl"
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def default_checkpoint_root() -> Path:
|
|
149
|
+
"""Same home-resolution rule as the durable run store."""
|
|
150
|
+
import os
|
|
151
|
+
configured = os.getenv("ARIA_HOME")
|
|
152
|
+
if configured:
|
|
153
|
+
root = Path(configured).expanduser()
|
|
154
|
+
else:
|
|
155
|
+
legacy = aria_home()
|
|
156
|
+
root = legacy if legacy.exists() else Path.home() / ".aria-code"
|
|
157
|
+
return root / "runtime" / "context_checkpoints"
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
"""Provider error classification and lightweight health state."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import time
|
|
6
|
+
from dataclasses import asdict, dataclass
|
|
7
|
+
from typing import Any, Dict, List
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class ProviderIssue:
|
|
12
|
+
provider: str
|
|
13
|
+
category: str
|
|
14
|
+
message: str
|
|
15
|
+
retryable: bool = True
|
|
16
|
+
cooldown_seconds: int = 0
|
|
17
|
+
|
|
18
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
19
|
+
return asdict(self)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class ProviderState:
|
|
24
|
+
provider: str
|
|
25
|
+
status: str = "ok"
|
|
26
|
+
last_error_category: str = ""
|
|
27
|
+
last_error: str = ""
|
|
28
|
+
failures: int = 0
|
|
29
|
+
cooldown_until: float = 0.0
|
|
30
|
+
last_seen_at: float = 0.0
|
|
31
|
+
last_success_at: float = 0.0
|
|
32
|
+
|
|
33
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
34
|
+
data = asdict(self)
|
|
35
|
+
data["cooldown_active"] = self.cooldown_until > time.time()
|
|
36
|
+
data["cooldown_remaining_seconds"] = max(0, int(self.cooldown_until - time.time()))
|
|
37
|
+
return data
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@dataclass(frozen=True)
|
|
41
|
+
class ProviderHealthSummary:
|
|
42
|
+
schema: str
|
|
43
|
+
total: int
|
|
44
|
+
ok: int
|
|
45
|
+
warn: int
|
|
46
|
+
err: int
|
|
47
|
+
cooldown: int
|
|
48
|
+
auth_errors: int
|
|
49
|
+
providers: List[str]
|
|
50
|
+
status: str
|
|
51
|
+
detail: str
|
|
52
|
+
suggestion: str
|
|
53
|
+
|
|
54
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
55
|
+
return asdict(self)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
_PUBLIC_SERVICE_LABELS = {
|
|
59
|
+
"market_data": "市场数据",
|
|
60
|
+
"ai": "AI 助手",
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True)
|
|
65
|
+
class PublicServiceStatus:
|
|
66
|
+
"""A safe, product-facing view of internal provider health."""
|
|
67
|
+
|
|
68
|
+
schema: str
|
|
69
|
+
service: str
|
|
70
|
+
state: str
|
|
71
|
+
label: str
|
|
72
|
+
message: str
|
|
73
|
+
can_retry: bool
|
|
74
|
+
|
|
75
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
76
|
+
return asdict(self)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def public_service_status(
|
|
80
|
+
service: str, snapshot: List[Dict[str, Any]] | None = None
|
|
81
|
+
) -> PublicServiceStatus:
|
|
82
|
+
"""Project provider health into copy that is safe to render in the product.
|
|
83
|
+
|
|
84
|
+
Provider names, endpoint details and raw failures remain internal diagnostics.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
service_key = str(service or "").strip().lower() or "service"
|
|
88
|
+
label = _PUBLIC_SERVICE_LABELS.get(service_key, "服务")
|
|
89
|
+
rows = list(snapshot or [])
|
|
90
|
+
|
|
91
|
+
if not rows:
|
|
92
|
+
return PublicServiceStatus(
|
|
93
|
+
schema="aria.public_service_status.v1",
|
|
94
|
+
service=service_key,
|
|
95
|
+
state="unknown",
|
|
96
|
+
label=label,
|
|
97
|
+
message=f"{label}状态将在首次使用后更新。",
|
|
98
|
+
can_retry=False,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
states = [str(row.get("status") or "unknown") for row in rows]
|
|
102
|
+
categories = [str(row.get("last_error_category") or "") for row in rows]
|
|
103
|
+
available_count = sum(state == "ok" for state in states)
|
|
104
|
+
|
|
105
|
+
if available_count == len(rows):
|
|
106
|
+
state, message, can_retry = "available", f"{label}可用。", False
|
|
107
|
+
elif available_count:
|
|
108
|
+
state, message, can_retry = (
|
|
109
|
+
"degraded",
|
|
110
|
+
f"{label}已自动切换到可用服务。",
|
|
111
|
+
True,
|
|
112
|
+
)
|
|
113
|
+
elif "auth" in categories:
|
|
114
|
+
state, message, can_retry = (
|
|
115
|
+
"unavailable",
|
|
116
|
+
f"{label}暂不可用,需要检查连接设置。",
|
|
117
|
+
False,
|
|
118
|
+
)
|
|
119
|
+
else:
|
|
120
|
+
state, message, can_retry = "unavailable", f"{label}暂不可用,请稍后重试。", True
|
|
121
|
+
|
|
122
|
+
return PublicServiceStatus(
|
|
123
|
+
schema="aria.public_service_status.v1",
|
|
124
|
+
service=service_key,
|
|
125
|
+
state=state,
|
|
126
|
+
label=label,
|
|
127
|
+
message=message,
|
|
128
|
+
can_retry=can_retry,
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def classify_provider_error(provider: str, error: Any) -> ProviderIssue:
|
|
133
|
+
text = str(error or "").strip()
|
|
134
|
+
low = text.lower()
|
|
135
|
+
if not text:
|
|
136
|
+
return ProviderIssue(provider, "unavailable", "provider returned no usable data", True, 30)
|
|
137
|
+
if any(token in low for token in ("429", "rate", "too many", "limit")):
|
|
138
|
+
return ProviderIssue(provider, "rate_limited", "provider rate limited the request", True, 60)
|
|
139
|
+
if any(token in low for token in ("timeout", "timed out", "curl: (28)", "read timed out")):
|
|
140
|
+
return ProviderIssue(provider, "timeout", "provider request timed out", True, 30)
|
|
141
|
+
if any(token in low for token in ("connection", "network", "refused", "remote", "dns", "name resolution")):
|
|
142
|
+
return ProviderIssue(provider, "network", "provider network connection failed", True, 30)
|
|
143
|
+
if any(token in low for token in ("empty", "no data", "not found", "none", "null")):
|
|
144
|
+
return ProviderIssue(provider, "no_data", "provider returned no market data", True, 15)
|
|
145
|
+
if any(token in low for token in ("unauthorized", "forbidden", "api key", "401", "403")):
|
|
146
|
+
return ProviderIssue(provider, "auth", "provider authentication failed", False, 0)
|
|
147
|
+
# Do not retain raw provider errors here. They can contain URLs, request
|
|
148
|
+
# headers, or tokens and this state is consumed by product diagnostics.
|
|
149
|
+
return ProviderIssue(provider, "error", "provider request failed", True, 30)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class ProviderHealthRegistry:
|
|
153
|
+
"""In-process health state for data providers."""
|
|
154
|
+
|
|
155
|
+
def __init__(self) -> None:
|
|
156
|
+
self._states: Dict[str, ProviderState] = {}
|
|
157
|
+
|
|
158
|
+
def mark_success(self, provider: str) -> None:
|
|
159
|
+
if not provider:
|
|
160
|
+
return
|
|
161
|
+
state = self._states.setdefault(provider, ProviderState(provider=provider))
|
|
162
|
+
state.status = "ok"
|
|
163
|
+
state.last_error_category = ""
|
|
164
|
+
state.last_error = ""
|
|
165
|
+
state.failures = 0
|
|
166
|
+
state.cooldown_until = 0.0
|
|
167
|
+
now = time.time()
|
|
168
|
+
state.last_seen_at = now
|
|
169
|
+
state.last_success_at = now
|
|
170
|
+
|
|
171
|
+
def mark_issue(self, issue: ProviderIssue) -> None:
|
|
172
|
+
if not issue.provider:
|
|
173
|
+
return
|
|
174
|
+
state = self._states.setdefault(issue.provider, ProviderState(provider=issue.provider))
|
|
175
|
+
now = time.time()
|
|
176
|
+
state.status = issue.category
|
|
177
|
+
state.last_error_category = issue.category
|
|
178
|
+
state.last_error = issue.message
|
|
179
|
+
state.failures += 1
|
|
180
|
+
state.last_seen_at = now
|
|
181
|
+
if issue.cooldown_seconds:
|
|
182
|
+
state.cooldown_until = max(state.cooldown_until, now + issue.cooldown_seconds)
|
|
183
|
+
|
|
184
|
+
def provider_in_cooldown(self, provider: str) -> bool:
|
|
185
|
+
state = self._states.get(provider)
|
|
186
|
+
return bool(state and state.cooldown_until > time.time())
|
|
187
|
+
|
|
188
|
+
def snapshot(self) -> List[Dict[str, Any]]:
|
|
189
|
+
return [self._states[name].to_dict() for name in sorted(self._states)]
|
|
190
|
+
|
|
191
|
+
def summary(self) -> ProviderHealthSummary:
|
|
192
|
+
return summarize_provider_health(self.snapshot())
|
|
193
|
+
|
|
194
|
+
def public_status(self, service: str) -> PublicServiceStatus:
|
|
195
|
+
return public_service_status(service, self.snapshot())
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
GLOBAL_PROVIDER_HEALTH = ProviderHealthRegistry()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def summarize_provider_health(snapshot: List[Dict[str, Any]] | None = None) -> ProviderHealthSummary:
|
|
202
|
+
rows = list(snapshot or [])
|
|
203
|
+
if not rows:
|
|
204
|
+
return ProviderHealthSummary(
|
|
205
|
+
schema="aria.provider_health_summary.v1",
|
|
206
|
+
total=0,
|
|
207
|
+
ok=0,
|
|
208
|
+
warn=0,
|
|
209
|
+
err=0,
|
|
210
|
+
cooldown=0,
|
|
211
|
+
auth_errors=0,
|
|
212
|
+
providers=[],
|
|
213
|
+
status="warn",
|
|
214
|
+
detail="no provider calls recorded in this session",
|
|
215
|
+
suggestion="Run /quote, /ta, /analyze, or /report to populate provider health.",
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
ok = warn = err = cooldown = auth_errors = 0
|
|
219
|
+
providers: list[str] = []
|
|
220
|
+
for row in rows:
|
|
221
|
+
provider = str(row.get("provider") or "provider")
|
|
222
|
+
providers.append(provider)
|
|
223
|
+
status = str(row.get("status") or "unknown")
|
|
224
|
+
error_category = str(row.get("last_error_category") or "")
|
|
225
|
+
if status == "ok":
|
|
226
|
+
ok += 1
|
|
227
|
+
elif error_category == "auth":
|
|
228
|
+
err += 1
|
|
229
|
+
auth_errors += 1
|
|
230
|
+
else:
|
|
231
|
+
warn += 1
|
|
232
|
+
if row.get("cooldown_active"):
|
|
233
|
+
cooldown += 1
|
|
234
|
+
|
|
235
|
+
if err:
|
|
236
|
+
status = "err"
|
|
237
|
+
elif warn or cooldown:
|
|
238
|
+
status = "warn"
|
|
239
|
+
else:
|
|
240
|
+
status = "ok"
|
|
241
|
+
|
|
242
|
+
parts = [f"{len(rows)} providers"]
|
|
243
|
+
if ok:
|
|
244
|
+
parts.append(f"{ok} ok")
|
|
245
|
+
if warn:
|
|
246
|
+
parts.append(f"{warn} warn")
|
|
247
|
+
if err:
|
|
248
|
+
parts.append(f"{err} err")
|
|
249
|
+
if cooldown:
|
|
250
|
+
parts.append(f"{cooldown} cooldown")
|
|
251
|
+
|
|
252
|
+
suggestion = "Run /doctor --network or inspect /apikey." if status != "ok" else "All providers healthy."
|
|
253
|
+
if auth_errors:
|
|
254
|
+
suggestion = "Fix API keys first, then retry /doctor or /cloud health."
|
|
255
|
+
|
|
256
|
+
return ProviderHealthSummary(
|
|
257
|
+
schema="aria.provider_health_summary.v1",
|
|
258
|
+
total=len(rows),
|
|
259
|
+
ok=ok,
|
|
260
|
+
warn=warn,
|
|
261
|
+
err=err,
|
|
262
|
+
cooldown=cooldown,
|
|
263
|
+
auth_errors=auth_errors,
|
|
264
|
+
providers=providers,
|
|
265
|
+
status=status,
|
|
266
|
+
detail=", ".join(parts),
|
|
267
|
+
suggestion=suggestion,
|
|
268
|
+
)
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
"""Typed, read-only ``@`` context references for Aria inputs.
|
|
2
|
+
|
|
3
|
+
``/`` selects an operation. ``@`` selects context for that operation or for a
|
|
4
|
+
natural-language request. This service deliberately has no terminal or LLM
|
|
5
|
+
dependency so the CLI, SDK, and future clients share the same semantics.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
import re
|
|
12
|
+
import shlex
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Iterable, Iterator, Sequence
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class ReferenceKind:
|
|
20
|
+
name: str
|
|
21
|
+
label_en: str
|
|
22
|
+
label_zh: str
|
|
23
|
+
filesystem: bool = False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
REFERENCE_KINDS: tuple[ReferenceKind, ...] = (
|
|
27
|
+
ReferenceKind("file", "File", "文件", filesystem=True),
|
|
28
|
+
ReferenceKind("folder", "Folder", "目录", filesystem=True),
|
|
29
|
+
ReferenceKind("asset", "Market asset", "市场资产"),
|
|
30
|
+
ReferenceKind("portfolio", "Portfolio", "投资组合", filesystem=True),
|
|
31
|
+
ReferenceKind("strategy", "Strategy", "策略", filesystem=True),
|
|
32
|
+
ReferenceKind("dataset", "Dataset", "数据集", filesystem=True),
|
|
33
|
+
ReferenceKind("run", "Research run", "研究运行", filesystem=True),
|
|
34
|
+
ReferenceKind("report", "Report", "报告", filesystem=True),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
_KIND_MAP = {item.name: item for item in REFERENCE_KINDS}
|
|
38
|
+
_REF_RE = re.compile(
|
|
39
|
+
r"(?<![\w@])@(?:(?P<kind>[A-Za-z][A-Za-z0-9_-]*):)?"
|
|
40
|
+
r"(?P<value>\"[^\"\n]+\"|'[^'\n]+'|[^\s@]+)"
|
|
41
|
+
)
|
|
42
|
+
_TRAILING_PUNCTUATION = ",;!?,。;!?)]}"
|
|
43
|
+
_ASSET_RE = re.compile(r"^[A-Za-z0-9^._=-]{1,32}$")
|
|
44
|
+
_RESOURCE_DIRS = {
|
|
45
|
+
"portfolio": ("portfolios",),
|
|
46
|
+
"strategy": ("strategies",),
|
|
47
|
+
"dataset": ("datasets", "data"),
|
|
48
|
+
"run": ("runs", "reports"),
|
|
49
|
+
"report": ("reports", "generated"),
|
|
50
|
+
}
|
|
51
|
+
_RESOURCE_EXTENSIONS = ("", ".md", ".json", ".yaml", ".yml", ".toml", ".csv", ".html")
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def reference_search_roots(kind: str, workspace: Path, output_root: Path | None = None) -> tuple[Path, ...]:
|
|
55
|
+
"""Return deterministic search roots used by resolution and completion."""
|
|
56
|
+
roots: list[Path] = []
|
|
57
|
+
for name in _RESOURCE_DIRS.get(kind, ()):
|
|
58
|
+
roots.extend((workspace / ".aria" / name, workspace / name))
|
|
59
|
+
if output_root:
|
|
60
|
+
roots.append(output_root / name)
|
|
61
|
+
return tuple(dict.fromkeys(roots))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dataclass(frozen=True)
|
|
65
|
+
class ReferencePolicy:
|
|
66
|
+
workspace: Path = field(default_factory=Path.cwd)
|
|
67
|
+
output_root: Path | None = None
|
|
68
|
+
allowed_roots: tuple[Path, ...] = ()
|
|
69
|
+
|
|
70
|
+
def normalized(self) -> "ReferencePolicy":
|
|
71
|
+
workspace = self.workspace.expanduser().resolve()
|
|
72
|
+
output_root = self.output_root.expanduser().resolve() if self.output_root else None
|
|
73
|
+
roots = [workspace]
|
|
74
|
+
if output_root:
|
|
75
|
+
roots.append(output_root)
|
|
76
|
+
roots.extend(path.expanduser().resolve() for path in self.allowed_roots)
|
|
77
|
+
return ReferencePolicy(
|
|
78
|
+
workspace=workspace,
|
|
79
|
+
output_root=output_root,
|
|
80
|
+
allowed_roots=tuple(dict.fromkeys(roots)),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@dataclass(frozen=True)
|
|
85
|
+
class ContextReference:
|
|
86
|
+
raw: str
|
|
87
|
+
kind: str
|
|
88
|
+
value: str
|
|
89
|
+
start: int
|
|
90
|
+
end: int
|
|
91
|
+
resolved_value: str = ""
|
|
92
|
+
path: Path | None = None
|
|
93
|
+
error: str = ""
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def ok(self) -> bool:
|
|
97
|
+
return not self.error
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@dataclass(frozen=True)
|
|
101
|
+
class PreparedReferences:
|
|
102
|
+
original_text: str
|
|
103
|
+
expanded_text: str
|
|
104
|
+
context_block: str
|
|
105
|
+
references: tuple[ContextReference, ...]
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def errors(self) -> tuple[ContextReference, ...]:
|
|
109
|
+
return tuple(ref for ref in self.references if ref.error)
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def prompt(self) -> str:
|
|
113
|
+
if not self.context_block:
|
|
114
|
+
return self.expanded_text
|
|
115
|
+
return f"{self.expanded_text}\n\n{self.context_block}"
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def iter_reference_tokens(text: str) -> Iterator[tuple[str, str, str, int, int]]:
|
|
119
|
+
"""Yield ``(raw, kind, value, start, end)`` while ignoring email addresses."""
|
|
120
|
+
for match in _REF_RE.finditer(text):
|
|
121
|
+
kind = (match.group("kind") or "file").lower()
|
|
122
|
+
token = match.group("value")
|
|
123
|
+
end = match.end()
|
|
124
|
+
if token[:1] in {"\"", "'"} and token[-1:] == token[:1]:
|
|
125
|
+
value = token[1:-1]
|
|
126
|
+
else:
|
|
127
|
+
trailing = _TRAILING_PUNCTUATION + ("." if kind not in {"file", "folder"} else "")
|
|
128
|
+
value = token.rstrip(trailing)
|
|
129
|
+
end -= len(token) - len(value)
|
|
130
|
+
if value:
|
|
131
|
+
yield text[match.start():end], kind, value, match.start(), end
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class ReferenceService:
|
|
135
|
+
"""Resolve and prepare explicit context references without side effects."""
|
|
136
|
+
|
|
137
|
+
def __init__(self, policy: ReferencePolicy | None = None):
|
|
138
|
+
self.policy = (policy or ReferencePolicy()).normalized()
|
|
139
|
+
|
|
140
|
+
def prepare(self, text: str) -> PreparedReferences:
|
|
141
|
+
refs = tuple(self._resolve(*token) for token in iter_reference_tokens(text))
|
|
142
|
+
expanded = text
|
|
143
|
+
for ref in reversed(refs):
|
|
144
|
+
if not ref.ok:
|
|
145
|
+
continue
|
|
146
|
+
replacement = self._replacement(ref)
|
|
147
|
+
expanded = expanded[:ref.start] + replacement + expanded[ref.end:]
|
|
148
|
+
|
|
149
|
+
blocks: list[str] = []
|
|
150
|
+
for ref in refs:
|
|
151
|
+
if not ref.ok:
|
|
152
|
+
continue
|
|
153
|
+
target = str(ref.path) if ref.path is not None else ref.resolved_value
|
|
154
|
+
if ref.kind == "folder":
|
|
155
|
+
action = "Inspect on demand with list_files or search_code."
|
|
156
|
+
elif ref.kind == "asset":
|
|
157
|
+
action = "Fetch on demand with get_market_data or get_market_history."
|
|
158
|
+
else:
|
|
159
|
+
action = "Inspect on demand with read_file or analyze_file."
|
|
160
|
+
blocks.append(f"- {ref.kind}: {target}\n {action}")
|
|
161
|
+
|
|
162
|
+
context_block = ""
|
|
163
|
+
if blocks:
|
|
164
|
+
context_block = (
|
|
165
|
+
"[Aria resource references - pointers only]\n"
|
|
166
|
+
"No resource content is preloaded. Use the named tools before answering.\n"
|
|
167
|
+
+ "\n".join(blocks)
|
|
168
|
+
+ "\n[End Aria resource references]"
|
|
169
|
+
)
|
|
170
|
+
return PreparedReferences(text, expanded, context_block, refs)
|
|
171
|
+
|
|
172
|
+
def _resolve(self, raw: str, kind: str, value: str, start: int, end: int) -> ContextReference:
|
|
173
|
+
if kind not in _KIND_MAP:
|
|
174
|
+
valid = ", ".join(item.name for item in REFERENCE_KINDS)
|
|
175
|
+
return ContextReference(raw, kind, value, start, end, error=f"unknown reference type '{kind}' (use: {valid})")
|
|
176
|
+
if kind == "asset":
|
|
177
|
+
if not _ASSET_RE.fullmatch(value):
|
|
178
|
+
return ContextReference(raw, kind, value, start, end, error=f"invalid market asset '{value}'")
|
|
179
|
+
return ContextReference(raw, kind, value, start, end, resolved_value=value.upper())
|
|
180
|
+
|
|
181
|
+
path = self._resolve_path(kind, value)
|
|
182
|
+
if path is None:
|
|
183
|
+
return ContextReference(raw, kind, value, start, end, error=f"{kind} not found: {value}")
|
|
184
|
+
if not self._is_allowed(path):
|
|
185
|
+
return ContextReference(raw, kind, value, start, end, path=path, error=f"reference is outside the allowed workspace: {value}")
|
|
186
|
+
if kind == "folder" and not path.is_dir():
|
|
187
|
+
return ContextReference(raw, kind, value, start, end, path=path, error=f"folder not found: {value}")
|
|
188
|
+
if kind != "folder" and not path.is_file():
|
|
189
|
+
return ContextReference(raw, kind, value, start, end, path=path, error=f"{kind} is not a file: {value}")
|
|
190
|
+
|
|
191
|
+
return ContextReference(
|
|
192
|
+
raw, kind, value, start, end,
|
|
193
|
+
resolved_value=str(path), path=path,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
def _resolve_path(self, kind: str, value: str) -> Path | None:
|
|
197
|
+
if kind in {"file", "folder"}:
|
|
198
|
+
candidate = Path(os.path.expandvars(value)).expanduser()
|
|
199
|
+
if not candidate.is_absolute():
|
|
200
|
+
candidate = self.policy.workspace / candidate
|
|
201
|
+
try:
|
|
202
|
+
return candidate.resolve()
|
|
203
|
+
except OSError:
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
for root in self._resource_roots(kind):
|
|
207
|
+
for suffix in _RESOURCE_EXTENSIONS:
|
|
208
|
+
candidate = root / f"{value}{suffix}"
|
|
209
|
+
if candidate.exists():
|
|
210
|
+
return candidate.resolve()
|
|
211
|
+
if not root.is_dir():
|
|
212
|
+
continue
|
|
213
|
+
target_names = {value.lower(), *(f"{value}{suffix}".lower() for suffix in _RESOURCE_EXTENSIONS if suffix)}
|
|
214
|
+
seen = 0
|
|
215
|
+
for candidate in root.rglob("*"):
|
|
216
|
+
seen += 1
|
|
217
|
+
if seen > 2_000:
|
|
218
|
+
break
|
|
219
|
+
if candidate.is_file() and (candidate.name.lower() in target_names or candidate.stem.lower() == value.lower()):
|
|
220
|
+
return candidate.resolve()
|
|
221
|
+
return None
|
|
222
|
+
|
|
223
|
+
def _resource_roots(self, kind: str) -> Iterable[Path]:
|
|
224
|
+
yield from reference_search_roots(kind, self.policy.workspace, self.policy.output_root)
|
|
225
|
+
|
|
226
|
+
def _is_allowed(self, path: Path) -> bool:
|
|
227
|
+
resolved = path.resolve()
|
|
228
|
+
return any(resolved == root or root in resolved.parents for root in self.policy.allowed_roots)
|
|
229
|
+
|
|
230
|
+
def _replacement(self, ref: ContextReference) -> str:
|
|
231
|
+
if ref.kind == "asset":
|
|
232
|
+
return ref.resolved_value
|
|
233
|
+
if ref.path is not None:
|
|
234
|
+
return shlex.quote(str(ref.path)) if " " in str(ref.path) else str(ref.path)
|
|
235
|
+
return ref.resolved_value
|
|
236
|
+
|
|
237
|
+
def build_reference_service(
|
|
238
|
+
*,
|
|
239
|
+
workspace: Path | None = None,
|
|
240
|
+
output_root: Path | None = None,
|
|
241
|
+
allowed_roots: Sequence[Path] = (),
|
|
242
|
+
) -> ReferenceService:
|
|
243
|
+
return ReferenceService(ReferencePolicy(
|
|
244
|
+
workspace=workspace or Path.cwd(),
|
|
245
|
+
output_root=output_root,
|
|
246
|
+
allowed_roots=tuple(allowed_roots),
|
|
247
|
+
))
|