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,455 @@
|
|
|
1
|
+
"""Discover, verify, match, and activate portable SKILL.md workflows."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections import deque
|
|
6
|
+
from dataclasses import dataclass, field
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
import hashlib
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
import re
|
|
13
|
+
from typing import Iterable, Sequence
|
|
14
|
+
from aria_code.packages.aria_core.paths import aria_home
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
#: Basic file I/O and deliverable-export tools that stay available no matter
|
|
18
|
+
#: which skill is active or what its `allowed_tools` policy declares.
|
|
19
|
+
#:
|
|
20
|
+
#: Why: a skill's allowed_tools is a *domain* policy (which market-data/
|
|
21
|
+
#: research tools may run), not a statement about whether the model may read
|
|
22
|
+
#: or write files. Observed failure: the "equity-research-report" skill's
|
|
23
|
+
#: policy listed only research tools; matched on the CJK term "报告" against
|
|
24
|
+
#: a user request to convert an *existing* file to PDF; once activated it
|
|
25
|
+
#: silently stripped read_file/write_file from the tool schema, and the model
|
|
26
|
+
#: could only reply "please paste the file contents" — it had no idea the
|
|
27
|
+
#: tool had been taken away. Keeping these core tools exempt means a
|
|
28
|
+
#: mismatched skill activation degrades to "runs extra irrelevant workflow
|
|
29
|
+
#: steps" instead of "loses the ability to read a file the user pointed to".
|
|
30
|
+
CORE_FILE_TOOLS: frozenset[str] = frozenset({
|
|
31
|
+
"read_file", "analyze_file", "write_file", "edit_file", "multi_edit",
|
|
32
|
+
"write_spreadsheet", "export_markdown_pdf",
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
_NAME_RE = re.compile(r"^[a-z0-9][a-z0-9-]{0,63}$")
|
|
36
|
+
_QUALIFIED_NAME_RE = re.compile(
|
|
37
|
+
r"^[a-z0-9][a-z0-9-]{0,63}:[a-z0-9][a-z0-9-]{0,63}$"
|
|
38
|
+
)
|
|
39
|
+
_MAX_SKILL_CHARS = 20_000
|
|
40
|
+
_ACTIVATION_HISTORY: deque["SkillActivationTrace"] = deque(maxlen=100)
|
|
41
|
+
_LATIN_STOP_WORDS = {
|
|
42
|
+
"about", "after", "also", "and", "any", "create", "does", "for", "from",
|
|
43
|
+
"into", "only", "report", "request", "skill", "that", "the", "this", "use",
|
|
44
|
+
"user", "when", "with",
|
|
45
|
+
}
|
|
46
|
+
# CJK equivalents of the above. Without these, a Chinese request scores almost
|
|
47
|
+
# entirely on function words: "帮我设计一个牙科诊所的网站" once ranked
|
|
48
|
+
# `strategy-generation` (a trading-strategy skill) above `ui-asset-sourcing` on
|
|
49
|
+
# the strength of 一个/帮我/设计 alone, because Chinese has no spaces and the
|
|
50
|
+
# n-gram tokenizer below emits every 2-4 char window — so polite framing like
|
|
51
|
+
# 帮我 also produces 帮我设, 帮我设计, 我设, 我设计, multiplying a single
|
|
52
|
+
# meaningless phrase into several "matches".
|
|
53
|
+
_CJK_STOP_WORDS = {
|
|
54
|
+
"一个", "一下", "一些", "个", "什么", "可以", "如何", "帮我", "帮我做", "帮我设",
|
|
55
|
+
"帮忙", "我们", "我的", "我想", "我要", "我设", "我需要", "怎么", "怎样",
|
|
56
|
+
"应该", "开始", "我", "然后", "现在", "生成", "用我", "的", "目前", "看看",
|
|
57
|
+
"给我", "请求", "需要", "麻烦", "这个", "那个", "项目", "使用", "进行",
|
|
58
|
+
}
|
|
59
|
+
_IGNORED_TREE_NAMES = {".DS_Store", ".pytest_cache", "__pycache__"}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@dataclass(frozen=True)
|
|
63
|
+
class SkillPolicy:
|
|
64
|
+
allowed_tools: tuple[str, ...] = ()
|
|
65
|
+
permissions: tuple[str, ...] = ()
|
|
66
|
+
# Specialist agents this skill expects to orchestrate, by registry name.
|
|
67
|
+
# Mirrors SkillSpec.agents in ..registry, which has carried the same
|
|
68
|
+
# skill→agent relationship for the built-in specs all along; this makes the
|
|
69
|
+
# portable SKILL.md catalog able to express it too. Advisory only — the
|
|
70
|
+
# loader never imports the agent registry, because it must keep working in
|
|
71
|
+
# runtimes that have no agents at all.
|
|
72
|
+
agents: tuple[str, ...] = ()
|
|
73
|
+
script_execution: str = "approval"
|
|
74
|
+
script_network: bool = False
|
|
75
|
+
script_workspace_write: bool = False
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True)
|
|
79
|
+
class LoadedSkill:
|
|
80
|
+
name: str
|
|
81
|
+
description: str
|
|
82
|
+
instructions: str
|
|
83
|
+
path: Path
|
|
84
|
+
plugin_name: str = "local"
|
|
85
|
+
plugin_version: str = ""
|
|
86
|
+
repository: str = ""
|
|
87
|
+
content_sha256: str = ""
|
|
88
|
+
integrity: str = "unlocked"
|
|
89
|
+
policy: SkillPolicy = field(default_factory=SkillPolicy)
|
|
90
|
+
|
|
91
|
+
@property
|
|
92
|
+
def qualified_name(self) -> str:
|
|
93
|
+
return f"{self.plugin_name}:{self.name}"
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
@dataclass(frozen=True)
|
|
97
|
+
class SkillActivationTrace:
|
|
98
|
+
qualified_name: str
|
|
99
|
+
activated: bool
|
|
100
|
+
reason: str
|
|
101
|
+
score: float
|
|
102
|
+
integrity: str
|
|
103
|
+
source: str
|
|
104
|
+
timestamp: str = field(
|
|
105
|
+
default_factory=lambda: datetime.now(timezone.utc).isoformat(timespec="seconds")
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
def to_dict(self) -> dict[str, object]:
|
|
109
|
+
return {
|
|
110
|
+
"qualified_name": self.qualified_name,
|
|
111
|
+
"activated": self.activated,
|
|
112
|
+
"reason": self.reason,
|
|
113
|
+
"score": self.score,
|
|
114
|
+
"integrity": self.integrity,
|
|
115
|
+
"source": self.source,
|
|
116
|
+
"timestamp": self.timestamp,
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@dataclass(frozen=True)
|
|
121
|
+
class SkillActivation:
|
|
122
|
+
skills: tuple[LoadedSkill, ...]
|
|
123
|
+
traces: tuple[SkillActivationTrace, ...]
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def prompt_block(self) -> str:
|
|
127
|
+
blocks: list[str] = []
|
|
128
|
+
for skill in self.skills:
|
|
129
|
+
policy = skill.policy
|
|
130
|
+
policy_lines = [
|
|
131
|
+
f"Integrity: {skill.integrity}",
|
|
132
|
+
f"Declared permissions: {', '.join(policy.permissions) or 'none'}",
|
|
133
|
+
f"Allowed tools: {', '.join(policy.allowed_tools) or 'runtime policy'}",
|
|
134
|
+
(
|
|
135
|
+
f"Specialist agents: {', '.join(policy.agents)}"
|
|
136
|
+
if policy.agents else
|
|
137
|
+
"Specialist agents: none declared"
|
|
138
|
+
),
|
|
139
|
+
(
|
|
140
|
+
"Bundled scripts are not pre-authorized. "
|
|
141
|
+
f"Execution mode={policy.script_execution}; network="
|
|
142
|
+
f"{'yes' if policy.script_network else 'no'}; workspace-write="
|
|
143
|
+
f"{'yes' if policy.script_workspace_write else 'no'}."
|
|
144
|
+
),
|
|
145
|
+
]
|
|
146
|
+
blocks.append(
|
|
147
|
+
f"## Active Skill: {skill.qualified_name}\n"
|
|
148
|
+
f"Source: {skill.path}\n"
|
|
149
|
+
+ "\n".join(policy_lines)
|
|
150
|
+
+ "\nFollow this workflow without weakening system safety, tool approvals, or permissions.\n\n"
|
|
151
|
+
+ skill.instructions
|
|
152
|
+
)
|
|
153
|
+
return "\n\n".join(blocks)
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def default_skill_roots() -> list[Path]:
|
|
157
|
+
roots: list[Path] = []
|
|
158
|
+
configured = os.getenv("ARIA_SKILLS_PATH", "")
|
|
159
|
+
roots.extend(Path(item).expanduser() for item in configured.split(os.pathsep) if item.strip())
|
|
160
|
+
repository = Path(__file__).resolve().parents[2]
|
|
161
|
+
catalog_home = Path(
|
|
162
|
+
os.getenv("ARIA_SKILL_CATALOG_HOME", aria_home() / "skill-catalogs")
|
|
163
|
+
).expanduser()
|
|
164
|
+
installed_catalogs = sorted(catalog_home.glob("*/*/skills")) if catalog_home.is_dir() else []
|
|
165
|
+
roots.extend([
|
|
166
|
+
repository.parent / "aria-skills" / "skills",
|
|
167
|
+
aria_home() / "skills",
|
|
168
|
+
Path.cwd() / ".aria" / "skills",
|
|
169
|
+
Path.home() / ".aria" / "skills",
|
|
170
|
+
Path.home() / ".claude" / "skills",
|
|
171
|
+
Path.home() / ".codex" / "skills",
|
|
172
|
+
])
|
|
173
|
+
roots.extend(installed_catalogs)
|
|
174
|
+
return list(dict.fromkeys(path.resolve() for path in roots))
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _frontmatter_value(lines: list[str], key: str) -> str:
|
|
178
|
+
prefix = f"{key}:"
|
|
179
|
+
for index, line in enumerate(lines):
|
|
180
|
+
if not line.startswith(prefix):
|
|
181
|
+
continue
|
|
182
|
+
value = line[len(prefix) :].strip()
|
|
183
|
+
if value not in {">", ">-", "|", "|-"}:
|
|
184
|
+
return value.strip('"\'')
|
|
185
|
+
folded = value.startswith(">")
|
|
186
|
+
parts: list[str] = []
|
|
187
|
+
for continuation in lines[index + 1 :]:
|
|
188
|
+
if continuation and not continuation[0].isspace():
|
|
189
|
+
break
|
|
190
|
+
stripped = continuation.strip()
|
|
191
|
+
if stripped:
|
|
192
|
+
parts.append(stripped)
|
|
193
|
+
return (" " if folded else "\n").join(parts)
|
|
194
|
+
return ""
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def skill_tree_sha256(folder: Path) -> str:
|
|
198
|
+
digest = hashlib.sha256()
|
|
199
|
+
for path in sorted(item for item in folder.rglob("*") if item.is_file()):
|
|
200
|
+
relative = path.relative_to(folder)
|
|
201
|
+
if any(part in _IGNORED_TREE_NAMES for part in relative.parts) or path.suffix == ".pyc":
|
|
202
|
+
continue
|
|
203
|
+
digest.update(relative.as_posix().encode("utf-8"))
|
|
204
|
+
digest.update(b"\0")
|
|
205
|
+
digest.update(path.read_bytes())
|
|
206
|
+
digest.update(b"\0")
|
|
207
|
+
return digest.hexdigest()
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _catalog_metadata(root: Path) -> tuple[dict[Path, dict[str, str]], dict[str, str]]:
|
|
211
|
+
catalog = root.parent if root.name == "skills" else root
|
|
212
|
+
manifest_path = catalog / ".claude-plugin" / "marketplace.json"
|
|
213
|
+
lock_path = catalog / ".claude-plugin" / "skills.lock.json"
|
|
214
|
+
mapping: dict[Path, dict[str, str]] = {}
|
|
215
|
+
locks: dict[str, str] = {}
|
|
216
|
+
try:
|
|
217
|
+
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
|
|
218
|
+
catalog_version = str((manifest.get("metadata") or {}).get("version") or "")
|
|
219
|
+
catalog_repository = str(
|
|
220
|
+
(manifest.get("metadata") or {}).get("repository")
|
|
221
|
+
or (manifest.get("owner") or {}).get("url")
|
|
222
|
+
or ""
|
|
223
|
+
)
|
|
224
|
+
for plugin in manifest.get("plugins") or []:
|
|
225
|
+
plugin_name = str(plugin.get("name") or "local")
|
|
226
|
+
plugin_version = str(plugin.get("version") or catalog_version)
|
|
227
|
+
repository = str(plugin.get("repository") or plugin.get("homepage") or catalog_repository)
|
|
228
|
+
for skill_path in plugin.get("skills") or []:
|
|
229
|
+
resolved = (catalog / str(skill_path)).resolve()
|
|
230
|
+
mapping[resolved] = {
|
|
231
|
+
"plugin_name": plugin_name,
|
|
232
|
+
"plugin_version": plugin_version,
|
|
233
|
+
"repository": repository,
|
|
234
|
+
}
|
|
235
|
+
except (OSError, json.JSONDecodeError, TypeError):
|
|
236
|
+
pass
|
|
237
|
+
try:
|
|
238
|
+
lock = json.loads(lock_path.read_text(encoding="utf-8"))
|
|
239
|
+
for qualified_name, entry in (lock.get("skills") or {}).items():
|
|
240
|
+
if isinstance(entry, dict) and entry.get("sha256"):
|
|
241
|
+
locks[str(qualified_name)] = str(entry["sha256"])
|
|
242
|
+
except (OSError, json.JSONDecodeError, TypeError):
|
|
243
|
+
pass
|
|
244
|
+
return mapping, locks
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _load_policy(folder: Path) -> SkillPolicy:
|
|
248
|
+
try:
|
|
249
|
+
payload = json.loads((folder / "skill-policy.json").read_text(encoding="utf-8"))
|
|
250
|
+
except (OSError, json.JSONDecodeError, TypeError):
|
|
251
|
+
return SkillPolicy()
|
|
252
|
+
scripts = payload.get("scripts") or {}
|
|
253
|
+
return SkillPolicy(
|
|
254
|
+
allowed_tools=tuple(str(item) for item in payload.get("allowed_tools") or []),
|
|
255
|
+
permissions=tuple(str(item) for item in payload.get("permissions") or []),
|
|
256
|
+
agents=tuple(str(item) for item in payload.get("agents") or []),
|
|
257
|
+
script_execution=str(scripts.get("execution") or "approval"),
|
|
258
|
+
script_network=bool(scripts.get("network", False)),
|
|
259
|
+
script_workspace_write=bool(scripts.get("workspace_write", False)),
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _parse_skill(
|
|
264
|
+
path: Path,
|
|
265
|
+
metadata: dict[str, str] | None = None,
|
|
266
|
+
locks: dict[str, str] | None = None,
|
|
267
|
+
) -> LoadedSkill | None:
|
|
268
|
+
try:
|
|
269
|
+
text = path.read_text(encoding="utf-8")
|
|
270
|
+
except OSError:
|
|
271
|
+
return None
|
|
272
|
+
if len(text) > _MAX_SKILL_CHARS or not text.startswith("---\n"):
|
|
273
|
+
return None
|
|
274
|
+
end = text.find("\n---\n", 4)
|
|
275
|
+
if end < 0:
|
|
276
|
+
return None
|
|
277
|
+
frontmatter = text[4:end].splitlines()
|
|
278
|
+
name = _frontmatter_value(frontmatter, "name")
|
|
279
|
+
description = _frontmatter_value(frontmatter, "description")
|
|
280
|
+
if not _NAME_RE.fullmatch(name) or not description:
|
|
281
|
+
return None
|
|
282
|
+
instructions = text[end + 5 :].strip()
|
|
283
|
+
if not instructions:
|
|
284
|
+
return None
|
|
285
|
+
metadata = metadata or {}
|
|
286
|
+
plugin_name = metadata.get("plugin_name") or "local"
|
|
287
|
+
if not _NAME_RE.fullmatch(plugin_name):
|
|
288
|
+
plugin_name = "local"
|
|
289
|
+
qualified_name = f"{plugin_name}:{name}"
|
|
290
|
+
content_sha256 = skill_tree_sha256(path.parent)
|
|
291
|
+
expected = (locks or {}).get(qualified_name)
|
|
292
|
+
integrity = "verified" if expected == content_sha256 else "mismatch" if expected else "unlocked"
|
|
293
|
+
return LoadedSkill(
|
|
294
|
+
name=name,
|
|
295
|
+
description=description,
|
|
296
|
+
instructions=instructions,
|
|
297
|
+
path=path,
|
|
298
|
+
plugin_name=plugin_name,
|
|
299
|
+
plugin_version=metadata.get("plugin_version", ""),
|
|
300
|
+
repository=metadata.get("repository", ""),
|
|
301
|
+
content_sha256=content_sha256,
|
|
302
|
+
integrity=integrity,
|
|
303
|
+
policy=_load_policy(path.parent),
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def discover_external_skills(roots: Sequence[Path] | None = None) -> list[LoadedSkill]:
|
|
308
|
+
found: dict[str, LoadedSkill] = {}
|
|
309
|
+
for raw_root in roots or default_skill_roots():
|
|
310
|
+
root = Path(raw_root).expanduser().resolve()
|
|
311
|
+
if not root.is_dir():
|
|
312
|
+
continue
|
|
313
|
+
mapping, locks = _catalog_metadata(root)
|
|
314
|
+
for candidate in sorted(root.glob("*/SKILL.md")):
|
|
315
|
+
loaded = _parse_skill(candidate, mapping.get(candidate.parent.resolve()), locks)
|
|
316
|
+
if loaded and loaded.qualified_name not in found:
|
|
317
|
+
found[loaded.qualified_name] = loaded
|
|
318
|
+
return list(found.values())
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _requested_skill_names(message: str) -> set[str]:
|
|
322
|
+
return set(
|
|
323
|
+
re.findall(
|
|
324
|
+
r"\$([a-z0-9][a-z0-9-]{0,63}(?::[a-z0-9][a-z0-9-]{0,63})?)\b",
|
|
325
|
+
message.lower(),
|
|
326
|
+
)
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _quoted_phrases(description: str) -> list[str]:
|
|
331
|
+
return [
|
|
332
|
+
next(group for group in match.groups() if group is not None).strip().lower()
|
|
333
|
+
for match in re.finditer(r'"([^"]+)"|“([^”]+)”|\'([^\']+)\'', description)
|
|
334
|
+
]
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _terms(text: str) -> set[str]:
|
|
338
|
+
low = text.lower()
|
|
339
|
+
latin = {
|
|
340
|
+
token for token in re.findall(r"[a-z0-9][a-z0-9_-]{2,}", low)
|
|
341
|
+
if token not in _LATIN_STOP_WORDS
|
|
342
|
+
}
|
|
343
|
+
cjk: set[str] = set()
|
|
344
|
+
for chunk in re.findall(r"[\u3400-\u9fff]{2,}", low):
|
|
345
|
+
if len(chunk) <= 8:
|
|
346
|
+
cjk.add(chunk)
|
|
347
|
+
for size in (2, 3, 4):
|
|
348
|
+
cjk.update(chunk[index : index + size] for index in range(len(chunk) - size + 1))
|
|
349
|
+
cjk -= _CJK_STOP_WORDS
|
|
350
|
+
# An n-gram that merely *contains* a stop word is usually noise too:
|
|
351
|
+
# "\u5e2e\u6211\u8bbe\u8ba1" is \u5e2e\u6211 + \u8bbe\u8ba1 with no extra signal, yet it scores as its own
|
|
352
|
+
# distinct match. Drop windows that are a stop word plus at most one
|
|
353
|
+
# character, which is where that inflation comes from.
|
|
354
|
+
cjk = {
|
|
355
|
+
gram for gram in cjk
|
|
356
|
+
if not any(
|
|
357
|
+
stop in gram and len(gram) - len(stop) <= 1
|
|
358
|
+
for stop in _CJK_STOP_WORDS
|
|
359
|
+
)
|
|
360
|
+
}
|
|
361
|
+
return latin | cjk
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
def _match_score(message: str, skill: LoadedSkill) -> tuple[float, str]:
|
|
365
|
+
low = message.lower()
|
|
366
|
+
for phrase in _quoted_phrases(skill.description):
|
|
367
|
+
if len(phrase) >= 2 and phrase in low:
|
|
368
|
+
return 20.0 + min(len(phrase), 20) / 10, f'description phrase "{phrase}"'
|
|
369
|
+
overlap = _terms(message) & _terms(skill.description)
|
|
370
|
+
if not overlap:
|
|
371
|
+
return 0.0, "no metadata overlap"
|
|
372
|
+
score = float(len(overlap))
|
|
373
|
+
if len(overlap) >= 2:
|
|
374
|
+
score += 2.0
|
|
375
|
+
return score, f"metadata overlap: {', '.join(sorted(overlap)[:6])}"
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def activate_external_skills(
|
|
379
|
+
message: str,
|
|
380
|
+
skills: Iterable[LoadedSkill] | None = None,
|
|
381
|
+
max_skills: int = 2,
|
|
382
|
+
record: bool = True,
|
|
383
|
+
) -> SkillActivation:
|
|
384
|
+
available = list(skills if skills is not None else discover_external_skills())
|
|
385
|
+
requested = _requested_skill_names(message)
|
|
386
|
+
by_short_name: dict[str, list[LoadedSkill]] = {}
|
|
387
|
+
for skill in available:
|
|
388
|
+
by_short_name.setdefault(skill.name, []).append(skill)
|
|
389
|
+
|
|
390
|
+
candidates: list[tuple[float, str, LoadedSkill]] = []
|
|
391
|
+
traces: list[SkillActivationTrace] = []
|
|
392
|
+
for skill in available:
|
|
393
|
+
explicit_qualified = skill.qualified_name in requested
|
|
394
|
+
explicit_short = skill.name in requested and len(by_short_name[skill.name]) == 1
|
|
395
|
+
if explicit_qualified or explicit_short:
|
|
396
|
+
score, reason = 100.0, "explicit invocation"
|
|
397
|
+
else:
|
|
398
|
+
score, reason = _match_score(message, skill)
|
|
399
|
+
if score < 4.0:
|
|
400
|
+
continue
|
|
401
|
+
if skill.integrity == "mismatch":
|
|
402
|
+
traces.append(SkillActivationTrace(
|
|
403
|
+
qualified_name=skill.qualified_name,
|
|
404
|
+
activated=False,
|
|
405
|
+
reason="integrity mismatch",
|
|
406
|
+
score=score,
|
|
407
|
+
integrity=skill.integrity,
|
|
408
|
+
source=str(skill.path),
|
|
409
|
+
))
|
|
410
|
+
continue
|
|
411
|
+
if skill.integrity == "unlocked" and not (explicit_qualified or explicit_short):
|
|
412
|
+
traces.append(SkillActivationTrace(
|
|
413
|
+
qualified_name=skill.qualified_name,
|
|
414
|
+
activated=False,
|
|
415
|
+
reason="automatic activation requires an integrity lock",
|
|
416
|
+
score=score,
|
|
417
|
+
integrity=skill.integrity,
|
|
418
|
+
source=str(skill.path),
|
|
419
|
+
))
|
|
420
|
+
continue
|
|
421
|
+
candidates.append((score, reason, skill))
|
|
422
|
+
|
|
423
|
+
candidates.sort(key=lambda item: (-item[0], item[2].qualified_name))
|
|
424
|
+
selected = candidates[: max(1, max_skills)]
|
|
425
|
+
for score, reason, skill in selected:
|
|
426
|
+
traces.append(SkillActivationTrace(
|
|
427
|
+
qualified_name=skill.qualified_name,
|
|
428
|
+
activated=True,
|
|
429
|
+
reason=reason,
|
|
430
|
+
score=round(score, 2),
|
|
431
|
+
integrity=skill.integrity,
|
|
432
|
+
source=str(skill.path),
|
|
433
|
+
))
|
|
434
|
+
if record:
|
|
435
|
+
_ACTIVATION_HISTORY.extend(traces)
|
|
436
|
+
return SkillActivation(
|
|
437
|
+
skills=tuple(item[2] for item in selected),
|
|
438
|
+
traces=tuple(traces),
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
|
|
442
|
+
def select_external_skills(
|
|
443
|
+
message: str,
|
|
444
|
+
skills: Iterable[LoadedSkill] | None = None,
|
|
445
|
+
) -> list[LoadedSkill]:
|
|
446
|
+
return list(activate_external_skills(message, skills).skills)
|
|
447
|
+
|
|
448
|
+
|
|
449
|
+
def build_skill_prompt_block(message: str) -> str:
|
|
450
|
+
return activate_external_skills(message).prompt_block
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def recent_skill_activation_traces(limit: int = 20) -> list[SkillActivationTrace]:
|
|
454
|
+
bounded = max(1, min(int(limit), len(_ACTIVATION_HISTORY) or 1))
|
|
455
|
+
return list(_ACTIVATION_HISTORY)[-bounded:]
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Skill manifests for model-facing workflows."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
from aria_code.packages.aria_core import CapabilityManifest, PermissionLevel, ServiceKind
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass(frozen=True)
|
|
12
|
+
class SkillSpec:
|
|
13
|
+
name: str
|
|
14
|
+
description: str
|
|
15
|
+
tools: List[str] = field(default_factory=list)
|
|
16
|
+
agents: List[str] = field(default_factory=list)
|
|
17
|
+
permissions: List[PermissionLevel] = field(default_factory=list)
|
|
18
|
+
|
|
19
|
+
def manifest(self) -> CapabilityManifest:
|
|
20
|
+
return CapabilityManifest(
|
|
21
|
+
name=self.name,
|
|
22
|
+
kind=ServiceKind.SKILL,
|
|
23
|
+
description=self.description,
|
|
24
|
+
capabilities=[f"tool:{name}" for name in self.tools] + [f"agent:{name}" for name in self.agents],
|
|
25
|
+
permissions=self.permissions,
|
|
26
|
+
required_services=self.tools + self.agents,
|
|
27
|
+
tags=["skill"],
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def builtin_skill_specs() -> List[SkillSpec]:
|
|
32
|
+
return [
|
|
33
|
+
SkillSpec(
|
|
34
|
+
name="financial-research",
|
|
35
|
+
description="Multi-source quote, fundamentals, technical analysis, and narrative report generation.",
|
|
36
|
+
tools=["get_market_data", "get_market_history", "get_risk_metrics", "analyze_news"],
|
|
37
|
+
agents=["technical", "fundamental", "macro", "risk", "synthesis"],
|
|
38
|
+
permissions=[PermissionLevel.NETWORK],
|
|
39
|
+
),
|
|
40
|
+
SkillSpec(
|
|
41
|
+
name="equity-research-report",
|
|
42
|
+
description=(
|
|
43
|
+
"Evidence-backed equity report workflow with specialist agents, "
|
|
44
|
+
"deterministic fallbacks, provenance, critique, and completion gates."
|
|
45
|
+
),
|
|
46
|
+
tools=[
|
|
47
|
+
"get_market_data",
|
|
48
|
+
"get_market_history",
|
|
49
|
+
"calculate_factors",
|
|
50
|
+
"get_risk_metrics",
|
|
51
|
+
"analyze_news",
|
|
52
|
+
],
|
|
53
|
+
agents=[
|
|
54
|
+
"technical",
|
|
55
|
+
"fundamental",
|
|
56
|
+
"macro",
|
|
57
|
+
"risk",
|
|
58
|
+
"news",
|
|
59
|
+
"catalyst",
|
|
60
|
+
"sector",
|
|
61
|
+
"synthesis",
|
|
62
|
+
],
|
|
63
|
+
permissions=[PermissionLevel.NETWORK, PermissionLevel.WORKSPACE_WRITE],
|
|
64
|
+
),
|
|
65
|
+
SkillSpec(
|
|
66
|
+
name="strategy-backtest",
|
|
67
|
+
description="Generate and validate trading strategies with historical simulation and artifacts.",
|
|
68
|
+
tools=["get_market_data"],
|
|
69
|
+
agents=["technical", "risk"],
|
|
70
|
+
permissions=[PermissionLevel.NETWORK, PermissionLevel.WORKSPACE_WRITE],
|
|
71
|
+
),
|
|
72
|
+
SkillSpec(
|
|
73
|
+
name="workspace-coding",
|
|
74
|
+
description="Read, edit, and verify local project files with explicit workspace permissions.",
|
|
75
|
+
tools=["read_file", "list_files", "search_code", "edit_file", "write_file", "run_command"],
|
|
76
|
+
permissions=[PermissionLevel.READ_ONLY, PermissionLevel.WORKSPACE_WRITE],
|
|
77
|
+
),
|
|
78
|
+
SkillSpec(
|
|
79
|
+
name="broker-portfolio",
|
|
80
|
+
description="Read broker account state and propose guarded orders with user approval.",
|
|
81
|
+
tools=["broker_query", "broker_order"],
|
|
82
|
+
permissions=[PermissionLevel.BROKER_READ, PermissionLevel.BROKER_TRADE],
|
|
83
|
+
),
|
|
84
|
+
]
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""Financial tools package."""
|
|
2
|
+
|
|
3
|
+
from .factors import run_factor_research
|
|
4
|
+
from .risk_tools import run_risk_profile
|
|
5
|
+
from .compliance import run_compliance_audit
|
|
6
|
+
from .strategy import validate_strategy_spec
|
|
7
|
+
|
|
8
|
+
__all__ = ["run_factor_research", "run_risk_profile", "run_compliance_audit", "validate_strategy_spec"]
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""定位 aria-skills 脚本与 Arthera SDK —— 替掉四个工具文件里写死的绝对路径。
|
|
2
|
+
|
|
3
|
+
2026-08-19:本目录下 factors / risk_tools / strategy / compliance 四个文件
|
|
4
|
+
各自硬编码了开发者本机的绝对路径 —— 指向 aria-skills 的脚本,以及 Arthera
|
|
5
|
+
SDK 的安装位置。aria-code 是公开仓库,这带来两个问题:
|
|
6
|
+
|
|
7
|
+
1. 功能上,任何别人 clone 下来这些路径都不存在,工具静默降级成
|
|
8
|
+
"Error: 脚本不存在",而错误信息里还打印出一个跟对方毫无关系的路径。
|
|
9
|
+
2. 信息上,公开仓库里出现开发者本机用户名,以及一个私有仓库
|
|
10
|
+
(Arthera) 的存在与目录结构。
|
|
11
|
+
|
|
12
|
+
skill 那边其实早就有一套正规的发现顺序 —— packages/aria_skills/loader.py
|
|
13
|
+
的 default_skill_roots()(ARIA_SKILLS_PATH 环境变量 → 同级 aria-skills
|
|
14
|
+
checkout → ~/.arthera/skills → ~/.claude/skills → …)。这里直接复用它,
|
|
15
|
+
而不是再写第二套查找逻辑,免得两处顺序不一致时出现"CLI 能发现这个 skill、
|
|
16
|
+
工具却找不到它的脚本"。
|
|
17
|
+
|
|
18
|
+
Arthera SDK 是私有仓库的东西,公开仓库无从假设它在哪,因此只认显式的
|
|
19
|
+
ARTHERA_SDK_PATH,外加一个同级 checkout 的兜底;找不到就安静跳过 —— 这些
|
|
20
|
+
工具本来就有 mcp_tool 的 ImportError 兜底分支,SDK 缺失不该是致命错误。
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import os
|
|
26
|
+
import sys
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
from aria_code.packages.aria_core.paths import aria_home
|
|
29
|
+
|
|
30
|
+
__all__ = ["find_skill_script", "missing_script_message", "ensure_arthera_sdk", "ensure_arthera_root"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _skill_roots() -> list[Path]:
|
|
34
|
+
"""优先复用 skill loader 的发现顺序;它不可用时退化为等价的最小实现。"""
|
|
35
|
+
try:
|
|
36
|
+
from packages.aria_skills.loader import default_skill_roots
|
|
37
|
+
|
|
38
|
+
return [Path(p) for p in default_skill_roots()]
|
|
39
|
+
except Exception:
|
|
40
|
+
roots: list[Path] = []
|
|
41
|
+
configured = os.getenv("ARIA_SKILLS_PATH", "")
|
|
42
|
+
roots.extend(
|
|
43
|
+
Path(item).expanduser() for item in configured.split(os.pathsep) if item.strip()
|
|
44
|
+
)
|
|
45
|
+
repository = Path(__file__).resolve().parents[3]
|
|
46
|
+
roots.extend([
|
|
47
|
+
repository.parent / "aria-skills" / "skills",
|
|
48
|
+
aria_home() / "skills",
|
|
49
|
+
Path.home() / ".aria" / "skills",
|
|
50
|
+
Path.home() / ".claude" / "skills",
|
|
51
|
+
])
|
|
52
|
+
return roots
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def find_skill_script(skill_name: str, script_name: str) -> Path | None:
|
|
56
|
+
"""返回 <某个 skill 根>/<skill_name>/scripts/<script_name>,找不到返回 None。
|
|
57
|
+
|
|
58
|
+
调用方负责把 None 变成对用户有意义的提示——这里不抛异常,因为"本机没装
|
|
59
|
+
这个 skill 目录"是完全正常的部署形态,不是错误。
|
|
60
|
+
"""
|
|
61
|
+
for root in _skill_roots():
|
|
62
|
+
candidate = root / skill_name / "scripts" / script_name
|
|
63
|
+
try:
|
|
64
|
+
if candidate.is_file():
|
|
65
|
+
return candidate
|
|
66
|
+
except OSError:
|
|
67
|
+
continue
|
|
68
|
+
return None
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def missing_script_message(skill_name: str, script_name: str) -> str:
|
|
72
|
+
"""脚本缺失时给出可操作的提示,而不是打印一条别人机器上不存在的路径。"""
|
|
73
|
+
return (
|
|
74
|
+
f"Error: 找不到 {skill_name}/scripts/{script_name}。"
|
|
75
|
+
" 请获取 aria-skills 目录,并用 ARIA_SKILLS_PATH 指向其中的 skills/,"
|
|
76
|
+
"或把它放在 aria-code 的同级目录下。"
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def ensure_arthera_sdk() -> bool:
|
|
81
|
+
"""把 Arthera SDK 加进 sys.path。返回是否找到。
|
|
82
|
+
|
|
83
|
+
只认显式配置 + 同级 checkout:Arthera 是私有仓库,公开仓库不该假设它
|
|
84
|
+
存在于某个固定绝对路径。
|
|
85
|
+
"""
|
|
86
|
+
candidates: list[Path] = []
|
|
87
|
+
configured = os.getenv("ARTHERA_SDK_PATH", "")
|
|
88
|
+
if configured.strip():
|
|
89
|
+
candidates.append(Path(configured).expanduser())
|
|
90
|
+
arthera_root = os.getenv("ARTHERA_PATH", "")
|
|
91
|
+
if arthera_root.strip():
|
|
92
|
+
candidates.append(Path(arthera_root).expanduser() / "sdks" / "python")
|
|
93
|
+
candidates.append(Path(__file__).resolve().parents[3].parent / "Arthera" / "sdks" / "python")
|
|
94
|
+
|
|
95
|
+
for candidate in candidates:
|
|
96
|
+
try:
|
|
97
|
+
if candidate.is_dir():
|
|
98
|
+
path_str = str(candidate)
|
|
99
|
+
if path_str not in sys.path:
|
|
100
|
+
sys.path.append(path_str)
|
|
101
|
+
return True
|
|
102
|
+
except OSError:
|
|
103
|
+
continue
|
|
104
|
+
return False
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def ensure_arthera_root() -> bool:
|
|
108
|
+
"""把 Arthera 仓库根目录加进 sys.path,让 ``packages.*`` 可导入。返回是否找到。
|
|
109
|
+
|
|
110
|
+
跟 ``ensure_arthera_sdk`` 同样的理由:Arthera 是私有仓库,公开仓库不该假设
|
|
111
|
+
它在某个写死的绝对路径上。这里认的是 ``ARTHERA_ROOT``(doctor.py 与
|
|
112
|
+
backtest_cmds.py 已经在用的同一个变量),外加一个同级 checkout 的兜底。
|
|
113
|
+
"""
|
|
114
|
+
candidates: list[Path] = []
|
|
115
|
+
for var in ("ARTHERA_ROOT", "ARTHERA_PATH"):
|
|
116
|
+
configured = os.getenv(var, "")
|
|
117
|
+
if configured.strip():
|
|
118
|
+
candidates.append(Path(configured).expanduser())
|
|
119
|
+
candidates.append(Path(__file__).resolve().parents[4].parent / "Arthera")
|
|
120
|
+
|
|
121
|
+
for candidate in candidates:
|
|
122
|
+
try:
|
|
123
|
+
if candidate.is_dir():
|
|
124
|
+
path_str = str(candidate)
|
|
125
|
+
if path_str not in sys.path:
|
|
126
|
+
sys.path.insert(0, path_str)
|
|
127
|
+
return True
|
|
128
|
+
except OSError:
|
|
129
|
+
continue
|
|
130
|
+
return False
|