aria-code 0.167.0__py3-none-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- aria_bots/__init__.py +7 -0
- aria_bots/channels/README.md +97 -0
- aria_bots/channels/__init__.py +1 -0
- aria_bots/channels/approvals.py +263 -0
- aria_bots/channels/conversation.py +280 -0
- aria_bots/channels/digest.py +186 -0
- aria_bots/channels/registry.py +95 -0
- aria_bots/engine.py +144 -0
- aria_bots/feishu_bot.py +1385 -0
- aria_bots/logistics/__init__.py +1 -0
- aria_bots/logistics/carriers.py +253 -0
- aria_bots/logistics/freight.py +154 -0
- aria_bots/logistics/inventory.py +242 -0
- aria_bots/logistics/mcp_server.py +156 -0
- aria_bots/logistics/records.py +119 -0
- aria_bots/logistics/tenancy.py +152 -0
- aria_bots/market.py +91 -0
- aria_bots/relay_client.py +326 -0
- aria_bots/telegram_bot.py +299 -0
- aria_code/__init__.py +111 -0
- aria_code/_bin/aria-native.exe +0 -0
- aria_code/_bin/aria-runtime.exe +0 -0
- aria_code/_version.py +20 -0
- aria_code/account_memory.py +296 -0
- aria_code/account_session.py +166 -0
- aria_code/adk_apps/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/__init__.py +1 -0
- aria_code/adk_apps/aria_code_review/agent.py +32 -0
- aria_code/adk_apps/aria_research/__init__.py +1 -0
- aria_code/adk_apps/aria_research/agent.py +31 -0
- aria_code/agents/__init__.py +49 -0
- aria_code/agents/base.py +329 -0
- aria_code/agents/code_review.py +200 -0
- aria_code/agents/deep/__init__.py +37 -0
- aria_code/agents/deep/calibration_loop.py +144 -0
- aria_code/agents/deep/critic.py +125 -0
- aria_code/agents/deep/deepen.py +193 -0
- aria_code/agents/deep/models.py +149 -0
- aria_code/agents/deep/pipeline.py +164 -0
- aria_code/agents/deep/quant_fusion.py +192 -0
- aria_code/agents/deep/themes.py +95 -0
- aria_code/agents/deep/tiers.py +106 -0
- aria_code/agents/engineering/__init__.py +14 -0
- aria_code/agents/engineering/coder.py +222 -0
- aria_code/agents/engineering/tester.py +145 -0
- aria_code/agents/financial/__init__.py +21 -0
- aria_code/agents/financial/cashflow_burn.py +119 -0
- aria_code/agents/financial/catalyst.py +279 -0
- aria_code/agents/financial/corporate_finance.py +175 -0
- aria_code/agents/financial/debate.py +224 -0
- aria_code/agents/financial/earnings.py +315 -0
- aria_code/agents/financial/fundamental.py +178 -0
- aria_code/agents/financial/macro.py +111 -0
- aria_code/agents/financial/news.py +231 -0
- aria_code/agents/financial/risk.py +146 -0
- aria_code/agents/financial/sector.py +279 -0
- aria_code/agents/financial/strategist.py +121 -0
- aria_code/agents/financial/stripe_revenue.py +127 -0
- aria_code/agents/financial/synthesis.py +274 -0
- aria_code/agents/financial/technical.py +277 -0
- aria_code/agents/orchestrator.py +44 -0
- aria_code/agents/portfolio_agent.py +392 -0
- aria_code/agents/realty/__init__.py +62 -0
- aria_code/agents/realty/asset_diagnosis.py +152 -0
- aria_code/agents/realty/business_match.py +166 -0
- aria_code/agents/realty/cashflow_verify.py +209 -0
- aria_code/agents/realty/contract_rules.py +210 -0
- aria_code/agents/realty/energy_anomaly.py +189 -0
- aria_code/agents/realty/exit_settlement.py +208 -0
- aria_code/agents/realty/fulfillment_risk.py +206 -0
- aria_code/agents/realty/ops_optimize.py +160 -0
- aria_code/agents/realty/revenue_share.py +215 -0
- aria_code/agents/registry.py +168 -0
- aria_code/agents/signal_scheme.py +111 -0
- aria_code/agents/sports/__init__.py +0 -0
- aria_code/agents/sports/football_agent.py +197 -0
- aria_code/agents/supervisor.py +113 -0
- aria_code/agents/team.py +500 -0
- aria_code/agents/video/__init__.py +16 -0
- aria_code/agents/video/director.py +376 -0
- aria_code/agents/video/qa_agent.py +143 -0
- aria_code/agents/video/script_doctor.py +112 -0
- aria_code/agents/warehouse/__init__.py +25 -0
- aria_code/agents/warehouse/contracts.py +26 -0
- aria_code/agents/warehouse/dashboard.py +155 -0
- aria_code/agents/warehouse/fulfillment_leadtime.py +95 -0
- aria_code/agents/warehouse/inbound_exceptions.py +25 -0
- aria_code/agents/warehouse/inventory_health.py +24 -0
- aria_code/agents/warehouse/local_run.py +71 -0
- aria_code/agents/warehouse/local_snapshot.py +132 -0
- aria_code/agents/warehouse/logistics_cost.py +84 -0
- aria_code/agents/warehouse/logistics_sync.py +21 -0
- aria_code/agents/warehouse/navigation.schema.json +110 -0
- aria_code/agents/warehouse/task_agent.py +172 -0
- aria_code/agents/warehouse/workflow.py +31 -0
- aria_code/aliyun_data_client.py +4 -0
- aria_code/apps/README.md +12 -0
- aria_code/apps/__init__.py +2 -0
- aria_code/apps/channels/README.md +9 -0
- aria_code/apps/channels/__init__.py +17 -0
- aria_code/apps/channels/approvals.py +11 -0
- aria_code/apps/channels/conversation.py +11 -0
- aria_code/apps/channels/digest.py +11 -0
- aria_code/apps/channels/intake.py +92 -0
- aria_code/apps/channels/registry.py +11 -0
- aria_code/apps/channels/tradingview.py +94 -0
- aria_code/apps/cli/README.md +13 -0
- aria_code/apps/cli/__init__.py +2 -0
- aria_code/apps/cli/_optional.py +120 -0
- aria_code/apps/cli/app_server.py +656 -0
- aria_code/apps/cli/auto_update.py +125 -0
- aria_code/apps/cli/bootstrap.py +206 -0
- aria_code/apps/cli/broker_render.py +206 -0
- aria_code/apps/cli/chat_turn.py +1327 -0
- aria_code/apps/cli/cloud_models.py +231 -0
- aria_code/apps/cli/codegen_paths.py +29 -0
- aria_code/apps/cli/commands/__init__.py +34 -0
- aria_code/apps/cli/commands/_business_render.py +78 -0
- aria_code/apps/cli/commands/_ui.py +71 -0
- aria_code/apps/cli/commands/analysis_cmds.py +378 -0
- aria_code/apps/cli/commands/ashare_prediction_cmds.py +327 -0
- aria_code/apps/cli/commands/auth_cmds.py +215 -0
- aria_code/apps/cli/commands/backtest_cmds.py +2518 -0
- aria_code/apps/cli/commands/broker_cmds.py +1236 -0
- aria_code/apps/cli/commands/business_workflow_cmds.py +379 -0
- aria_code/apps/cli/commands/canvas_cmds.py +77 -0
- aria_code/apps/cli/commands/catalog.py +189 -0
- aria_code/apps/cli/commands/core_cmds.py +2748 -0
- aria_code/apps/cli/commands/data_cmds.py +521 -0
- aria_code/apps/cli/commands/diagnostic_cmds.py +285 -0
- aria_code/apps/cli/commands/diagnostic_ops_cmds.py +813 -0
- aria_code/apps/cli/commands/engineering_cmds.py +399 -0
- aria_code/apps/cli/commands/file_cmds.py +312 -0
- aria_code/apps/cli/commands/finance_service_cmds.py +60 -0
- aria_code/apps/cli/commands/fx_commodity_cmds.py +123 -0
- aria_code/apps/cli/commands/market.py +544 -0
- aria_code/apps/cli/commands/market_cmds.py +1419 -0
- aria_code/apps/cli/commands/market_context.py +432 -0
- aria_code/apps/cli/commands/market_render.py +7 -0
- aria_code/apps/cli/commands/model_cmds.py +2374 -0
- aria_code/apps/cli/commands/ops_cmds.py +721 -0
- aria_code/apps/cli/commands/orchestrator_cmds.py +181 -0
- aria_code/apps/cli/commands/pdf_export_cmds.py +125 -0
- aria_code/apps/cli/commands/portfolio_cmds.py +1316 -0
- aria_code/apps/cli/commands/report.py +476 -0
- aria_code/apps/cli/commands/scaffold_templates.py +617 -0
- aria_code/apps/cli/commands/session_cmds.py +236 -0
- aria_code/apps/cli/commands/session_ux_cmds.py +359 -0
- aria_code/apps/cli/commands/team.py +723 -0
- aria_code/apps/cli/commands/team_render.py +8 -0
- aria_code/apps/cli/commands/ui_cmds.py +401 -0
- aria_code/apps/cli/commands/warehouse_cmds.py +297 -0
- aria_code/apps/cli/commands/workflow_cmds.py +629 -0
- aria_code/apps/cli/commands/workspace_cmds.py +1543 -0
- aria_code/apps/cli/config_paths.py +73 -0
- aria_code/apps/cli/config_store.py +158 -0
- aria_code/apps/cli/constants.py +88 -0
- aria_code/apps/cli/context.py +34 -0
- aria_code/apps/cli/conversation_policy.py +53 -0
- aria_code/apps/cli/deterministic.py +200 -0
- aria_code/apps/cli/direct.py +48 -0
- aria_code/apps/cli/earnings_view.py +172 -0
- aria_code/apps/cli/endpoint_migration.py +64 -0
- aria_code/apps/cli/exec_events.py +255 -0
- aria_code/apps/cli/file_references.py +155 -0
- aria_code/apps/cli/finance_service_catalog.py +111 -0
- aria_code/apps/cli/football_reports.py +231 -0
- aria_code/apps/cli/github_app_auth.py +135 -0
- aria_code/apps/cli/google_login.py +239 -0
- aria_code/apps/cli/handlers/__init__.py +11 -0
- aria_code/apps/cli/handlers/broker_handlers.py +122 -0
- aria_code/apps/cli/handlers/chart_handlers.py +1314 -0
- aria_code/apps/cli/handlers/market_handlers.py +2570 -0
- aria_code/apps/cli/handlers/realty_handlers.py +114 -0
- aria_code/apps/cli/handlers/sports_handlers.py +22 -0
- aria_code/apps/cli/handlers/strategy_advice.py +82 -0
- aria_code/apps/cli/headless.py +352 -0
- aria_code/apps/cli/helpers.py +326 -0
- aria_code/apps/cli/hooks.py +180 -0
- aria_code/apps/cli/i18n.py +310 -0
- aria_code/apps/cli/intent.py +136 -0
- aria_code/apps/cli/intent_router.py +305 -0
- aria_code/apps/cli/intent_shadow.py +72 -0
- aria_code/apps/cli/intent_signals.py +347 -0
- aria_code/apps/cli/lifecycle_hooks.py +48 -0
- aria_code/apps/cli/local_tool_schemas.py +550 -0
- aria_code/apps/cli/main.py +39 -0
- aria_code/apps/cli/market_hours.py +100 -0
- aria_code/apps/cli/market_metadata.py +135 -0
- aria_code/apps/cli/market_universe.py +487 -0
- aria_code/apps/cli/message_processing.py +310 -0
- aria_code/apps/cli/mixin_binding.py +32 -0
- aria_code/apps/cli/model_catalog.py +652 -0
- aria_code/apps/cli/model_probe.py +169 -0
- aria_code/apps/cli/model_retirement.py +65 -0
- aria_code/apps/cli/native_branding.py +12 -0
- aria_code/apps/cli/native_bridge.py +118 -0
- aria_code/apps/cli/native_entry.py +58 -0
- aria_code/apps/cli/pdf_report.py +977 -0
- aria_code/apps/cli/plan_mode.py +139 -0
- aria_code/apps/cli/plotly_html.py +15 -0
- aria_code/apps/cli/prediction_feedback.py +202 -0
- aria_code/apps/cli/preflight.py +502 -0
- aria_code/apps/cli/project_aria.py +60 -0
- aria_code/apps/cli/prompt_assembly.py +78 -0
- aria_code/apps/cli/prompts/__init__.py +0 -0
- aria_code/apps/cli/prompts/coding.py +71 -0
- aria_code/apps/cli/prompts/select.py +193 -0
- aria_code/apps/cli/prompts/system_prompts.py +562 -0
- aria_code/apps/cli/prompts/ui.py +436 -0
- aria_code/apps/cli/provider_endpoints.py +237 -0
- aria_code/apps/cli/provider_keys.py +73 -0
- aria_code/apps/cli/providers/__init__.py +1 -0
- aria_code/apps/cli/providers/base.py +775 -0
- aria_code/apps/cli/providers/chat_routing.py +139 -0
- aria_code/apps/cli/providers/collaboration.py +98 -0
- aria_code/apps/cli/providers/llm/__init__.py +1 -0
- aria_code/apps/cli/providers/llm/offline_responses.py +68 -0
- aria_code/apps/cli/providers/llm/ollama_stream.py +1574 -0
- aria_code/apps/cli/providers/llm/response_cache.py +60 -0
- aria_code/apps/cli/providers/llm/sse_stream.py +298 -0
- aria_code/apps/cli/providers/runtime_bridge.py +586 -0
- aria_code/apps/cli/providers/vertexai_stream.py +592 -0
- aria_code/apps/cli/release_download.py +159 -0
- aria_code/apps/cli/review_runner.py +111 -0
- aria_code/apps/cli/runtime_consumer.py +769 -0
- aria_code/apps/cli/sandbox.py +32 -0
- aria_code/apps/cli/session_export.py +120 -0
- aria_code/apps/cli/session_jsonl.py +208 -0
- aria_code/apps/cli/session_refresh.py +123 -0
- aria_code/apps/cli/session_store.py +125 -0
- aria_code/apps/cli/skills_catalog.py +273 -0
- aria_code/apps/cli/task_isolation.py +211 -0
- aria_code/apps/cli/terminal_chrome.py +412 -0
- aria_code/apps/cli/todo_tracker.py +190 -0
- aria_code/apps/cli/tool_executor.py +932 -0
- aria_code/apps/cli/tool_registry.py +34 -0
- aria_code/apps/cli/tool_scope.py +113 -0
- aria_code/apps/cli/tools/__init__.py +40 -0
- aria_code/apps/cli/tools/context.py +46 -0
- aria_code/apps/cli/tools/file_tools.py +181 -0
- aria_code/apps/cli/tools/market_tools.py +626 -0
- aria_code/apps/cli/tools/notebook_tools.py +111 -0
- aria_code/apps/cli/tools/system_tools.py +656 -0
- aria_code/apps/cli/tools/write_tools.py +961 -0
- aria_code/apps/cli/tradingview_bridge.py +529 -0
- aria_code/apps/cli/transactions.py +269 -0
- aria_code/apps/cli/turn_planning.py +137 -0
- aria_code/apps/cli/update_check.py +257 -0
- aria_code/apps/cli/updater.py +373 -0
- aria_code/apps/cli/utils/__init__.py +0 -0
- aria_code/apps/cli/utils/market_detect.py +1663 -0
- aria_code/apps/cli/web_login.py +136 -0
- aria_code/apps/cli/workflow_runner.py +127 -0
- aria_code/apps/cli/workspace_route.py +121 -0
- aria_code/apps/daemon/README.md +14 -0
- aria_code/apps/vscode/README.md +115 -0
- aria_code/apps/vscode/package.json +70 -0
- aria_code/aria_cli.py +5244 -0
- aria_code/aria_daemon.py +1372 -0
- aria_code/aria_feishu_bot.py +16 -0
- aria_code/aria_mcp_server.py +61 -0
- aria_code/aria_relay_client.py +4 -0
- aria_code/aria_telegram_bot.py +11 -0
- aria_code/ariarc.py +385 -0
- aria_code/artifacts.py +581 -0
- aria_code/backtest_engine.py +4 -0
- aria_code/backtest_report.py +4 -0
- aria_code/brokers/__init__.py +78 -0
- aria_code/brokers/automation.py +193 -0
- aria_code/brokers/base.py +231 -0
- aria_code/brokers/capabilities.py +264 -0
- aria_code/brokers/cn/__init__.py +10 -0
- aria_code/brokers/cn/easytrader_broker.py +193 -0
- aria_code/brokers/cn/futu_broker.py +194 -0
- aria_code/brokers/cn/longbridge_broker.py +190 -0
- aria_code/brokers/cn/tiger_broker.py +196 -0
- aria_code/brokers/cn/xtquant_broker.py +225 -0
- aria_code/brokers/config.py +414 -0
- aria_code/brokers/intl/__init__.py +5 -0
- aria_code/brokers/intl/alpaca_broker.py +183 -0
- aria_code/brokers/intl/ibkr_broker.py +215 -0
- aria_code/brokers/intl/trading212_broker.py +183 -0
- aria_code/brokers/intl/webull_broker.py +156 -0
- aria_code/brokers/paper_broker.py +266 -0
- aria_code/brokers/planning.py +296 -0
- aria_code/brokers/registry.py +213 -0
- aria_code/brokers/trading.py +267 -0
- aria_code/canva_client.py +4 -0
- aria_code/change_store.py +127 -0
- aria_code/clients/__init__.py +0 -0
- aria_code/clients/aliyun_data_client.py +660 -0
- aria_code/clients/aria_relay_client.py +16 -0
- aria_code/clients/canva_client.py +284 -0
- aria_code/clients/figma_client.py +100 -0
- aria_code/clients/football_data_client.py +1671 -0
- aria_code/clients/kling_video_client.py +182 -0
- aria_code/clients/market_data_client.py +1985 -0
- aria_code/clients/openai_image_client.py +242 -0
- aria_code/clients/runway_video_client.py +157 -0
- aria_code/clients/warehouse_erp_client.py +105 -0
- aria_code/cloud_memory.py +190 -0
- aria_code/command_safety.py +19 -0
- aria_code/computer_use_server.py +276 -0
- aria_code/computer_use_tools.py +4 -0
- aria_code/dashboard_generator.py +579 -0
- aria_code/data_analysis_tools.py +4 -0
- aria_code/data_cleaner.py +483 -0
- aria_code/data_service.py +513 -0
- aria_code/datasources/__init__.py +23 -0
- aria_code/datasources/base.py +166 -0
- aria_code/datasources/router.py +253 -0
- aria_code/datasources/sec_statements.py +217 -0
- aria_code/datasources/sources/__init__.py +15 -0
- aria_code/datasources/sources/akshare_source.py +269 -0
- aria_code/datasources/sources/alpha_vantage_source.py +203 -0
- aria_code/datasources/sources/edgar_source.py +219 -0
- aria_code/datasources/sources/finnhub_source.py +198 -0
- aria_code/datasources/sources/fred_source.py +223 -0
- aria_code/datasources/sources/tushare_source.py +143 -0
- aria_code/datasources/sources/web_scraper_source.py +278 -0
- aria_code/datasources/sources/world_bank_source.py +206 -0
- aria_code/datasources/sources/yfinance_source.py +152 -0
- aria_code/doctor.py +860 -0
- aria_code/domain/__init__.py +0 -0
- aria_code/domain/_finance_core/__init__.py +5 -0
- aria_code/domain/_finance_core/backtest_engine.py +325 -0
- aria_code/domain/_finance_core/performance_stats.py +101 -0
- aria_code/domain/backtest_engine.py +75 -0
- aria_code/domain/backtest_report.py +488 -0
- aria_code/domain/finance_formulas.py +389 -0
- aria_code/domain/performance_stats.py +6 -0
- aria_code/domain/portfolio_ledger.py +307 -0
- aria_code/domain/strategy_vault.py +421 -0
- aria_code/evals/__init__.py +37 -0
- aria_code/evals/behavior.py +113 -0
- aria_code/evals/gap.py +93 -0
- aria_code/evals/harness.py +949 -0
- aria_code/evals/review_probe.py +132 -0
- aria_code/evals/runner.py +425 -0
- aria_code/evals/scorecard.py +322 -0
- aria_code/evals/summary.py +125 -0
- aria_code/external_agent_runner.py +115 -0
- aria_code/figma_client.py +4 -0
- aria_code/file_analysis_tools.py +4 -0
- aria_code/finance_formulas.py +4 -0
- aria_code/football_data_client.py +4 -0
- aria_code/image_gen_tools.py +4 -0
- aria_code/intent_classifier.py +459 -0
- aria_code/kling_video_client.py +4 -0
- aria_code/licensing.py +151 -0
- aria_code/local_finance_tools.py +4 -0
- aria_code/local_image_provider.py +4 -0
- aria_code/local_llm_provider.py +4 -0
- aria_code/macro_tools.py +4 -0
- aria_code/markdown_pdf.py +289 -0
- aria_code/market_data_client.py +4 -0
- aria_code/mcp_client.py +744 -0
- aria_code/memory_manager.py +273 -0
- aria_code/model_capability.py +512 -0
- aria_code/notification_tools.py +4 -0
- aria_code/openai_image_client.py +4 -0
- aria_code/packages/__init__.py +26 -0
- aria_code/packages/adk_bridge/__init__.py +11 -0
- aria_code/packages/adk_bridge/code_review_tools.py +49 -0
- aria_code/packages/adk_bridge/market_tools.py +125 -0
- aria_code/packages/aria_agents/__init__.py +5 -0
- aria_code/packages/aria_agents/manifest.py +78 -0
- aria_code/packages/aria_core/__init__.py +34 -0
- aria_code/packages/aria_core/architecture.py +269 -0
- aria_code/packages/aria_core/export.py +147 -0
- aria_code/packages/aria_core/manifest.py +65 -0
- aria_code/packages/aria_core/paths.py +57 -0
- aria_code/packages/aria_core/secure_file.py +68 -0
- aria_code/packages/aria_infra/__init__.py +15 -0
- aria_code/packages/aria_infra/arthera.py +58 -0
- aria_code/packages/aria_infra/doctor.py +246 -0
- aria_code/packages/aria_infra/product.py +40 -0
- aria_code/packages/aria_mcp/__init__.py +25 -0
- aria_code/packages/aria_mcp/bridge.py +232 -0
- aria_code/packages/aria_mcp/circuit.py +82 -0
- aria_code/packages/aria_mcp/config.py +162 -0
- aria_code/packages/aria_mcp/installer.py +95 -0
- aria_code/packages/aria_mcp/server.py +1705 -0
- aria_code/packages/aria_mcp/tools.py +81 -0
- aria_code/packages/aria_sdk/__init__.py +19 -0
- aria_code/packages/aria_sdk/client.py +393 -0
- aria_code/packages/aria_sdk/providers.py +70 -0
- aria_code/packages/aria_sdk/streaming.py +75 -0
- aria_code/packages/aria_sdk/types.py +86 -0
- aria_code/packages/aria_services/__init__.py +92 -0
- aria_code/packages/aria_services/cache.py +60 -0
- aria_code/packages/aria_services/context.py +356 -0
- aria_code/packages/aria_services/context_checkpoints.py +157 -0
- aria_code/packages/aria_services/data.py +11 -0
- aria_code/packages/aria_services/provider_health.py +304 -0
- aria_code/packages/aria_services/references.py +247 -0
- aria_code/packages/aria_services/registry.py +249 -0
- aria_code/packages/aria_services/research_protocol.py +137 -0
- aria_code/packages/aria_services/research_quality.py +186 -0
- aria_code/packages/aria_services/settings.py +160 -0
- aria_code/packages/aria_services/usage.py +151 -0
- aria_code/packages/aria_skills/__init__.py +46 -0
- aria_code/packages/aria_skills/catalog.py +149 -0
- aria_code/packages/aria_skills/loader.py +455 -0
- aria_code/packages/aria_skills/registry.py +84 -0
- aria_code/packages/aria_tools/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/__init__.py +5 -0
- aria_code/packages/aria_tools/financial/_paths.py +92 -0
- aria_code/packages/aria_tools/registry.py +129 -0
- aria_code/packages/quant_engine/__init__.py +33 -0
- aria_code/packages/quant_engine/_namespace.py +24 -0
- aria_code/packages/quant_engine/backtest/__init__.py +10 -0
- aria_code/packages/quant_engine/backtest/core.py +53 -0
- aria_code/packages/quant_engine/backtest/engine.py +102 -0
- aria_code/packages/quant_engine/portfolio/__init__.py +9 -0
- aria_code/packages/quant_engine/portfolio/optimizer.py +85 -0
- aria_code/packages/quant_engine/sports/__init__.py +76 -0
- aria_code/packages/quant_engine/sports/calibrator.py +354 -0
- aria_code/packages/quant_engine/sports/dixon_coles.py +234 -0
- aria_code/packages/quant_engine/sports/elo.py +300 -0
- aria_code/packages/quant_engine/sports/form.py +188 -0
- aria_code/packages/quant_engine/sports/h2h.py +195 -0
- aria_code/packages/quant_engine/sports/predictor.py +312 -0
- aria_code/packages/quant_engine/sports/tracker.py +665 -0
- aria_code/packages/quant_engine/stochastic/__init__.py +31 -0
- aria_code/packages/quant_engine/stochastic/gbm_enhanced.py +195 -0
- aria_code/packages/quant_engine/stochastic/ito_calculus.py +477 -0
- aria_code/packages/quant_engine/stochastic/kelly_criterion.py +181 -0
- aria_code/packages/quant_engine/stochastic/monte_carlo_advanced.py +95 -0
- aria_code/packages/quant_engine/stochastic/options_pricing.py +576 -0
- aria_code/packages/quant_engine/stochastic/stochastic_processes.py +90 -0
- aria_code/packs/__init__.py +85 -0
- aria_code/packs/base.py +168 -0
- aria_code/packs/finance/__init__.py +250 -0
- aria_code/packs/healthcare/__init__.py +40 -0
- aria_code/packs/logistics/__init__.py +171 -0
- aria_code/packs/payments/__init__.py +139 -0
- aria_code/packs/realty/__init__.py +190 -0
- aria_code/packs/registry.py +202 -0
- aria_code/packs/rules.py +90 -0
- aria_code/plan_utils.py +194 -0
- aria_code/plugin_loader.py +328 -0
- aria_code/portfolio_ledger.py +4 -0
- aria_code/preview_server.py +449 -0
- aria_code/privacy/__init__.py +5 -0
- aria_code/privacy/feedback.py +123 -0
- aria_code/project_review.py +278 -0
- aria_code/project_review_api.py +235 -0
- aria_code/project_review_server.py +18 -0
- aria_code/project_tools.py +4 -0
- aria_code/providers/__init__.py +30 -0
- aria_code/providers/google_video_provider.py +256 -0
- aria_code/providers/llm/__init__.py +19 -0
- aria_code/providers/llm/anthropic.py +184 -0
- aria_code/providers/llm/arthera_gateway.py +90 -0
- aria_code/providers/llm/autoconfig.py +213 -0
- aria_code/providers/llm/base.py +139 -0
- aria_code/providers/llm/custom.py +158 -0
- aria_code/providers/llm/ollama.py +137 -0
- aria_code/providers/llm/openai_compat.py +350 -0
- aria_code/providers/llm/registry.py +457 -0
- aria_code/providers/local_image_provider.py +202 -0
- aria_code/providers/local_llm_provider.py +549 -0
- aria_code/providers/ollama_models.py +79 -0
- aria_code/providers/openai_params.py +32 -0
- aria_code/providers/tool_messages.py +100 -0
- aria_code/realty_data_tools.py +4 -0
- aria_code/report_exporters.py +150 -0
- aria_code/report_generator.py +1764 -0
- aria_code/review_service.py +497 -0
- aria_code/runtime/__init__.py +176 -0
- aria_code/runtime/acceptance.py +632 -0
- aria_code/runtime/agent_loop.py +1851 -0
- aria_code/runtime/agent_profiles.py +75 -0
- aria_code/runtime/approval.py +71 -0
- aria_code/runtime/baseline.py +219 -0
- aria_code/runtime/budget.py +232 -0
- aria_code/runtime/checkpoints.py +481 -0
- aria_code/runtime/consult.py +65 -0
- aria_code/runtime/contract.py +282 -0
- aria_code/runtime/delivery.py +432 -0
- aria_code/runtime/events.py +113 -0
- aria_code/runtime/gateway.py +167 -0
- aria_code/runtime/lsp.py +484 -0
- aria_code/runtime/native_index.py +153 -0
- aria_code/runtime/orchestrator.py +55 -0
- aria_code/runtime/processes.py +343 -0
- aria_code/runtime/project_graph.py +614 -0
- aria_code/runtime/repo_map.py +712 -0
- aria_code/runtime/research_bundle.py +105 -0
- aria_code/runtime/review.py +269 -0
- aria_code/runtime/run_state.py +127 -0
- aria_code/runtime/run_store.py +522 -0
- aria_code/runtime/scoped_runs.py +113 -0
- aria_code/runtime/self_healing.py +422 -0
- aria_code/runtime/semantic_diff.py +171 -0
- aria_code/runtime/subagent.py +721 -0
- aria_code/runtime/symbol_edit.py +117 -0
- aria_code/runtime/task_brief.py +46 -0
- aria_code/runtime/task_context.py +145 -0
- aria_code/runtime/task_ledger.py +108 -0
- aria_code/runtime/task_orchestrator.py +90 -0
- aria_code/runtime/task_outcome.py +33 -0
- aria_code/runtime/task_verification.py +266 -0
- aria_code/runtime/task_worktree.py +448 -0
- aria_code/runtime/tool_executor.py +329 -0
- aria_code/runtime/tool_policy.py +107 -0
- aria_code/runtime/transactions.py +216 -0
- aria_code/runtime/windows_job.py +115 -0
- aria_code/runtime/workflows.py +222 -0
- aria_code/runtime/worktrees.py +215 -0
- aria_code/runway_video_client.py +4 -0
- aria_code/safety/__init__.py +27 -0
- aria_code/safety/client_isolation.py +178 -0
- aria_code/safety/permissions.py +339 -0
- aria_code/safety/risk.py +699 -0
- aria_code/safety/sandbox.py +148 -0
- aria_code/safety/service.py +102 -0
- aria_code/service_endpoints.py +14 -0
- aria_code/setup_wizard.py +684 -0
- aria_code/spreadsheet_tools.py +4 -0
- aria_code/strategy_vault.py +4 -0
- aria_code/tools/__init__.py +0 -0
- aria_code/tools/artifact_tools.py +252 -0
- aria_code/tools/broker_tools.py +92 -0
- aria_code/tools/build_quant_engine.py +97 -0
- aria_code/tools/code_audit_tools.py +192 -0
- aria_code/tools/computer_use_tools.py +509 -0
- aria_code/tools/data_analysis_tools.py +831 -0
- aria_code/tools/enterprise_finance_tools.py +202 -0
- aria_code/tools/extended_tools.py +240 -0
- aria_code/tools/file_analysis_tools.py +880 -0
- aria_code/tools/image_gen_tools.py +114 -0
- aria_code/tools/local_finance_tools.py +3488 -0
- aria_code/tools/logistics_carriers.py +11 -0
- aria_code/tools/logistics_inventory.py +11 -0
- aria_code/tools/logistics_io.py +11 -0
- aria_code/tools/logistics_tenancy.py +11 -0
- aria_code/tools/logistics_tools.py +11 -0
- aria_code/tools/macro_tools.py +412 -0
- aria_code/tools/notification_tools.py +249 -0
- aria_code/tools/project_tools.py +525 -0
- aria_code/tools/realty_data_tools.py +662 -0
- aria_code/tools/spreadsheet_tools.py +399 -0
- aria_code/tools/stripe_tools.py +115 -0
- aria_code/tools/video_tools.py +500 -0
- aria_code/training/__init__.py +1 -0
- aria_code/training/compare.py +117 -0
- aria_code/training/prelude_commands.py +111 -0
- aria_code/training/prelude_eval.py +390 -0
- aria_code/training/prelude_real.py +183 -0
- aria_code/training/prelude_train.py +248 -0
- aria_code/training/sft_dataset.py +230 -0
- aria_code/training/trajectory_dataset.py +263 -0
- aria_code/ui/__init__.py +105 -0
- aria_code/ui/banner.py +404 -0
- aria_code/ui/completer.py +571 -0
- aria_code/ui/console.py +285 -0
- aria_code/ui/input_box.py +505 -0
- aria_code/ui/mentions.py +206 -0
- aria_code/ui/picker.py +248 -0
- aria_code/ui/render/__init__.py +11 -0
- aria_code/ui/render/actions.py +297 -0
- aria_code/ui/render/finance.py +1732 -0
- aria_code/ui/render/market.py +253 -0
- aria_code/ui/render/output.py +954 -0
- aria_code/ui/render/responsive.py +75 -0
- aria_code/ui/render/team.py +367 -0
- aria_code/ui/robot.py +239 -0
- aria_code/ui/startup_dashboard.py +110 -0
- aria_code/video_analysis.py +147 -0
- aria_code/video_editor.py +518 -0
- aria_code/video_workflow/__init__.py +8 -0
- aria_code/video_workflow/__main__.py +61 -0
- aria_code/video_workflow/engines/__init__.py +18 -0
- aria_code/video_workflow/engines/base.py +69 -0
- aria_code/video_workflow/engines/custom_model.py +204 -0
- aria_code/video_workflow/engines/google_veo.py +94 -0
- aria_code/video_workflow/engines/local_engine.py +71 -0
- aria_code/video_workflow/engines/registry.py +98 -0
- aria_code/video_workflow/google_models.py +278 -0
- aria_code/video_workflow/music/__init__.py +5 -0
- aria_code/video_workflow/music/foley_service.py +132 -0
- aria_code/video_workflow/music/service.py +212 -0
- aria_code/video_workflow/pipeline.py +620 -0
- aria_code/video_workflow/plan.py +202 -0
- aria_code/video_workflow/session.py +297 -0
- aria_code/workspace/__init__.py +6 -0
- aria_code/workspace/files.py +219 -0
- aria_code/workspace/verify.py +167 -0
- aria_code/workspace_changes.py +199 -0
- aria_code/workspace_service.py +196 -0
- aria_code/workspace_tools.py +150 -0
- aria_code-0.167.0.dist-info/METADATA +330 -0
- aria_code-0.167.0.dist-info/RECORD +601 -0
- aria_code-0.167.0.dist-info/WHEEL +5 -0
- aria_code-0.167.0.dist-info/entry_points.txt +5 -0
- aria_code-0.167.0.dist-info/licenses/LICENSE +201 -0
- aria_code-0.167.0.dist-info/licenses/NOTICE +49 -0
- aria_code-0.167.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,769 @@
|
|
|
1
|
+
"""CLI consumers for runtime/model events.
|
|
2
|
+
|
|
3
|
+
The runtime core emits typed events and asks for approval decisions. This
|
|
4
|
+
module keeps terminal-specific rendering and prompts at the CLI adapter layer
|
|
5
|
+
instead of letting them spread through the agent loop.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
import sys
|
|
12
|
+
import time
|
|
13
|
+
from dataclasses import dataclass, field
|
|
14
|
+
from enum import Enum
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any, Callable
|
|
17
|
+
|
|
18
|
+
from aria_code.runtime import (
|
|
19
|
+
AgentEventStatus,
|
|
20
|
+
AgentEventThinking,
|
|
21
|
+
AgentEventToken,
|
|
22
|
+
AgentEventToolCall,
|
|
23
|
+
AgentEventToolResult,
|
|
24
|
+
ApprovalDecision,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_REPETITION_MARKER = "*[model stopped — repetition detected]*"
|
|
29
|
+
_REPETITION_NOTICE = (
|
|
30
|
+
"\n\n> 已检测到模型开始重复输出,已自动停止展开。"
|
|
31
|
+
"上方结果仍然有效;如需继续,请指定要补充的部分。"
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _redact_activity_text(value: Any, limit: int = 90) -> str:
|
|
36
|
+
"""One line, secrets masked — for anything echoed to the screen or a log.
|
|
37
|
+
|
|
38
|
+
Shell commands are now shown on their step line, so this also covers
|
|
39
|
+
"Authorization: Bearer …", credentials in URLs and the usual key shapes.
|
|
40
|
+
"""
|
|
41
|
+
text = re.sub(r"\s+", " ", str(value or "")).strip()
|
|
42
|
+
text = re.sub(
|
|
43
|
+
r"(?i)(api[_-]?key|token|password|passwd|secret)(\s*[=:]\s*|\s+)[^\s]+",
|
|
44
|
+
r"\1\2***",
|
|
45
|
+
text,
|
|
46
|
+
)
|
|
47
|
+
text = re.sub(r"(?i)\b(bearer|basic)\s+[A-Za-z0-9._~+/=-]+", r"\1 ***", text)
|
|
48
|
+
text = re.sub(r"://[^/\s:@]+:[^@\s]+@", "://***@", text)
|
|
49
|
+
text = re.sub(r"\b(sk|pk|rk)-[A-Za-z0-9_-]{8,}|\bgh[pousr]_[A-Za-z0-9]{12,}|\bAIza[0-9A-Za-z_-]{20,}"
|
|
50
|
+
r"|\bxox[abposr]-[A-Za-z0-9-]{10,}", "***", text)
|
|
51
|
+
return text[:limit]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# How long the runtime measured a tool call to take, carried with its result.
|
|
55
|
+
MEASURED_ELAPSED_KEY = "_elapsed_s"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def with_measured_elapsed(result: Any, elapsed: Any) -> Any:
|
|
59
|
+
if isinstance(result, dict) and isinstance(elapsed, (int, float)):
|
|
60
|
+
return {**result, MEASURED_ELAPSED_KEY: float(elapsed)}
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def approval_subject(tool_name: str, params: dict[str, Any]) -> str:
|
|
65
|
+
"""What an answered approval leaves behind: the file, or the command with secrets masked."""
|
|
66
|
+
if tool_name == "run_command":
|
|
67
|
+
return _redact_activity_text(params.get("command", ""), limit=80)
|
|
68
|
+
if tool_name == "computer_action":
|
|
69
|
+
return _desktop_subject(params)
|
|
70
|
+
for key in ("path", "file_path"):
|
|
71
|
+
if params.get(key):
|
|
72
|
+
return Path(str(params[key])).name
|
|
73
|
+
return tool_name.replace("_", " ")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _desktop_subject(params: dict[str, Any]) -> str:
|
|
77
|
+
"""What the mouse or keyboard is about to do; typed text is counted, never echoed."""
|
|
78
|
+
action = str(params.get("action") or "input").lower()
|
|
79
|
+
if action == "type":
|
|
80
|
+
return f"type {len(str(params.get('text') or ''))} characters"
|
|
81
|
+
if action == "key":
|
|
82
|
+
return f"press {str(params.get('key') or '?')[:40]}"
|
|
83
|
+
if action == "drag":
|
|
84
|
+
return f"drag ({params.get('x', 0)}, {params.get('y', 0)}) to ({params.get('ex', 0)}, {params.get('ey', 0)})"
|
|
85
|
+
return f"{action.replace('_', ' ')} at ({params.get('x', 0)}, {params.get('y', 0)})"
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _tool_activity_hint(tool: str, params: dict[str, Any]) -> str:
|
|
89
|
+
"""Return one safe, useful argument for a compact activity row."""
|
|
90
|
+
if not isinstance(params, dict):
|
|
91
|
+
return ""
|
|
92
|
+
for key in ("path", "file_path", "filename", "file"):
|
|
93
|
+
if params.get(key):
|
|
94
|
+
return Path(str(params[key])).name[:90]
|
|
95
|
+
for key in ("symbol", "ticker", "query", "q", "command", "url", "task"):
|
|
96
|
+
if params.get(key):
|
|
97
|
+
return _redact_activity_text(params[key])
|
|
98
|
+
title = params.get("title")
|
|
99
|
+
return _redact_activity_text(title) if title else ""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class TurnPhase(str, Enum):
|
|
103
|
+
"""User-visible lifecycle for one assistant turn."""
|
|
104
|
+
|
|
105
|
+
CONNECTING = "connecting"
|
|
106
|
+
THINKING = "thinking"
|
|
107
|
+
STREAMING = "streaming"
|
|
108
|
+
TOOL = "tool"
|
|
109
|
+
DONE = "done"
|
|
110
|
+
ERROR = "error"
|
|
111
|
+
CANCELLED = "cancelled"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclass
|
|
115
|
+
class TurnLifecycle:
|
|
116
|
+
phase: TurnPhase = TurnPhase.CONNECTING
|
|
117
|
+
started_at: float = field(default_factory=time.time)
|
|
118
|
+
first_visible_at: float | None = None
|
|
119
|
+
ended_at: float | None = None
|
|
120
|
+
|
|
121
|
+
def transition(self, phase: TurnPhase) -> None:
|
|
122
|
+
self.phase = phase
|
|
123
|
+
now = time.time()
|
|
124
|
+
if phase is TurnPhase.STREAMING and self.first_visible_at is None:
|
|
125
|
+
self.first_visible_at = now
|
|
126
|
+
if phase in {TurnPhase.DONE, TurnPhase.ERROR, TurnPhase.CANCELLED}:
|
|
127
|
+
self.ended_at = now
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class TerminalRuntimeEventConsumer:
|
|
131
|
+
"""Consume runtime/provider events and render them to a terminal."""
|
|
132
|
+
|
|
133
|
+
action_view: Any = None
|
|
134
|
+
|
|
135
|
+
def __init__(
|
|
136
|
+
self,
|
|
137
|
+
*,
|
|
138
|
+
terminal: Any,
|
|
139
|
+
console: Any,
|
|
140
|
+
has_rich: bool,
|
|
141
|
+
markdown_cls: type | None,
|
|
142
|
+
live_cls: type | None,
|
|
143
|
+
strip_latex: Callable[[str], str],
|
|
144
|
+
set_robot_state: Callable[[Any], None] | None = None,
|
|
145
|
+
streaming_state: Any = None,
|
|
146
|
+
print_tool_call: Callable[[str, dict], None] | None = None,
|
|
147
|
+
print_tool_done: Callable[..., None] | None = None,
|
|
148
|
+
fallback_from: str = "local",
|
|
149
|
+
live_update_interval: float = 0.08,
|
|
150
|
+
ui_lang: str = "en",
|
|
151
|
+
on_response_start: Callable[[], None] | None = None,
|
|
152
|
+
on_phase_change: Callable[[TurnPhase], None] | None = None,
|
|
153
|
+
action_view: Any = None,
|
|
154
|
+
) -> None:
|
|
155
|
+
self.terminal = terminal
|
|
156
|
+
self.console = console
|
|
157
|
+
self.has_rich = has_rich
|
|
158
|
+
self.markdown_cls = markdown_cls
|
|
159
|
+
self.live_cls = live_cls
|
|
160
|
+
self.strip_latex = strip_latex
|
|
161
|
+
self.set_robot_state = set_robot_state
|
|
162
|
+
self.streaming_state = streaming_state
|
|
163
|
+
self.print_tool_call = print_tool_call
|
|
164
|
+
self.print_tool_done = print_tool_done
|
|
165
|
+
self.fallback_from = fallback_from
|
|
166
|
+
self.live_update_interval = live_update_interval
|
|
167
|
+
self.ui_lang = ui_lang or "en"
|
|
168
|
+
self.on_response_start = on_response_start
|
|
169
|
+
self.on_phase_change = on_phase_change
|
|
170
|
+
# ui.render.actions.ActionView: tool events as Explored / Ran / Edit
|
|
171
|
+
# cells. None keeps the per-call ⏺ / ✓ lines.
|
|
172
|
+
self.action_view = action_view
|
|
173
|
+
self.lifecycle = TurnLifecycle()
|
|
174
|
+
|
|
175
|
+
self.response_text = ""
|
|
176
|
+
self.streamed_any = False
|
|
177
|
+
self.token_count = 0
|
|
178
|
+
self.thinking_tokens = 0
|
|
179
|
+
self.thinking_shown = False
|
|
180
|
+
self.thinking_start: float | None = None
|
|
181
|
+
self.thinking_finished = False
|
|
182
|
+
self.thinking_preview_buf: list[str] = []
|
|
183
|
+
self.thinking_full_buf: list[str] = []
|
|
184
|
+
self.tool_start_times: dict[str, list[float]] = {}
|
|
185
|
+
self.tool_params: dict[str, list[dict[str, Any]]] = {}
|
|
186
|
+
self.repetition_stopped = False
|
|
187
|
+
self.repetition_notice_printed = False
|
|
188
|
+
self.response_started = False
|
|
189
|
+
|
|
190
|
+
self.live_display = None
|
|
191
|
+
self.spinner = None
|
|
192
|
+
self.first_token_received_ref = [False]
|
|
193
|
+
self.token_start_time: float | None = None
|
|
194
|
+
self.last_live_update = 0.0
|
|
195
|
+
self.use_plain_print_ref = [False]
|
|
196
|
+
self.use_batch_render_ref = [False]
|
|
197
|
+
self.latex_buf_ref = [""]
|
|
198
|
+
self.in_latex_ref = [False]
|
|
199
|
+
|
|
200
|
+
@property
|
|
201
|
+
def first_token_received(self) -> bool:
|
|
202
|
+
return self.first_token_received_ref[0]
|
|
203
|
+
|
|
204
|
+
@first_token_received.setter
|
|
205
|
+
def first_token_received(self, value: bool) -> None:
|
|
206
|
+
self.first_token_received_ref[0] = value
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def use_plain_print(self) -> bool:
|
|
210
|
+
return self.use_plain_print_ref[0]
|
|
211
|
+
|
|
212
|
+
@use_plain_print.setter
|
|
213
|
+
def use_plain_print(self, value: bool) -> None:
|
|
214
|
+
self.use_plain_print_ref[0] = value
|
|
215
|
+
|
|
216
|
+
@property
|
|
217
|
+
def use_batch_render(self) -> bool:
|
|
218
|
+
return self.use_batch_render_ref[0]
|
|
219
|
+
|
|
220
|
+
@use_batch_render.setter
|
|
221
|
+
def use_batch_render(self, value: bool) -> None:
|
|
222
|
+
self.use_batch_render_ref[0] = value
|
|
223
|
+
|
|
224
|
+
@property
|
|
225
|
+
def latex_buf(self) -> str:
|
|
226
|
+
return self.latex_buf_ref[0]
|
|
227
|
+
|
|
228
|
+
@latex_buf.setter
|
|
229
|
+
def latex_buf(self, value: str) -> None:
|
|
230
|
+
self.latex_buf_ref[0] = value
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def in_latex(self) -> bool:
|
|
234
|
+
return self.in_latex_ref[0]
|
|
235
|
+
|
|
236
|
+
@in_latex.setter
|
|
237
|
+
def in_latex(self, value: bool) -> None:
|
|
238
|
+
self.in_latex_ref[0] = value
|
|
239
|
+
|
|
240
|
+
def start_spinner(self) -> None:
|
|
241
|
+
self.set_phase(TurnPhase.THINKING)
|
|
242
|
+
if self.has_rich and self.spinner is None and not self.first_token_received:
|
|
243
|
+
label = "思考中" if self.ui_lang.lower().startswith("zh") else "Thinking"
|
|
244
|
+
cancel = "esc 取消" if self.ui_lang.lower().startswith("zh") else "esc cancel"
|
|
245
|
+
self.spinner = self.console.status(
|
|
246
|
+
f"[dim]{label}… [/dim][dim italic]{cancel}[/dim italic]",
|
|
247
|
+
spinner="dots",
|
|
248
|
+
spinner_style="dim",
|
|
249
|
+
)
|
|
250
|
+
self.spinner.__enter__()
|
|
251
|
+
|
|
252
|
+
def set_phase(self, phase: TurnPhase) -> None:
|
|
253
|
+
if self.lifecycle.phase is phase:
|
|
254
|
+
return
|
|
255
|
+
self.lifecycle.transition(phase)
|
|
256
|
+
if self.on_phase_change is not None:
|
|
257
|
+
self.on_phase_change(phase)
|
|
258
|
+
|
|
259
|
+
def ensure_response_started(self) -> None:
|
|
260
|
+
"""Render the response label only when visible answer text exists."""
|
|
261
|
+
if self.response_started:
|
|
262
|
+
return
|
|
263
|
+
self._finish_thinking()
|
|
264
|
+
self.stop_spinner()
|
|
265
|
+
self.flush_actions()
|
|
266
|
+
self.response_started = True
|
|
267
|
+
if self.on_response_start is not None:
|
|
268
|
+
self.on_response_start()
|
|
269
|
+
|
|
270
|
+
def finish(self, phase: TurnPhase = TurnPhase.DONE) -> None:
|
|
271
|
+
self._finish_thinking()
|
|
272
|
+
self.stop_spinner()
|
|
273
|
+
self.close_tool_spinner()
|
|
274
|
+
self.flush_actions()
|
|
275
|
+
self.set_phase(phase)
|
|
276
|
+
|
|
277
|
+
def print_action_lines(self, lines) -> None:
|
|
278
|
+
rich = self.has_rich and self.console is not None
|
|
279
|
+
for style, text in lines:
|
|
280
|
+
if text.startswith("⏺"): # a new cell: one blank line above it
|
|
281
|
+
self.console.print() if rich else print()
|
|
282
|
+
if rich:
|
|
283
|
+
from rich.markup import escape
|
|
284
|
+
body = f" {escape(text)}"
|
|
285
|
+
self.console.print(f"[{style}]{body}[/{style}]" if style else body, highlight=False)
|
|
286
|
+
else:
|
|
287
|
+
print(f" {text}")
|
|
288
|
+
|
|
289
|
+
def flush_actions(self) -> None:
|
|
290
|
+
"""Print a pending Explored cell before anything else reaches the screen."""
|
|
291
|
+
if self.action_view is not None:
|
|
292
|
+
self.print_action_lines(self.action_view.flush())
|
|
293
|
+
|
|
294
|
+
def stop_spinner(self) -> None:
|
|
295
|
+
if self.spinner is not None:
|
|
296
|
+
try:
|
|
297
|
+
self.spinner.__exit__(None, None, None)
|
|
298
|
+
except Exception:
|
|
299
|
+
pass
|
|
300
|
+
self.spinner = None
|
|
301
|
+
|
|
302
|
+
def stop_live(self, discard: bool = False) -> None:
|
|
303
|
+
self.stop_spinner()
|
|
304
|
+
if self.live_display:
|
|
305
|
+
try:
|
|
306
|
+
if discard:
|
|
307
|
+
try:
|
|
308
|
+
from rich.text import Text as _RichText
|
|
309
|
+
|
|
310
|
+
self.live_display.update(_RichText(""))
|
|
311
|
+
self.live_display.refresh()
|
|
312
|
+
except Exception:
|
|
313
|
+
pass
|
|
314
|
+
self.live_display.stop()
|
|
315
|
+
except Exception:
|
|
316
|
+
pass
|
|
317
|
+
self.live_display = None
|
|
318
|
+
elif self.first_token_received and not discard and not self.use_batch_render:
|
|
319
|
+
print(flush=True)
|
|
320
|
+
|
|
321
|
+
def set_batch_render_mode(self, enabled: bool = True) -> None:
|
|
322
|
+
self.use_plain_print = enabled
|
|
323
|
+
self.use_batch_render = enabled
|
|
324
|
+
|
|
325
|
+
def reset_stream_state(self) -> None:
|
|
326
|
+
self.response_text = ""
|
|
327
|
+
self.streamed_any = False
|
|
328
|
+
self.token_count = 0
|
|
329
|
+
self.first_token_received = False
|
|
330
|
+
self.token_start_time = None
|
|
331
|
+
self.latex_buf = ""
|
|
332
|
+
self.in_latex = False
|
|
333
|
+
|
|
334
|
+
def flush_latex_buf(self) -> str:
|
|
335
|
+
raw = self.latex_buf
|
|
336
|
+
self.latex_buf = ""
|
|
337
|
+
self.in_latex = False
|
|
338
|
+
return self.strip_latex(raw) if raw.strip() else raw
|
|
339
|
+
|
|
340
|
+
def _show_repetition_notice(self) -> None:
|
|
341
|
+
if self.repetition_notice_printed or self.use_batch_render:
|
|
342
|
+
return
|
|
343
|
+
self.repetition_notice_printed = True
|
|
344
|
+
if self.live_display and self.has_rich and self.markdown_cls is not None:
|
|
345
|
+
clean_text = self.response_text.split(_REPETITION_MARKER, 1)[0].rstrip()
|
|
346
|
+
self.live_display.update(self.markdown_cls(self.strip_latex(clean_text + _REPETITION_NOTICE)))
|
|
347
|
+
self.live_display.refresh()
|
|
348
|
+
return
|
|
349
|
+
print(_REPETITION_NOTICE, end="", flush=True)
|
|
350
|
+
|
|
351
|
+
def finalize_text(self, final_text: str) -> str:
|
|
352
|
+
if self.in_latex and self.latex_buf:
|
|
353
|
+
leftover = self.flush_latex_buf()
|
|
354
|
+
final_text = (final_text or "") + leftover
|
|
355
|
+
if self.use_plain_print and not self.use_batch_render:
|
|
356
|
+
print(leftover, end="", flush=True)
|
|
357
|
+
return final_text
|
|
358
|
+
|
|
359
|
+
def _finish_thinking(self) -> None:
|
|
360
|
+
if not self.thinking_shown or self.thinking_finished:
|
|
361
|
+
return
|
|
362
|
+
self.thinking_finished = True
|
|
363
|
+
self.stop_spinner()
|
|
364
|
+
elapsed_t = time.time() - self.thinking_start if self.thinking_start else 0
|
|
365
|
+
self.terminal._last_thinking = "".join(self.thinking_full_buf).strip()
|
|
366
|
+
is_zh = self.ui_lang.lower().startswith("zh")
|
|
367
|
+
t_info = f"思考 {elapsed_t:.1f}s" if is_zh else f"Thought for {elapsed_t:.1f}s"
|
|
368
|
+
if self.thinking_tokens > 0:
|
|
369
|
+
t_info += f" · {self.thinking_tokens:,} tokens"
|
|
370
|
+
expand = "展开" if is_zh else "expand"
|
|
371
|
+
ctrlo = f" [dim]· Ctrl+O {expand}[/dim]" if self.terminal._last_thinking else ""
|
|
372
|
+
if self.has_rich:
|
|
373
|
+
sys.stdout.write("\r\033[K")
|
|
374
|
+
sys.stdout.flush()
|
|
375
|
+
self.console.print(f" [dim]✻[/dim] [dim]{t_info}[/dim]{ctrlo}")
|
|
376
|
+
if self.terminal.config.get("thinking_preview") and self.thinking_preview_buf:
|
|
377
|
+
preview_text = "".join(self.thinking_preview_buf)[:280].strip()
|
|
378
|
+
if len("".join(self.thinking_preview_buf)) > 280:
|
|
379
|
+
preview_text += "…"
|
|
380
|
+
self.console.print(f" [dim italic]{preview_text}[/dim italic]")
|
|
381
|
+
else:
|
|
382
|
+
print(f"\r ✻ {t_info}")
|
|
383
|
+
|
|
384
|
+
def on_token(self, token: str) -> None:
|
|
385
|
+
if "<|im_start|>" in token or "<|im_end|>" in token:
|
|
386
|
+
token = token.replace("<|im_start|>", "").replace("<|im_end|>", "")
|
|
387
|
+
if not token.strip():
|
|
388
|
+
return
|
|
389
|
+
|
|
390
|
+
meta_artifacts = (
|
|
391
|
+
"(注释:",
|
|
392
|
+
"(注释:",
|
|
393
|
+
"(提示:",
|
|
394
|
+
"(提示:",
|
|
395
|
+
"请使用实际注入的数据",
|
|
396
|
+
"请使用实际数据",
|
|
397
|
+
"实际注入的数据",
|
|
398
|
+
"[system]",
|
|
399
|
+
"[/system]",
|
|
400
|
+
"[INST]",
|
|
401
|
+
"[/INST]",
|
|
402
|
+
)
|
|
403
|
+
if any(a in token for a in meta_artifacts):
|
|
404
|
+
token = re.sub(
|
|
405
|
+
r"\(注[释释]:[^))]*[))]|(注[释释]:[^))]*[))]"
|
|
406
|
+
r"|\(提示:[^))]*[))]|(提示:[^))]*[))]"
|
|
407
|
+
r"|请使用实际(?:注入的)?数据[^。\n]*"
|
|
408
|
+
r"|\[/?(?:system|INST)\]",
|
|
409
|
+
"",
|
|
410
|
+
token,
|
|
411
|
+
)
|
|
412
|
+
if not token.strip():
|
|
413
|
+
return
|
|
414
|
+
|
|
415
|
+
if not self.first_token_received and not token.strip():
|
|
416
|
+
self.response_text += token
|
|
417
|
+
self.streamed_any = True
|
|
418
|
+
self.token_count += 1
|
|
419
|
+
return
|
|
420
|
+
|
|
421
|
+
if not self.first_token_received:
|
|
422
|
+
self.first_token_received = True
|
|
423
|
+
self.token_start_time = time.time()
|
|
424
|
+
self.set_phase(TurnPhase.STREAMING)
|
|
425
|
+
if self.set_robot_state is not None:
|
|
426
|
+
self.set_robot_state(self.streaming_state)
|
|
427
|
+
if not self.use_batch_render:
|
|
428
|
+
self.ensure_response_started()
|
|
429
|
+
|
|
430
|
+
if _REPETITION_MARKER in token:
|
|
431
|
+
if self.use_batch_render:
|
|
432
|
+
self.response_text += token
|
|
433
|
+
self.streamed_any = True
|
|
434
|
+
self.token_count += 1
|
|
435
|
+
self.repetition_stopped = True
|
|
436
|
+
return
|
|
437
|
+
before, _, _after = token.partition(_REPETITION_MARKER)
|
|
438
|
+
if before:
|
|
439
|
+
self.on_token(before)
|
|
440
|
+
self.response_text += _REPETITION_MARKER
|
|
441
|
+
self.streamed_any = True
|
|
442
|
+
self.token_count += 1
|
|
443
|
+
self.repetition_stopped = True
|
|
444
|
+
self._show_repetition_notice()
|
|
445
|
+
return
|
|
446
|
+
|
|
447
|
+
self._finish_thinking()
|
|
448
|
+
|
|
449
|
+
if self.use_batch_render:
|
|
450
|
+
self.response_text += token
|
|
451
|
+
self.streamed_any = True
|
|
452
|
+
self.token_count += 1
|
|
453
|
+
return
|
|
454
|
+
|
|
455
|
+
open_delims = (r"\(", r"\[", "$$")
|
|
456
|
+
close_delims = (r"\)", r"\]", "$$")
|
|
457
|
+
if not self.in_latex:
|
|
458
|
+
if any(d in token for d in open_delims):
|
|
459
|
+
self.in_latex = True
|
|
460
|
+
self.latex_buf = token
|
|
461
|
+
tail = token
|
|
462
|
+
for od, cd in zip(open_delims, close_delims):
|
|
463
|
+
if od in tail:
|
|
464
|
+
after = tail[tail.index(od) + len(od):]
|
|
465
|
+
if cd in after:
|
|
466
|
+
token = self.flush_latex_buf()
|
|
467
|
+
break
|
|
468
|
+
else:
|
|
469
|
+
self.response_text += self.latex_buf
|
|
470
|
+
self.streamed_any = True
|
|
471
|
+
self.token_count += 1
|
|
472
|
+
return
|
|
473
|
+
else:
|
|
474
|
+
token = self.strip_latex(token)
|
|
475
|
+
else:
|
|
476
|
+
self.latex_buf += token
|
|
477
|
+
if any(d in token for d in close_delims):
|
|
478
|
+
token = self.flush_latex_buf()
|
|
479
|
+
else:
|
|
480
|
+
self.response_text += token
|
|
481
|
+
self.streamed_any = True
|
|
482
|
+
self.token_count += 1
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
self.response_text += token
|
|
486
|
+
self.streamed_any = True
|
|
487
|
+
self.token_count += 1
|
|
488
|
+
can_live = (
|
|
489
|
+
self.has_rich
|
|
490
|
+
and not self.use_plain_print
|
|
491
|
+
and getattr(self.console, "is_terminal", False)
|
|
492
|
+
and not getattr(self.console, "is_dumb_terminal", True)
|
|
493
|
+
and self.markdown_cls is not None
|
|
494
|
+
and self.live_cls is not None
|
|
495
|
+
)
|
|
496
|
+
if can_live:
|
|
497
|
+
now = time.time()
|
|
498
|
+
if self.live_display is not None and now - self.last_live_update < self.live_update_interval:
|
|
499
|
+
return
|
|
500
|
+
md = self.markdown_cls(self.strip_latex(self.response_text))
|
|
501
|
+
if self.live_display is None:
|
|
502
|
+
self.live_display = self.live_cls(
|
|
503
|
+
md,
|
|
504
|
+
console=self.console,
|
|
505
|
+
refresh_per_second=12,
|
|
506
|
+
vertical_overflow="visible",
|
|
507
|
+
)
|
|
508
|
+
self.live_display.start()
|
|
509
|
+
self.last_live_update = now
|
|
510
|
+
elif now - self.last_live_update >= self.live_update_interval:
|
|
511
|
+
self.live_display.update(md)
|
|
512
|
+
self.last_live_update = now
|
|
513
|
+
else:
|
|
514
|
+
print(token, end="", flush=True)
|
|
515
|
+
|
|
516
|
+
def on_thinking(self, content: str) -> None:
|
|
517
|
+
self.set_phase(TurnPhase.THINKING)
|
|
518
|
+
if self.thinking_finished:
|
|
519
|
+
# 工具调用后的新一段思考:重置段状态,否则上一段的 finished 闩锁
|
|
520
|
+
# 让本段永远无法 finalize,"思考中 …(N tokens)"活行被冻结在滚动区
|
|
521
|
+
# (即多条思考行并存的根因)。full_buf 不清,Ctrl+O 展开保留全部段落。
|
|
522
|
+
self.thinking_finished = False
|
|
523
|
+
self.thinking_shown = False
|
|
524
|
+
self.thinking_tokens = 0
|
|
525
|
+
self.thinking_preview_buf = []
|
|
526
|
+
if not self.thinking_shown:
|
|
527
|
+
self.stop_spinner()
|
|
528
|
+
self.thinking_start = time.time()
|
|
529
|
+
self.thinking_shown = True
|
|
530
|
+
self.thinking_tokens += 1
|
|
531
|
+
if self.thinking_tokens % 30 == 1:
|
|
532
|
+
elapsed = time.time() - self.thinking_start
|
|
533
|
+
label = "思考中" if self.ui_lang.lower().startswith("zh") else "Thinking"
|
|
534
|
+
# 与 finalize 行(思考 Xs · N tokens)同格式,避免括号/点分隔两种样式并存
|
|
535
|
+
sys.stdout.write(
|
|
536
|
+
f"\r \033[2m✻\033[0m \033[2m{label} {elapsed:.1f}s"
|
|
537
|
+
f" · {self.thinking_tokens} tokens\033[0m "
|
|
538
|
+
)
|
|
539
|
+
sys.stdout.flush()
|
|
540
|
+
if len("".join(self.thinking_preview_buf)) < 300:
|
|
541
|
+
self.thinking_preview_buf.append(content)
|
|
542
|
+
if len("".join(self.thinking_full_buf)) < 8000:
|
|
543
|
+
self.thinking_full_buf.append(content)
|
|
544
|
+
|
|
545
|
+
def on_tool_call(self, tool: str, params: dict) -> None:
|
|
546
|
+
self.set_phase(TurnPhase.TOOL)
|
|
547
|
+
self._finish_thinking()
|
|
548
|
+
|
|
549
|
+
# Cursor/Windsurf 风格的实时加载动画
|
|
550
|
+
# One spinner at a time. /team's analysts can call tools in parallel;
|
|
551
|
+
# close the previous indicator before starting the next one.
|
|
552
|
+
self.close_tool_spinner()
|
|
553
|
+
if self.has_rich and hasattr(self, 'console'):
|
|
554
|
+
hint = _tool_activity_hint(tool, params)
|
|
555
|
+
verb = "调用工具" if str(self.ui_lang).lower().startswith("zh") else "Running"
|
|
556
|
+
label = f"[bold cyan]{verb}[/bold cyan] [green]{tool}[/green]"
|
|
557
|
+
if hint:
|
|
558
|
+
label += f" [dim]({hint})[/dim]"
|
|
559
|
+
self.tool_spinner = self.console.status(label, spinner="dots12", spinner_style="cyan")
|
|
560
|
+
self.tool_spinner.__enter__()
|
|
561
|
+
|
|
562
|
+
if self.action_view is not None:
|
|
563
|
+
self.print_action_lines(self.action_view.start(tool, params if isinstance(params, dict) else {}))
|
|
564
|
+
elif self.print_tool_call is not None:
|
|
565
|
+
self.print_tool_call(tool, params if isinstance(params, dict) else {})
|
|
566
|
+
|
|
567
|
+
self.tool_start_times.setdefault(tool, []).append(time.time())
|
|
568
|
+
safe_params = dict(params) if isinstance(params, dict) else {}
|
|
569
|
+
self.tool_params.setdefault(tool, []).append(safe_params)
|
|
570
|
+
|
|
571
|
+
def close_tool_spinner(self) -> None:
|
|
572
|
+
"""Stop the tool spinner, if one is running."""
|
|
573
|
+
spinner = getattr(self, "tool_spinner", None)
|
|
574
|
+
if spinner is not None:
|
|
575
|
+
try:
|
|
576
|
+
spinner.__exit__(None, None, None)
|
|
577
|
+
except Exception:
|
|
578
|
+
pass
|
|
579
|
+
self.tool_spinner = None
|
|
580
|
+
|
|
581
|
+
def on_tool_result(self, tool: str, summary: Any) -> None:
|
|
582
|
+
# 关闭工具加载动画
|
|
583
|
+
self.close_tool_spinner()
|
|
584
|
+
|
|
585
|
+
starts = self.tool_start_times.get(tool) or []
|
|
586
|
+
started_at = starts.pop(0) if starts else time.time()
|
|
587
|
+
if not starts:
|
|
588
|
+
self.tool_start_times.pop(tool, None)
|
|
589
|
+
elapsed_ms = int((time.time() - started_at) * 1000)
|
|
590
|
+
# The runtime times each call itself. Measuring from the announcement
|
|
591
|
+
# counted the person reading an approval, and results of a batch
|
|
592
|
+
# arrive together — "✓ writing file test_fx.py 13.9s" for a 9ms write.
|
|
593
|
+
if isinstance(summary, dict) and isinstance(summary.get(MEASURED_ELAPSED_KEY), (int, float)):
|
|
594
|
+
elapsed_ms = int(summary[MEASURED_ELAPSED_KEY] * 1000)
|
|
595
|
+
params_queue = self.tool_params.get(tool) or []
|
|
596
|
+
params = params_queue.pop(0) if params_queue else {}
|
|
597
|
+
if not params_queue:
|
|
598
|
+
self.tool_params.pop(tool, None)
|
|
599
|
+
ok = not (isinstance(summary, dict) and not summary.get("success", True))
|
|
600
|
+
if self.action_view is not None:
|
|
601
|
+
self.print_action_lines(self.action_view.done(tool, params, summary, elapsed_ms / 1000))
|
|
602
|
+
elif self.print_tool_done is not None:
|
|
603
|
+
# Surface the failure reason on the ✗ line — a red cross with no
|
|
604
|
+
# explanation leaves both the user and the reviewer blind to WHY
|
|
605
|
+
# (observed in the channels drill: peer_comparison failed 4× with
|
|
606
|
+
# nothing on screen).
|
|
607
|
+
detail = ""
|
|
608
|
+
if not ok and isinstance(summary, dict):
|
|
609
|
+
detail = str(summary.get("error") or "")[:90]
|
|
610
|
+
elif ok:
|
|
611
|
+
# Name what was done: two bare "✓ writing file" lines in a row
|
|
612
|
+
# did not say which file each was.
|
|
613
|
+
detail = _tool_activity_hint(tool, params)
|
|
614
|
+
self.print_tool_done(tool, elapsed_ms, success=ok, summary=detail)
|
|
615
|
+
|
|
616
|
+
ts = time.strftime("%H:%M:%S")
|
|
617
|
+
icon = "✓" if ok else "✗"
|
|
618
|
+
hint = _tool_activity_hint(tool, params)
|
|
619
|
+
duration = f"{elapsed_ms}ms" if elapsed_ms < 1000 else f"{elapsed_ms / 1000:.1f}s"
|
|
620
|
+
entry = f"{ts} {icon} {tool}"
|
|
621
|
+
if hint:
|
|
622
|
+
entry += f" {hint}"
|
|
623
|
+
entry += f" · {duration}"
|
|
624
|
+
if not ok and isinstance(summary, dict):
|
|
625
|
+
error = _redact_activity_text(summary.get("error"), limit=100)
|
|
626
|
+
if error:
|
|
627
|
+
entry += f" · {error}"
|
|
628
|
+
self.terminal._transcript_log.append(entry)
|
|
629
|
+
if len(self.terminal._transcript_log) > 100:
|
|
630
|
+
self.terminal._transcript_log = self.terminal._transcript_log[-100:]
|
|
631
|
+
|
|
632
|
+
if tool in ("TaskCreate", "TaskUpdate") and isinstance(summary, dict):
|
|
633
|
+
tid = summary.get("id") or summary.get("task_id")
|
|
634
|
+
title = summary.get("title", "")
|
|
635
|
+
status = summary.get("status", "pending")
|
|
636
|
+
if tid:
|
|
637
|
+
existing = next((t for t in self.terminal._task_list if t.get("id") == tid), None)
|
|
638
|
+
if existing:
|
|
639
|
+
existing["status"] = status
|
|
640
|
+
if title:
|
|
641
|
+
existing["title"] = title
|
|
642
|
+
else:
|
|
643
|
+
self.terminal._task_list.append({"id": tid, "title": title, "status": status})
|
|
644
|
+
|
|
645
|
+
def on_status(self, state: str, message: str) -> None:
|
|
646
|
+
if state in ("acceptance_passed", "acceptance_failed"):
|
|
647
|
+
# The gate's verdict is the one piece of evidence behind "done", so
|
|
648
|
+
# it is printed rather than left in the turn result: a user who
|
|
649
|
+
# cannot see that the check went red will read the model's summary
|
|
650
|
+
# as if it had gone green.
|
|
651
|
+
passed = state == "acceptance_passed"
|
|
652
|
+
if self.has_rich and self.console is not None:
|
|
653
|
+
colour = "green" if passed else "yellow"
|
|
654
|
+
self.console.print(f" [{colour}]{'✓' if passed else '✗'} {message}[/{colour}]")
|
|
655
|
+
else:
|
|
656
|
+
print(f" {'✓' if passed else '✗'} {message}")
|
|
657
|
+
return
|
|
658
|
+
if state in ("review_passed", "review_blocking", "review_error"):
|
|
659
|
+
mark, colour = {"review_passed": ("✓", "green"), "review_blocking": ("✗", "yellow"),
|
|
660
|
+
"review_error": ("⚠", "dim")}[state]
|
|
661
|
+
if self.has_rich and self.console is not None:
|
|
662
|
+
from rich.markup import escape
|
|
663
|
+
self.console.print(f" [{colour}]{mark} {escape(message)}[/{colour}]")
|
|
664
|
+
else:
|
|
665
|
+
print(f" {mark} {message}")
|
|
666
|
+
return
|
|
667
|
+
if state in {"max_rounds", "budget_exhausted", "loop_guard", "checks_failed"}:
|
|
668
|
+
if self.has_rich and self.console is not None:
|
|
669
|
+
self.console.print(f" [yellow]Task incomplete: {message}[/yellow]")
|
|
670
|
+
else:
|
|
671
|
+
print(f" Task incomplete: {message}")
|
|
672
|
+
return
|
|
673
|
+
if state != "fallback":
|
|
674
|
+
return
|
|
675
|
+
match = re.search(r"(?:from\s+)?(\w+)\s*(?:→|->|to)\s*(\w+)", message or "", re.I)
|
|
676
|
+
if match:
|
|
677
|
+
from_provider, to_provider = match.group(1), match.group(2)
|
|
678
|
+
else:
|
|
679
|
+
from_provider, to_provider = self.fallback_from, "cloud"
|
|
680
|
+
from aria_code.ui.render.output import print_fallback_toast
|
|
681
|
+
|
|
682
|
+
print_fallback_toast(
|
|
683
|
+
from_provider,
|
|
684
|
+
to_provider,
|
|
685
|
+
message or "",
|
|
686
|
+
console=self.console,
|
|
687
|
+
has_rich=self.has_rich,
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
def handle_runtime_event(self, event: Any) -> None:
|
|
691
|
+
if isinstance(event, AgentEventToken):
|
|
692
|
+
self.on_token(event.text)
|
|
693
|
+
elif isinstance(event, AgentEventThinking):
|
|
694
|
+
self.on_thinking(event.content)
|
|
695
|
+
elif isinstance(event, AgentEventToolCall):
|
|
696
|
+
self.on_tool_call(event.tool, event.params)
|
|
697
|
+
elif isinstance(event, AgentEventToolResult):
|
|
698
|
+
self.on_tool_result(event.tool, with_measured_elapsed(event.result, getattr(event, "elapsed", None)))
|
|
699
|
+
elif isinstance(event, AgentEventStatus):
|
|
700
|
+
self.on_status(event.state, event.message)
|
|
701
|
+
|
|
702
|
+
|
|
703
|
+
class TerminalApprovalEventConsumer:
|
|
704
|
+
"""Terminal-side approval prompt consumer for runtime tool execution."""
|
|
705
|
+
|
|
706
|
+
def __init__(
|
|
707
|
+
self,
|
|
708
|
+
*,
|
|
709
|
+
terminal: Any,
|
|
710
|
+
console: Any,
|
|
711
|
+
has_rich: bool,
|
|
712
|
+
confirm_decision: Callable[..., ApprovalDecision],
|
|
713
|
+
apply_decision: Callable[[dict, ApprovalDecision], dict],
|
|
714
|
+
save_config: Callable[[dict], None],
|
|
715
|
+
) -> None:
|
|
716
|
+
self.terminal = terminal
|
|
717
|
+
self.console = console
|
|
718
|
+
self.has_rich = has_rich
|
|
719
|
+
self.confirm_decision = confirm_decision
|
|
720
|
+
self.apply_decision = apply_decision
|
|
721
|
+
self.save_config = save_config
|
|
722
|
+
|
|
723
|
+
async def approve(
|
|
724
|
+
self,
|
|
725
|
+
tool_name: str,
|
|
726
|
+
tool_params: dict,
|
|
727
|
+
*,
|
|
728
|
+
stop_before_prompt: Callable[[], None] | None = None,
|
|
729
|
+
) -> ApprovalDecision:
|
|
730
|
+
if stop_before_prompt is not None:
|
|
731
|
+
stop_before_prompt()
|
|
732
|
+
try:
|
|
733
|
+
approval = self.confirm_decision(
|
|
734
|
+
tool_name,
|
|
735
|
+
tool_params,
|
|
736
|
+
config_policy=self.terminal.config.get("command_policy", "safe"),
|
|
737
|
+
)
|
|
738
|
+
except KeyboardInterrupt:
|
|
739
|
+
approval = ApprovalDecision.deny("KeyboardInterrupt")
|
|
740
|
+
|
|
741
|
+
self.terminal._record_feedback(
|
|
742
|
+
"tool_accept" if approval.approved else "tool_reject",
|
|
743
|
+
tool_name,
|
|
744
|
+
)
|
|
745
|
+
if not approval.approved:
|
|
746
|
+
from aria_code.ui.render.output import print_tool_blocked
|
|
747
|
+
|
|
748
|
+
zh = str(self.terminal.config.get("ui_lang", "en")).lower().startswith("zh")
|
|
749
|
+
print_tool_blocked(tool_name, "用户取消" if zh else "declined", console=self.console,
|
|
750
|
+
has_rich=self.has_rich)
|
|
751
|
+
return approval
|
|
752
|
+
|
|
753
|
+
def apply(self, tool_params: dict, approval: ApprovalDecision) -> dict:
|
|
754
|
+
self.apply_decision(tool_params, approval)
|
|
755
|
+
if approval.upgrade_policy:
|
|
756
|
+
tool_params.pop("_upgrade_policy", None)
|
|
757
|
+
self.terminal.config["command_policy"] = "balanced"
|
|
758
|
+
try:
|
|
759
|
+
self.save_config(self.terminal.config)
|
|
760
|
+
if self.has_rich:
|
|
761
|
+
zh = str(self.terminal.config.get("ui_lang", "en")).lower().startswith("zh")
|
|
762
|
+
self.console.print(" [dim]策略已升级为 balanced 并保存[/dim]" if zh else
|
|
763
|
+
" [dim]Policy set to balanced and saved[/dim]")
|
|
764
|
+
except Exception:
|
|
765
|
+
pass
|
|
766
|
+
return tool_params
|
|
767
|
+
|
|
768
|
+
|
|
769
|
+
__all__ = ["TerminalApprovalEventConsumer", "TerminalRuntimeEventConsumer"]
|