polyrob 0.5.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.
- agents/README.md +453 -0
- agents/__init__.py +164 -0
- agents/base_agent.py +743 -0
- agents/personality/character.py +119 -0
- agents/personality/character_manager.py +309 -0
- agents/personality/characters/rob.character.json +56 -0
- agents/personality/characters/trump.character.json +63 -0
- agents/personality/persona_render.py +78 -0
- agents/personality/persona_resolver.py +115 -0
- agents/prompt/__init__.py +58 -0
- agents/prompt/base_prompt.py +92 -0
- agents/prompt/system.py +417 -0
- agents/task/README_PATH_MANAGEMENT.md +141 -0
- agents/task/__init__.py +162 -0
- agents/task/agent/__init__.py +70 -0
- agents/task/agent/agent_state.py +408 -0
- agents/task/agent/async_delegation.py +274 -0
- agents/task/agent/autonomy_state.py +275 -0
- agents/task/agent/conversation.py +75 -0
- agents/task/agent/core/__init__.py +6 -0
- agents/task/agent/core/aux_metering.py +61 -0
- agents/task/agent/core/background_review.py +157 -0
- agents/task/agent/core/construction.py +1179 -0
- agents/task/agent/core/conversational_exit.py +49 -0
- agents/task/agent/core/correspondent_gate.py +227 -0
- agents/task/agent/core/curator.py +246 -0
- agents/task/agent/core/episodic_digest.py +103 -0
- agents/task/agent/core/error_recovery.py +480 -0
- agents/task/agent/core/history_io.py +257 -0
- agents/task/agent/core/llm_provisioning.py +287 -0
- agents/task/agent/core/llm_runner.py +547 -0
- agents/task/agent/core/logging_io.py +276 -0
- agents/task/agent/core/loop_detection.py +262 -0
- agents/task/agent/core/memory_prefetch.py +325 -0
- agents/task/agent/core/memory_writer.py +667 -0
- agents/task/agent/core/model_introspection.py +82 -0
- agents/task/agent/core/model_swap.py +177 -0
- agents/task/agent/core/next_action_internal.py +1331 -0
- agents/task/agent/core/output_validation.py +303 -0
- agents/task/agent/core/project_context.py +272 -0
- agents/task/agent/core/resources.py +93 -0
- agents/task/agent/core/result_processing.py +389 -0
- agents/task/agent/core/run_loop.py +661 -0
- agents/task/agent/core/safety_lifecycle.py +228 -0
- agents/task/agent/core/secret_guard.py +293 -0
- agents/task/agent/core/self_wake.py +280 -0
- agents/task/agent/core/session_metadata.py +104 -0
- agents/task/agent/core/step.py +781 -0
- agents/task/agent/core/step_execution.py +398 -0
- agents/task/agent/core/step_telemetry.py +222 -0
- agents/task/agent/core/test_aux_metering.py +50 -0
- agents/task/agent/core/test_billing_failover.py +68 -0
- agents/task/agent/core/test_fatal_error_classifier.py +38 -0
- agents/task/agent/core/test_judge_validation.py +275 -0
- agents/task/agent/core/test_model_swap.py +190 -0
- agents/task/agent/core/test_reflection_offload.py +54 -0
- agents/task/agent/core/turn_input.py +8 -0
- agents/task/agent/core/untrusted_wrap.py +103 -0
- agents/task/agent/core/user_ingress.py +228 -0
- agents/task/agent/hitl_manager.py +396 -0
- agents/task/agent/log_sanitize.py +36 -0
- agents/task/agent/message_manager/config.py +97 -0
- agents/task/agent/message_manager/service.py +758 -0
- agents/task/agent/message_manager/tests.py +305 -0
- agents/task/agent/message_manager/tool_call_builder.py +539 -0
- agents/task/agent/message_manager/tool_message_repair.py +364 -0
- agents/task/agent/message_manager/views.py +143 -0
- agents/task/agent/messages/__init__.py +8 -0
- agents/task/agent/messages/builders.py +309 -0
- agents/task/agent/messages/compactor.py +611 -0
- agents/task/agent/messages/context_references.py +271 -0
- agents/task/agent/messages/filters.py +333 -0
- agents/task/agent/messages/guidance.py +328 -0
- agents/task/agent/messages/persistence.py +448 -0
- agents/task/agent/messages/retrieval.py +506 -0
- agents/task/agent/messages/sqlite_persistence.py +85 -0
- agents/task/agent/messages/test_compaction_upgrade.py +349 -0
- agents/task/agent/messages/test_persistence_subdir_fix.py +28 -0
- agents/task/agent/messages/test_sqlite_persistence.py +54 -0
- agents/task/agent/messages/token_counter.py +532 -0
- agents/task/agent/orchestrator.py +776 -0
- agents/task/agent/profile_manager.py +105 -0
- agents/task/agent/profile_registry.py +173 -0
- agents/task/agent/prompts.py +1090 -0
- agents/task/agent/scenario_registry.py +226 -0
- agents/task/agent/service.py +509 -0
- agents/task/agent/session.py +984 -0
- agents/task/agent/skill_discovery.py +110 -0
- agents/task/agent/skill_frontmatter.py +94 -0
- agents/task/agent/skill_manager.py +1201 -0
- agents/task/agent/skill_store.py +627 -0
- agents/task/agent/skill_validation.py +56 -0
- agents/task/agent/skill_writer.py +492 -0
- agents/task/agent/sub_agent_manager.py +1137 -0
- agents/task/agent/test_session_disk_recovery.py +34 -0
- agents/task/agent/tests.py +513 -0
- agents/task/agent/tool_call_tracker.py +511 -0
- agents/task/agent/views.py +469 -0
- agents/task/config.py +432 -0
- agents/task/constants.py +1384 -0
- agents/task/flag_defaults.py +63 -0
- agents/task/goals/__init__.py +1 -0
- agents/task/goals/autonomy_marker.py +28 -0
- agents/task/goals/board.py +690 -0
- agents/task/goals/completion_judge.py +247 -0
- agents/task/goals/context.py +62 -0
- agents/task/goals/dispatcher.py +848 -0
- agents/task/goals/escalation.py +102 -0
- agents/task/goals/planner.py +114 -0
- agents/task/logging_config.py +155 -0
- agents/task/path.py +991 -0
- agents/task/robust_parse_config.py +400 -0
- agents/task/runtime/__init__.py +0 -0
- agents/task/runtime/run_as_session.py +130 -0
- agents/task/runtime_safety.py +308 -0
- agents/task/session/__init__.py +5 -0
- agents/task/session/browser_pool.py +130 -0
- agents/task/session/cleanup.py +612 -0
- agents/task/session/execution.py +397 -0
- agents/task/session/feed.py +200 -0
- agents/task/session/hitl_ingress.py +201 -0
- agents/task/session/hooks.py +83 -0
- agents/task/session/multi_agent.py +243 -0
- agents/task/session/workspace.py +91 -0
- agents/task/session_registry.py +91 -0
- agents/task/session_route.py +35 -0
- agents/task/sqlite_session_registry.py +261 -0
- agents/task/surface_config.py +185 -0
- agents/task/telemetry/__init__.py +58 -0
- agents/task/telemetry/event_log.py +180 -0
- agents/task/telemetry/formatters.py +642 -0
- agents/task/telemetry/manager.py +571 -0
- agents/task/telemetry/memory_events.py +66 -0
- agents/task/telemetry/self_events.py +53 -0
- agents/task/telemetry/sequence.py +160 -0
- agents/task/telemetry/service.py +1265 -0
- agents/task/telemetry/views.py +882 -0
- agents/task/templates.py +98 -0
- agents/task/test_aux_chain.py +151 -0
- agents/task/test_aux_router.py +41 -0
- agents/task/tests/test_parse_robustness.py +170 -0
- agents/task/tool_defaults.py +110 -0
- agents/task/utils.py +762 -0
- agents/task/utils_json.py +768 -0
- agents/task/utils_webview.py +86 -0
- agents/task/workspace_context.py +229 -0
- agents/task_agent_lite.py +2398 -0
- api/README.md +683 -0
- api/TASK_API_DOCS.md +151 -0
- api/__init__.py +0 -0
- api/a2a/__init__.py +37 -0
- api/a2a/agent_card.py +337 -0
- api/a2a/client.py +657 -0
- api/a2a/endpoints.py +375 -0
- api/a2a/models.py +268 -0
- api/a2a/streaming.py +419 -0
- api/a2a/task_handler.py +929 -0
- api/admin_endpoints.py +1584 -0
- api/app.py +923 -0
- api/auth_constants.py +28 -0
- api/auth_endpoints.py +402 -0
- api/auth_state.py +36 -0
- api/chat_via_task.py +42 -0
- api/conversation_manager.py +231 -0
- api/dependencies.py +186 -0
- api/eip8004_endpoints.py +452 -0
- api/hyperliquid_models.py +174 -0
- api/hyperliquid_routes.py +450 -0
- api/interfaces.py +207 -0
- api/jwt_middleware.py +83 -0
- api/kb/__init__.py +1 -0
- api/kb/endpoints.py +314 -0
- api/mcp_models.py +279 -0
- api/mcp_routes.py +438 -0
- api/middleware.py +526 -0
- api/models.py +176 -0
- api/openai_compat/__init__.py +0 -0
- api/openai_compat/model_map.py +25 -0
- api/openai_compat/models.py +59 -0
- api/openai_compat/router.py +140 -0
- api/payment_endpoints.py +444 -0
- api/payment_verification.py +138 -0
- api/polymarket_models.py +155 -0
- api/polymarket_routes.py +501 -0
- api/pricing_endpoints.py +89 -0
- api/session_routing.py +54 -0
- api/skill_endpoints.py +453 -0
- api/task_http_api.py +1856 -0
- api/webhooks.py +52 -0
- api/x402_endpoints.py +280 -0
- avatar/README.md +44 -0
- avatar/__init__.py +1 -0
- avatar/config/rob.json +7 -0
- avatar/mindprint.js +395 -0
- avatar/renders/rob.meta.json +20 -0
- avatar/renders/rob.png +0 -0
- avatar/studio.html +373 -0
- avatar/webview/avatar-live.js +44 -0
- cli/README.md +89 -0
- cli/__init__.py +0 -0
- cli/cli_surface.py +67 -0
- cli/commands/__init__.py +0 -0
- cli/commands/_bootstrap.py +49 -0
- cli/commands/_errors.py +53 -0
- cli/commands/chat.py +973 -0
- cli/commands/config.py +100 -0
- cli/commands/dashboard.py +103 -0
- cli/commands/doctor.py +222 -0
- cli/commands/email.py +174 -0
- cli/commands/gateway.py +346 -0
- cli/commands/goals.py +509 -0
- cli/commands/init.py +276 -0
- cli/commands/journey.py +30 -0
- cli/commands/kb.py +280 -0
- cli/commands/model.py +107 -0
- cli/commands/owner.py +431 -0
- cli/commands/pfp.py +232 -0
- cli/commands/run.py +414 -0
- cli/commands/serve.py +41 -0
- cli/commands/session.py +684 -0
- cli/commands/skill_install.py +786 -0
- cli/commands/skills.py +128 -0
- cli/commands/subagents.py +100 -0
- cli/commands/surface.py +81 -0
- cli/commands/telegram.py +185 -0
- cli/commands/todos.py +209 -0
- cli/commands/tools.py +102 -0
- cli/commands/update.py +316 -0
- cli/commands/whatsapp.py +175 -0
- cli/config_store.py +276 -0
- cli/gitignore.py +43 -0
- cli/inventory.py +46 -0
- cli/keys.py +101 -0
- cli/persona.py +17 -0
- cli/polyrob.py +137 -0
- cli/ui/__init__.py +64 -0
- cli/ui/activity.py +141 -0
- cli/ui/app.py +538 -0
- cli/ui/banner.py +233 -0
- cli/ui/blocks.py +534 -0
- cli/ui/bootstrap_notice.py +47 -0
- cli/ui/commands/__init__.py +41 -0
- cli/ui/commands/h_cron.py +64 -0
- cli/ui/commands/h_journey.py +164 -0
- cli/ui/commands/h_kb.py +121 -0
- cli/ui/commands/h_learn.py +100 -0
- cli/ui/commands/h_mcp.py +215 -0
- cli/ui/commands/h_self.py +113 -0
- cli/ui/commands/h_skills.py +314 -0
- cli/ui/commands/handlers.py +1672 -0
- cli/ui/commands/registry.py +426 -0
- cli/ui/dialog.py +425 -0
- cli/ui/event_registry.py +110 -0
- cli/ui/event_specs.py +45 -0
- cli/ui/events.py +433 -0
- cli/ui/identity.py +15 -0
- cli/ui/lifecycle.py +159 -0
- cli/ui/live_hooks.py +43 -0
- cli/ui/model_selector.py +532 -0
- cli/ui/persistent_loop.py +181 -0
- cli/ui/pick.py +113 -0
- cli/ui/plain_renderer.py +351 -0
- cli/ui/renderer.py +384 -0
- cli/ui/rich_renderer.py +533 -0
- cli/ui/secrets.py +115 -0
- cli/ui/state.py +376 -0
- cli/ui/statusbar.py +231 -0
- cli/ui/streaming.py +254 -0
- cli/ui/terminal_render.py +105 -0
- cli/ui/theme.py +132 -0
- cli/update/__init__.py +6 -0
- cli/update/context.py +121 -0
- cli/update/detect.py +176 -0
- cli/update/engine.py +95 -0
- cli/update/migrate_guarded.py +55 -0
- cli/update/process_guard.py +215 -0
- cli/update/runners.py +78 -0
- cli/update/snapshot.py +270 -0
- cli/update/versions.py +205 -0
- core/README.md +377 -0
- core/__init__.py +174 -0
- core/assets.py +87 -0
- core/async_bridge.py +76 -0
- core/autonomy_runtime.py +259 -0
- core/base_component.py +302 -0
- core/bootstrap.py +681 -0
- core/bot.py +310 -0
- core/config.py +833 -0
- core/constants.py +126 -0
- core/container.py +462 -0
- core/db_manifest.py +101 -0
- core/embedding.py +57 -0
- core/env.py +39 -0
- core/exceptions.py +287 -0
- core/flags.py +144 -0
- core/flags_catalog.py +345 -0
- core/home_migration.py +62 -0
- core/identity.py +118 -0
- core/initialization.py +1004 -0
- core/instance.py +537 -0
- core/interactive_gate.py +96 -0
- core/logging.py +436 -0
- core/owner_doc_writer.py +176 -0
- core/pairing.py +193 -0
- core/path_safety.py +20 -0
- core/paths.py +27 -0
- core/payment_config.py +21 -0
- core/permissions.py +322 -0
- core/runtime_config.py +96 -0
- core/runtime_paths.py +135 -0
- core/seams.py +35 -0
- core/secret_scan.py +25 -0
- core/secret_scrub.py +71 -0
- core/secrets.py +35 -0
- core/security_logging_filter.py +148 -0
- core/self_context_writer.py +333 -0
- core/self_evolution.py +294 -0
- core/session_context.py +47 -0
- core/sqlite_util.py +59 -0
- core/surfaces/__init__.py +16 -0
- core/surfaces/access.py +118 -0
- core/surfaces/binding.py +88 -0
- core/surfaces/bootstrap.py +78 -0
- core/surfaces/circuit.py +168 -0
- core/surfaces/continuity.py +77 -0
- core/surfaces/correspondents.py +228 -0
- core/surfaces/dispatcher.py +216 -0
- core/surfaces/envelopes.py +75 -0
- core/surfaces/gc.py +30 -0
- core/surfaces/idempotency.py +57 -0
- core/surfaces/inbound_webhook.py +124 -0
- core/surfaces/media.py +32 -0
- core/surfaces/message_router.py +91 -0
- core/surfaces/outbound_allowlist.py +80 -0
- core/surfaces/outbound_dispatcher.py +110 -0
- core/surfaces/outbound_mirror.py +37 -0
- core/surfaces/outbound_queue.py +107 -0
- core/surfaces/outbound_target.py +12 -0
- core/surfaces/owner_admin.py +29 -0
- core/surfaces/proactive.py +78 -0
- core/surfaces/progress.py +108 -0
- core/surfaces/rate_bucket.py +21 -0
- core/surfaces/registry.py +51 -0
- core/surfaces/rendering.py +53 -0
- core/surfaces/send_policy.py +14 -0
- core/surfaces/serialize.py +24 -0
- core/surfaces/session_chat_registry.py +112 -0
- core/surfaces/session_policy.py +60 -0
- core/surfaces/surface.py +288 -0
- core/surfaces/transcription.py +88 -0
- core/surfaces/voice_echo.py +26 -0
- core/surfaces/voice_guard.py +23 -0
- core/tickers.py +174 -0
- core/tool_catalog.py +192 -0
- core/version.py +75 -0
- core/wallet/__init__.py +5 -0
- core/wallet/agent_wallet.py +59 -0
- core/wallet/audit_sink.py +61 -0
- core/wallet/config.py +70 -0
- core/wallet/factory.py +87 -0
- core/wallet/policy.py +104 -0
- core/wallet/signer.py +51 -0
- cron/README.md +65 -0
- cron/__init__.py +0 -0
- cron/delivery.py +277 -0
- cron/digest.py +124 -0
- cron/jobs.py +207 -0
- cron/runner.py +297 -0
- cron/schedule.py +194 -0
- cron/scheduler.py +201 -0
- cron/service.py +57 -0
- cron/wake_gate.py +223 -0
- data/__init__.py +1 -0
- data/prompts/autov2_prompts.json +87 -0
- data/prompts/skills/browser-automation/SKILL.md +43 -0
- data/prompts/skills/coding-workflow/SKILL.md +36 -0
- data/prompts/skills/crypto-trading-safety/SKILL.md +53 -0
- data/prompts/skills/document-writing/SKILL.md +33 -0
- data/prompts/skills/email-comms/SKILL.md +33 -0
- data/prompts/skills/file-data-ops/SKILL.md +35 -0
- data/prompts/skills/hyperliquid-account-review/SKILL.md +48 -0
- data/prompts/skills/hyperliquid-market-data/SKILL.md +52 -0
- data/prompts/skills/hyperliquid-trading/SKILL.md +59 -0
- data/prompts/skills/lead-research/SKILL.md +86 -0
- data/prompts/skills/market-research-brief/SKILL.md +104 -0
- data/prompts/skills/person-analyzer/SKILL.md +124 -0
- data/prompts/skills/polymarket-market-research/SKILL.md +54 -0
- data/prompts/skills/polymarket-portfolio-review/SKILL.md +50 -0
- data/prompts/skills/polymarket-trading/SKILL.md +55 -0
- data/prompts/skills/presentation-creator/SKILL.md +36 -0
- data/prompts/skills/project-analyzer/SKILL.md +169 -0
- data/prompts/skills/rules.json +268 -0
- data/prompts/skills/secret-handling/SKILL.md +20 -0
- data/prompts/skills/skill-authoring/SKILL.md +31 -0
- data/prompts/skills/skill-security-review/SKILL.md +27 -0
- data/prompts/skills/social-discovery/SKILL.md +57 -0
- data/prompts/skills/task-planning/SKILL.md +33 -0
- data/prompts/skills/web-research/SKILL.md +39 -0
- data/prompts/skills/web-scraping/SKILL.md +73 -0
- data/prompts/skills/x-engagement/SKILL.md +69 -0
- data/prompts/system_prompts.json +4 -0
- data/subscription_channels.json +5 -0
- modules/README.md +503 -0
- modules/__init__.py +124 -0
- modules/auth/__init__.py +13 -0
- modules/auth/api_key_manager.py +157 -0
- modules/auth/identity_mapper.py +380 -0
- modules/auth/siwe_auth.py +273 -0
- modules/auth/tier_manager.py +173 -0
- modules/base_module.py +190 -0
- modules/credits/__init__.py +29 -0
- modules/credits/balance_manager.py +237 -0
- modules/credits/cost_utils.py +129 -0
- modules/credits/pricing.py +139 -0
- modules/credits/unified_ledger.py +148 -0
- modules/credits/usage_meter.py +310 -0
- modules/credits/usage_tracker.py +674 -0
- modules/database/__init__.py +33 -0
- modules/database/audit_log.py +322 -0
- modules/database/auth_tables.py +276 -0
- modules/database/connection.py +644 -0
- modules/database/connection_pool.py +305 -0
- modules/database/conversation_contexts.py +446 -0
- modules/database/database_manager.py +237 -0
- modules/database/hyperliquid.py +375 -0
- modules/database/polymarket.py +574 -0
- modules/database/user_mcp_servers.py +728 -0
- modules/database/user_profiles.py +562 -0
- modules/database/utils.py +107 -0
- modules/database/x402_tables.py +92 -0
- modules/eip8004/README.md +1069 -0
- modules/eip8004/__init__.py +65 -0
- modules/eip8004/contracts.py +498 -0
- modules/eip8004/models.py +271 -0
- modules/eip8004/registration.py +148 -0
- modules/eip8004/reputation.py +367 -0
- modules/eip8004/validation.py +277 -0
- modules/llm/__init__.py +97 -0
- modules/llm/adapters.py +963 -0
- modules/llm/anthropic_client.py +831 -0
- modules/llm/available_models.py +106 -0
- modules/llm/brain_scrubber.py +137 -0
- modules/llm/cache_hints.py +162 -0
- modules/llm/deepseek_client.py +562 -0
- modules/llm/gemini_client.py +1367 -0
- modules/llm/llm_client.py +1139 -0
- modules/llm/llm_client_registry.py +188 -0
- modules/llm/llm_factory.py +237 -0
- modules/llm/llm_manager.py +1036 -0
- modules/llm/messages.py +374 -0
- modules/llm/model_registry.py +1935 -0
- modules/llm/nvidia_client.py +125 -0
- modules/llm/openai_client.py +800 -0
- modules/llm/openrouter_client.py +969 -0
- modules/llm/profiles.py +178 -0
- modules/llm/test_anthropic_cache_capture.py +35 -0
- modules/llm/test_cached_pricing_derivation.py +24 -0
- modules/llm/think_scrubber.py +444 -0
- modules/llm/token_counter.py +268 -0
- modules/memory/__init__.py +35 -0
- modules/memory/backend_factory.py +83 -0
- modules/memory/cache_manager.py +186 -0
- modules/memory/episodic.py +108 -0
- modules/memory/local_vector_memory_provider.py +552 -0
- modules/memory/memory_manager.py +225 -0
- modules/memory/models.py +312 -0
- modules/memory/provider.py +210 -0
- modules/memory/registry.py +205 -0
- modules/memory/sqlite_memory_provider.py +767 -0
- modules/memory/task/__init__.py +52 -0
- modules/memory/task/compaction_manager.py +157 -0
- modules/memory/task/context_retriever.py +809 -0
- modules/memory/task/hierarchical_memory.py +1002 -0
- modules/memory/task/lexical_retriever.py +71 -0
- modules/memory/task/null_context_manager.py +213 -0
- modules/memory/task/phase_manager.py +625 -0
- modules/memory/task/reflection_service.py +157 -0
- modules/memory/task/semantic_retriever.py +218 -0
- modules/memory/task/task_context_manager.py +1165 -0
- modules/memory/task/test_forgetting_engages.py +24 -0
- modules/memory/task/test_reflection_llm.py +39 -0
- modules/memory/task/test_threat_scan.py +53 -0
- modules/memory/task/threat_scan.py +90 -0
- modules/memory/test_sqlite_memory_provider.py +106 -0
- modules/memory/user_profile_manager.py +527 -0
- modules/payments/__init__.py +11 -0
- modules/payments/deposit_monitor.py +478 -0
- modules/payments/price_oracle.py +70 -0
- modules/payments/treasury_sweeper.py +401 -0
- modules/payments/wallet_generator.py +145 -0
- modules/pfp/__init__.py +1 -0
- modules/pfp/config.py +92 -0
- modules/pfp/mesh.py +565 -0
- modules/pfp/push.py +69 -0
- modules/pfp/renderer.py +85 -0
- modules/pfp/store.py +82 -0
- modules/skills/__init__.py +1 -0
- modules/skills/skill_usage.py +248 -0
- modules/transcription/__init__.py +24 -0
- modules/transcription/base.py +20 -0
- modules/transcription/faster_whisper_transcriber.py +55 -0
- modules/x402/README.md +249 -0
- modules/x402/__init__.py +22 -0
- modules/x402/invoicing.py +416 -0
- modules/x402/middleware.py +364 -0
- modules/x402/settlement_watcher.py +145 -0
- modules/x402/x402_integration.py +344 -0
- polyrob-0.5.0.dist-info/METADATA +482 -0
- polyrob-0.5.0.dist-info/RECORD +745 -0
- polyrob-0.5.0.dist-info/WHEEL +5 -0
- polyrob-0.5.0.dist-info/entry_points.txt +2 -0
- polyrob-0.5.0.dist-info/licenses/LICENSE +21 -0
- polyrob-0.5.0.dist-info/top_level.txt +12 -0
- surfaces/README.md +70 -0
- surfaces/__init__.py +0 -0
- surfaces/email/__init__.py +0 -0
- surfaces/email/dedup.py +44 -0
- surfaces/email/harness.py +187 -0
- surfaces/email/inbound.py +159 -0
- surfaces/email/seed.py +60 -0
- surfaces/email/surface.py +105 -0
- surfaces/telegram/__init__.py +0 -0
- surfaces/telegram/dedup.py +83 -0
- surfaces/telegram/harness.py +886 -0
- surfaces/telegram/inbound.py +145 -0
- surfaces/telegram/interactive_tools.py +49 -0
- surfaces/telegram/markdown.py +347 -0
- surfaces/telegram/rate_limit.py +167 -0
- surfaces/telegram/surface.py +187 -0
- surfaces/telegram/voice.py +52 -0
- surfaces/whatsapp/__init__.py +0 -0
- surfaces/whatsapp/client.py +51 -0
- surfaces/whatsapp/harness.py +62 -0
- surfaces/whatsapp/inbound.py +132 -0
- surfaces/whatsapp/surface.py +64 -0
- surfaces/whatsapp/window.py +34 -0
- tools/README.md +578 -0
- tools/__init__.py +424 -0
- tools/alchemy/__init__.py +5 -0
- tools/alchemy/alchemy_tool.py +389 -0
- tools/anysite/__init__.py +17 -0
- tools/anysite/client.py +88 -0
- tools/anysite/tool.py +89 -0
- tools/base_tool.py +468 -0
- tools/browser/__init__.py +22 -0
- tools/browser/actions.py +118 -0
- tools/browser/browser.py +1457 -0
- tools/browser/browser_manager.py +690 -0
- tools/browser/context.py +1748 -0
- tools/browser/playwright_utils.py +137 -0
- tools/browser/views.py +56 -0
- tools/code_exec/SANDBOX_SECURITY.md +144 -0
- tools/code_exec/__init__.py +178 -0
- tools/code_exec/backend.py +73 -0
- tools/code_exec/backends/__init__.py +1 -0
- tools/code_exec/backends/docker.py +779 -0
- tools/code_exec/backends/local_subprocess.py +154 -0
- tools/code_exec/env_policy.py +45 -0
- tools/code_exec/result.py +42 -0
- tools/code_exec/sandbox_guard.py +59 -0
- tools/code_exec/tool.py +227 -0
- tools/coding/__init__.py +50 -0
- tools/coding/edit.py +107 -0
- tools/coding/search.py +113 -0
- tools/coding/tool.py +353 -0
- tools/collabland/__init__.py +5 -0
- tools/collabland/collabland_tool.py +471 -0
- tools/controller/__init__.py +0 -0
- tools/controller/_helpers.py +165 -0
- tools/controller/action_registration.py +1765 -0
- tools/controller/approval.py +244 -0
- tools/controller/approval_interactive.py +90 -0
- tools/controller/delegation.py +195 -0
- tools/controller/execution.py +797 -0
- tools/controller/execution_context.py +141 -0
- tools/controller/hooks.py +135 -0
- tools/controller/introspection.py +344 -0
- tools/controller/mcp_registrar.py +190 -0
- tools/controller/message_send.py +31 -0
- tools/controller/registry/__init__.py +4 -0
- tools/controller/registry/schema_generators.py +395 -0
- tools/controller/registry/schema_sanitizer.py +406 -0
- tools/controller/registry/service.py +1266 -0
- tools/controller/registry/views.py +151 -0
- tools/controller/service.py +409 -0
- tools/controller/tool_management.py +334 -0
- tools/controller/types.py +28 -0
- tools/controller/views.py +450 -0
- tools/cronjob_tools.py +166 -0
- tools/crypto_trade_gate.py +59 -0
- tools/descriptors.py +452 -0
- tools/dom/__init__.py +0 -0
- tools/dom/history_tree_processor/service.py +244 -0
- tools/dom/history_tree_processor/view.py +99 -0
- tools/dom/service.py +471 -0
- tools/dom/views.py +346 -0
- tools/email_tool.py +403 -0
- tools/exceptions.py +169 -0
- tools/filesystem.py +1038 -0
- tools/filesystem_docproc.py +331 -0
- tools/filesystem_pdf.py +755 -0
- tools/git/__init__.py +42 -0
- tools/git/tool.py +258 -0
- tools/github/__init__.py +33 -0
- tools/github/client.py +94 -0
- tools/github/tool.py +237 -0
- tools/goal_tools.py +309 -0
- tools/hyperliquid/__init__.py +56 -0
- tools/hyperliquid/models.py +233 -0
- tools/hyperliquid/service.py +1680 -0
- tools/knowledge_ingest.py +950 -0
- tools/mcp/README.md +535 -0
- tools/mcp/__init__.py +52 -0
- tools/mcp/catalog.py +115 -0
- tools/mcp/config.py +337 -0
- tools/mcp/mcp_tool.py +1415 -0
- tools/mcp/param_coercion.py +289 -0
- tools/mcp/protocol.py +1520 -0
- tools/mcp/rate_limit.py +50 -0
- tools/mcp/security.py +424 -0
- tools/mcp/self_install.py +111 -0
- tools/mcp/server_manager.py +829 -0
- tools/mcp/subscriptions.py +72 -0
- tools/mcp/user_mcp_service.py +899 -0
- tools/mcp/validation_tracker.py +89 -0
- tools/mcp/views.py +346 -0
- tools/oauth/__init__.py +17 -0
- tools/oauth/manager.py +75 -0
- tools/oauth/provider.py +69 -0
- tools/oauth/providers/__init__.py +1 -0
- tools/oauth/providers/generic_oauth2.py +96 -0
- tools/perplexity_tool.py +280 -0
- tools/polymarket/__init__.py +41 -0
- tools/polymarket/clob_adapter.py +76 -0
- tools/polymarket/models.py +182 -0
- tools/polymarket/service.py +2082 -0
- tools/self_env/__init__.py +61 -0
- tools/self_env/tool.py +274 -0
- tools/shell/__init__.py +80 -0
- tools/shell/backend_pool.py +118 -0
- tools/shell/discipline.py +62 -0
- tools/shell/executor.py +157 -0
- tools/shell/loopback_allow.py +87 -0
- tools/shell/process_registry.py +113 -0
- tools/shell/process_tool.py +131 -0
- tools/shell/state.py +129 -0
- tools/shell/tool.py +151 -0
- tools/task_tool.py +637 -0
- tools/twitter_tool.py +2086 -0
- tools/user_directory.py +179 -0
- tools/web_fetch/__init__.py +3 -0
- tools/web_fetch/fetcher.py +144 -0
- tools/web_fetch/render.py +44 -0
- tools/web_fetch/tool.py +60 -0
- tools/x402/__init__.py +60 -0
- tools/x402/client.py +47 -0
- tools/x402/invoice_tool.py +143 -0
- tools/x402/real_client.py +250 -0
- tools/x402/service.py +131 -0
- utils/README.md +498 -0
- utils/__init__.py +46 -0
- utils/auth_utils.py +114 -0
- utils/bounded_collections.py +162 -0
- utils/circuit_breaker.py +389 -0
- utils/gif_utils.py +435 -0
- utils/markdown_utils.py +291 -0
- utils/message_utils.py +147 -0
- utils/metrics.py +36 -0
- utils/path_validator.py +227 -0
- utils/rate_limit_manager.py +399 -0
- utils/result_size.py +296 -0
- utils/time_utils.py +206 -0
- utils/user_utils.py +188 -0
- webview/README.md +111 -0
- webview/RENAME_AND_ALIGN_HANDOFF.md +200 -0
- webview/__init__.py +8 -0
- webview/activity.py +636 -0
- webview/owner_auth.py +108 -0
- webview/pages.py +448 -0
- webview/repair_sessions.py +399 -0
- webview/server.py +4323 -0
- webview/server_launcher.py +112 -0
- webview/static/css/activity.css +89 -0
- webview/static/css/chat.css +1772 -0
- webview/static/css/components.css +1190 -0
- webview/static/css/config-panel.css +651 -0
- webview/static/css/pages/admin.css +766 -0
- webview/static/css/pages/profile.css +231 -0
- webview/static/css/pages/settings.css +297 -0
- webview/static/css/pages/signin.css +316 -0
- webview/static/css/style.css +4250 -0
- webview/static/css/variables.css +147 -0
- webview/static/css/workspace-fullwidth-fix.css +109 -0
- webview/static/img/favicon.ico +0 -0
- webview/static/js/activity.js +394 -0
- webview/static/js/admin/activity.js +186 -0
- webview/static/js/admin/dashboard.js +163 -0
- webview/static/js/admin/user_detail.js +400 -0
- webview/static/js/admin/users.js +174 -0
- webview/static/js/admin/utils.js +214 -0
- webview/static/js/chat.js +3178 -0
- webview/static/js/config-panel.js +522 -0
- webview/static/js/constants.js +20 -0
- webview/static/js/error-handler.js +226 -0
- webview/static/js/ethers.min.js +1 -0
- webview/static/js/event-filter.js +137 -0
- webview/static/js/event-store.js +351 -0
- webview/static/js/file-attachments.js +374 -0
- webview/static/js/file-loader.js +163 -0
- webview/static/js/index.js +103 -0
- webview/static/js/performance-utils.js +196 -0
- webview/static/js/profile.js +396 -0
- webview/static/js/screenshot.js +386 -0
- webview/static/js/session.js +1527 -0
- webview/static/js/settings.js +1288 -0
- webview/static/js/sidebar-data.js +766 -0
- webview/static/js/sidebar-toggle.js +296 -0
- webview/static/js/socket.io.min.js +7 -0
- webview/static/js/stats.js +355 -0
- webview/static/js/ui-utils.js +420 -0
- webview/static/js/workspace.js +966 -0
- webview/stats_service.py +613 -0
- webview/templates/__init__.py +1 -0
- webview/templates/__pycache__/__init__.cpython-311.pyc +0 -0
- webview/templates/activity.html +36 -0
- webview/templates/admin/activity.html +122 -0
- webview/templates/admin/dashboard.html +112 -0
- webview/templates/admin/user_detail.html +228 -0
- webview/templates/admin/users.html +105 -0
- webview/templates/autonomy.html +60 -0
- webview/templates/error.html +133 -0
- webview/templates/finance.html +90 -0
- webview/templates/identity.html +73 -0
- webview/templates/index.html +42 -0
- webview/templates/layout.html +125 -0
- webview/templates/memory.html +59 -0
- webview/templates/owner_login.html +15 -0
- webview/templates/profile.html +121 -0
- webview/templates/session.html +312 -0
- webview/templates/settings.html +534 -0
- webview/templates/sidebar.html +112 -0
- webview/templates/signin.html +213 -0
- webview/templates/status.html +14 -0
- webview/templates/system.html +45 -0
- webview/webgate.py +196 -0
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
import functools
|
|
5
|
+
from typing import Awaitable, Callable, Dict, List, Optional, Any
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import os
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
import time
|
|
10
|
+
import json
|
|
11
|
+
|
|
12
|
+
# Streaming callback contract: invoked once per LLM output chunk.
|
|
13
|
+
# Kept generic so core has no transport dependency (httpx/websocket/socketio
|
|
14
|
+
# live in the server layer that supplies the callback).
|
|
15
|
+
StreamChunkCallback = Callable[[str, str, str, int], Awaitable[None]]
|
|
16
|
+
"""async (session_id, agent_id, chunk, step) -> None"""
|
|
17
|
+
|
|
18
|
+
# Import centralized path manager
|
|
19
|
+
from agents.task.path import pm
|
|
20
|
+
|
|
21
|
+
# Import LLM error types for fallback handling
|
|
22
|
+
from core.exceptions import (
|
|
23
|
+
LLMError,
|
|
24
|
+
LLMProviderExhaustedError,
|
|
25
|
+
LLMPermanentError
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
# Import session manager
|
|
29
|
+
from agents.task.agent.session import SessionManager
|
|
30
|
+
from agents.task.agent.service import Agent
|
|
31
|
+
from tools.browser.browser import Browser
|
|
32
|
+
from tools.browser.context import BrowserContext
|
|
33
|
+
from tools.browser.browser_manager import BrowserManager
|
|
34
|
+
from tools.controller.service import Controller
|
|
35
|
+
# Import ActionResult for loop detection and intervention
|
|
36
|
+
from agents.task.agent.views import ActionResult
|
|
37
|
+
# ToolManager functionality is now integrated into Controller
|
|
38
|
+
|
|
39
|
+
# Concern-group mixins (pure code-motion from this module)
|
|
40
|
+
from agents.task.session.workspace import WorkspaceMixin
|
|
41
|
+
from agents.task.session.feed import FeedMixin
|
|
42
|
+
from agents.task.session.multi_agent import MultiAgentMixin
|
|
43
|
+
from agents.task.session.browser_pool import BrowserPoolMixin
|
|
44
|
+
from agents.task.session.hitl_ingress import HITLIngressMixin
|
|
45
|
+
from agents.task.session.cleanup import SessionCleanupMixin
|
|
46
|
+
from agents.task.session.execution import SessionExecutionMixin
|
|
47
|
+
from agents.task.session.hooks import SessionHooksMixin
|
|
48
|
+
|
|
49
|
+
# Default tools that are always loaded for every session
|
|
50
|
+
DEFAULT_TOOLS = ['filesystem', 'task']
|
|
51
|
+
|
|
52
|
+
# SA-01: strong refs for fire-and-forget async-delegation wake kicks. asyncio only
|
|
53
|
+
# weakly references a bare create_task result, so without this the kick task could be
|
|
54
|
+
# GC'd mid-flight before it re-runs the idle session's loop.
|
|
55
|
+
_ASYNC_DELEGATION_KICKS: set = set()
|
|
56
|
+
|
|
57
|
+
class SessionOrchestrator(WorkspaceMixin, FeedMixin, MultiAgentMixin, BrowserPoolMixin, HITLIngressMixin, SessionCleanupMixin, SessionExecutionMixin, SessionHooksMixin):
|
|
58
|
+
"""Orchestrates sessions with multiple agents.
|
|
59
|
+
|
|
60
|
+
The SessionOrchestrator is responsible for coordinating agents, services and
|
|
61
|
+
browser contexts throughout the session lifecycle. It has the following responsibilities:
|
|
62
|
+
|
|
63
|
+
1. Agent Management: Create, register and coordinate execution of agents
|
|
64
|
+
2. Service Management: Register services and ensure they're available to agents
|
|
65
|
+
3. Browser Management: Initialize and share browser instances across agents
|
|
66
|
+
4. Session Tracking: Track session status and completion
|
|
67
|
+
|
|
68
|
+
The orchestrator works with the Controller component, which manages the action registry
|
|
69
|
+
and handles action execution. This separation of concerns allows:
|
|
70
|
+
- Orchestrator: Focus on high-level session management, agent coordination, and service lifecycle
|
|
71
|
+
- Controller: Focus on action registration and execution, serving as the "action bridge" for agents
|
|
72
|
+
|
|
73
|
+
Services registered with the orchestrator are made available to the controller,
|
|
74
|
+
which then registers appropriate actions based on the service capabilities.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
# Class-level sentinels satisfy SessionContext Protocol structural check via getattr().
|
|
78
|
+
# Instance assignments in __init__ shadow these immediately.
|
|
79
|
+
session_id: str = "" # type: ignore[assignment]
|
|
80
|
+
user_id: Optional[str] = "" # type: ignore[assignment]
|
|
81
|
+
|
|
82
|
+
def __init__(self,
|
|
83
|
+
session_id: str,
|
|
84
|
+
user_id: Optional[str] = None,
|
|
85
|
+
config: Optional[Any] = None,
|
|
86
|
+
browser_manager: Optional[BrowserManager] = None,
|
|
87
|
+
container: Optional[Any] = None,
|
|
88
|
+
on_stream_chunk: Optional[StreamChunkCallback] = None):
|
|
89
|
+
"""Initialize session orchestrator.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
session_id: The unique identifier for this session
|
|
93
|
+
user_id: Optional user identifier for multi-user installations
|
|
94
|
+
config: Optional session configuration with orchestrator settings
|
|
95
|
+
browser_manager: Optional BrowserManager instance for centralized browser management
|
|
96
|
+
container: Optional DependencyContainer for tool loading
|
|
97
|
+
on_stream_chunk: Optional async callback that receives LLM output
|
|
98
|
+
chunks. Signature: (session_id, agent_id, chunk, step). When
|
|
99
|
+
None (core mode), streaming is disabled. The server layer
|
|
100
|
+
supplies an httpx/socketio implementation to bridge chunks to
|
|
101
|
+
the webview frontend.
|
|
102
|
+
"""
|
|
103
|
+
# Store container for tool loading
|
|
104
|
+
self.container = container
|
|
105
|
+
# Optional streaming sink (no transport coupling in core).
|
|
106
|
+
self._on_stream_chunk: Optional[StreamChunkCallback] = on_stream_chunk
|
|
107
|
+
# Get session manager instance
|
|
108
|
+
from agents.task.path import get_safe_singleton
|
|
109
|
+
self.session_manager = get_safe_singleton(SessionManager)()
|
|
110
|
+
|
|
111
|
+
# Clean the session ID
|
|
112
|
+
self.session_id = pm().clean_session_id(session_id)
|
|
113
|
+
|
|
114
|
+
# Validate session_id is provided
|
|
115
|
+
if not self.session_id:
|
|
116
|
+
raise ValueError("session_id is required for SessionOrchestrator")
|
|
117
|
+
|
|
118
|
+
# Store user_id
|
|
119
|
+
self.user_id = user_id
|
|
120
|
+
|
|
121
|
+
# Store configuration and set up orchestrator behavior
|
|
122
|
+
self.config = config
|
|
123
|
+
self.orchestrator_config = None
|
|
124
|
+
|
|
125
|
+
# Initialize TelemetryManager for orchestrator-level telemetry
|
|
126
|
+
try:
|
|
127
|
+
from agents.task.telemetry.manager import TelemetryManager
|
|
128
|
+
self.telemetry_manager = TelemetryManager(
|
|
129
|
+
session_id=self.session_id,
|
|
130
|
+
agent_id=f"orchestrator_{self.session_id}"
|
|
131
|
+
)
|
|
132
|
+
except Exception as e:
|
|
133
|
+
# Create dummy if initialization fails - include ALL telemetry methods
|
|
134
|
+
import logging
|
|
135
|
+
logger = logging.getLogger(f"task.orchestrator[{session_id}]")
|
|
136
|
+
logger.error(f"Failed to initialize TelemetryManager: {e}", exc_info=True)
|
|
137
|
+
|
|
138
|
+
class DummyTelemetryManager:
|
|
139
|
+
"""Dummy telemetry manager that no-ops all telemetry calls."""
|
|
140
|
+
def capture_agent_registration(self, *args, **kwargs): pass
|
|
141
|
+
def capture_multi_agent_relationship(self, *args, **kwargs): pass
|
|
142
|
+
def capture_tool_execution(self, *args, **kwargs): pass
|
|
143
|
+
def capture_error(self, *args, **kwargs): pass
|
|
144
|
+
def capture_llm_request(self, *args, **kwargs): pass
|
|
145
|
+
def capture_step(self, *args, **kwargs): pass
|
|
146
|
+
def flush_buffers(self, *args, **kwargs): pass
|
|
147
|
+
self.telemetry_manager = DummyTelemetryManager()
|
|
148
|
+
|
|
149
|
+
# Initialize unified usage tracker (replaces separate usage_meter + telemetry calls)
|
|
150
|
+
# This provides single source of truth for token tracking and cost calculation
|
|
151
|
+
try:
|
|
152
|
+
from modules.credits.usage_tracker import LLMUsageTracker
|
|
153
|
+
|
|
154
|
+
# Get required services from container
|
|
155
|
+
if container and hasattr(container, 'get_service'):
|
|
156
|
+
db = container.get_service('database_manager')
|
|
157
|
+
balance_manager = container.get_service('balance_manager')
|
|
158
|
+
|
|
159
|
+
if db and balance_manager:
|
|
160
|
+
self.usage_tracker = LLMUsageTracker(
|
|
161
|
+
db=db,
|
|
162
|
+
balance_manager=balance_manager,
|
|
163
|
+
telemetry_manager=self.telemetry_manager
|
|
164
|
+
)
|
|
165
|
+
import logging
|
|
166
|
+
logger = logging.getLogger(f"task.orchestrator[{session_id}]")
|
|
167
|
+
logger.info("✓ Unified usage tracker initialized")
|
|
168
|
+
else:
|
|
169
|
+
self.usage_tracker = None
|
|
170
|
+
import logging
|
|
171
|
+
logger = logging.getLogger(f"task.orchestrator[{session_id}]")
|
|
172
|
+
logger.debug("Usage tracker not initialized - missing database or balance_manager")
|
|
173
|
+
else:
|
|
174
|
+
self.usage_tracker = None
|
|
175
|
+
except Exception as e:
|
|
176
|
+
import logging
|
|
177
|
+
logger = logging.getLogger(f"task.orchestrator[{session_id}]")
|
|
178
|
+
logger.error(f"Failed to initialize usage tracker: {e}", exc_info=True)
|
|
179
|
+
self.usage_tracker = None
|
|
180
|
+
|
|
181
|
+
if config and hasattr(config, 'orchestrator'):
|
|
182
|
+
self.orchestrator_config = config.orchestrator
|
|
183
|
+
else:
|
|
184
|
+
# Create default orchestrator config based on current mode
|
|
185
|
+
from agents.task.config import OrchestratorConfigModel, TaskMode
|
|
186
|
+
mode = TaskMode.get_mode()
|
|
187
|
+
self.orchestrator_config = OrchestratorConfigModel.from_mode(mode)
|
|
188
|
+
|
|
189
|
+
# Create session
|
|
190
|
+
try:
|
|
191
|
+
self.session_id = self.session_manager.create_session(self.session_id, user_id)
|
|
192
|
+
# Create session logger (integrates with core logging automatically)
|
|
193
|
+
from agents.task.logging_config import get_task_logger
|
|
194
|
+
self.logger = get_task_logger("orchestrator", self.session_id)
|
|
195
|
+
self.logger.debug(f"Session {self.session_id} ready")
|
|
196
|
+
except Exception as e:
|
|
197
|
+
# Create logger before raising error
|
|
198
|
+
self.logger = logging.getLogger(f"task.orchestrator[{self.session_id}]")
|
|
199
|
+
self.logger.error(f"Error creating/getting session: {e}")
|
|
200
|
+
raise
|
|
201
|
+
|
|
202
|
+
# REMOVED: Injected browser tracking - BrowserManager owns all browsers
|
|
203
|
+
|
|
204
|
+
# Initialize or get BrowserManager
|
|
205
|
+
self.browser_manager = browser_manager
|
|
206
|
+
if not self.browser_manager:
|
|
207
|
+
# Try to get from dependency container
|
|
208
|
+
try:
|
|
209
|
+
from core.container import DependencyContainer
|
|
210
|
+
container = DependencyContainer.get_instance()
|
|
211
|
+
if container and container.has_service('browser_manager'):
|
|
212
|
+
self.browser_manager = container.get_service('browser_manager')
|
|
213
|
+
self.logger.debug("Got BrowserManager from container")
|
|
214
|
+
except Exception as e:
|
|
215
|
+
self.logger.debug(f"Could not get BrowserManager from container: {e}")
|
|
216
|
+
# Don't create a new instance - let it be created on demand if needed
|
|
217
|
+
|
|
218
|
+
# TodoManager will be initialized before Controller (not lazy loaded)
|
|
219
|
+
|
|
220
|
+
# Track initialization status
|
|
221
|
+
self._initialized = False
|
|
222
|
+
|
|
223
|
+
# Agent tracking dictionaries
|
|
224
|
+
self.agents = {} # agent_id -> agent_instance
|
|
225
|
+
self.agent_types = {} # agent_id -> agent type
|
|
226
|
+
self.agent_names = {} # agent_id -> agent name
|
|
227
|
+
self.agent_creation_times = {} # agent_id -> creation time
|
|
228
|
+
self.agent_models = {} # agent_id -> model name
|
|
229
|
+
self.agent_execution_sequence = [] # Track agent execution sequence
|
|
230
|
+
|
|
231
|
+
# CRITICAL FIX (Nov 26, 2025): Pending messages for pre-agent queuing
|
|
232
|
+
# When messages arrive before agents exist, store here for later delivery
|
|
233
|
+
self._pending_messages = [] # List of (text, kind, metadata) tuples
|
|
234
|
+
# SECURITY FIX: Lock to prevent race condition between submit_user_message and create_agent
|
|
235
|
+
self._pending_messages_lock = asyncio.Lock()
|
|
236
|
+
|
|
237
|
+
# NOTE: Message routing only - agents own their queues via HITLManager
|
|
238
|
+
|
|
239
|
+
# REMOVED: Legacy browser storage - use BrowserManager only
|
|
240
|
+
# Warn if deprecated parameters provided
|
|
241
|
+
# Get workspace directory from PathManager BEFORE creating controller
|
|
242
|
+
try:
|
|
243
|
+
# Always use PathManager for workspace directory
|
|
244
|
+
_pm = (self.container.get_service("path_manager")
|
|
245
|
+
if self.container and hasattr(self.container, "get_service") else None) or pm()
|
|
246
|
+
self._path_manager = _pm
|
|
247
|
+
self._workspace_dir = str(_pm.get_workspace_dir(self.session_id, self.user_id))
|
|
248
|
+
self.logger.debug(f"Using workspace directory: {self._workspace_dir}")
|
|
249
|
+
except Exception as e:
|
|
250
|
+
self.logger.error(f"Failed to create workspace directory: {e}")
|
|
251
|
+
# Raise exception - cannot proceed without workspace
|
|
252
|
+
raise RuntimeError(f"Critical error: Cannot properly set workspace path for session {self.session_id}. Session will be unusable.")
|
|
253
|
+
|
|
254
|
+
# REMOVED: TodoManager initialization - now handled by TaskTool
|
|
255
|
+
# TaskTool manages its own TodoManager instances per session
|
|
256
|
+
|
|
257
|
+
# Initialize Controller for tool management
|
|
258
|
+
# Try to get existing controller from container, or create new one
|
|
259
|
+
from tools.controller.service import Controller
|
|
260
|
+
|
|
261
|
+
# Get container (use provided one or try to get global instance)
|
|
262
|
+
container = self.container
|
|
263
|
+
if not container:
|
|
264
|
+
try:
|
|
265
|
+
from core.container import DependencyContainer
|
|
266
|
+
container = DependencyContainer.get_instance()
|
|
267
|
+
except Exception as e:
|
|
268
|
+
self.logger.debug(f"No container available: {e}")
|
|
269
|
+
|
|
270
|
+
# Try to get existing controller from container
|
|
271
|
+
self.controller = None
|
|
272
|
+
if container and hasattr(container, 'get_service'):
|
|
273
|
+
try:
|
|
274
|
+
self.controller = container.get_service('controller')
|
|
275
|
+
if self.controller:
|
|
276
|
+
self.logger.debug("Using existing controller from container")
|
|
277
|
+
except Exception as e:
|
|
278
|
+
self.logger.debug(f"Could not get controller from container: {e}")
|
|
279
|
+
|
|
280
|
+
# Create new controller if not found
|
|
281
|
+
if not self.controller:
|
|
282
|
+
self.controller = Controller(
|
|
283
|
+
container=container, # May be None
|
|
284
|
+
orchestrator=self # Controller gets session_id/user_id/workspace_dir from orchestrator
|
|
285
|
+
)
|
|
286
|
+
container_status = "with container" if container else "without container"
|
|
287
|
+
self.logger.debug(f"Created new Controller {container_status}, workspace: {self._workspace_dir}")
|
|
288
|
+
|
|
289
|
+
# Browser context management delegated to BrowserManager
|
|
290
|
+
# REMOVED: browser_context_in_use - BrowserManager tracks all contexts
|
|
291
|
+
|
|
292
|
+
# Initialize SubAgentManager for subtask delegation
|
|
293
|
+
from agents.task.agent.sub_agent_manager import SubAgentManager
|
|
294
|
+
self.sub_agent_manager = SubAgentManager(orchestrator=self)
|
|
295
|
+
self.logger.debug("SubAgentManager initialized")
|
|
296
|
+
|
|
297
|
+
# UP-12: durable async delegation registry (delegate_task background=true).
|
|
298
|
+
# Reuses the SubAgentManager (its semaphore/timeout) and delivers completions
|
|
299
|
+
# back into this session as a new turn via submit_user_message. Inert until a
|
|
300
|
+
# background delegation is dispatched.
|
|
301
|
+
from agents.task.agent.async_delegation import AsyncDelegationRegistry
|
|
302
|
+
# Durable write-through to autonomy_state.db (restart-durable record of
|
|
303
|
+
# dispatched/terminal delegations; recovery sweep in autonomy_state.py).
|
|
304
|
+
# Fail-open — a missing/broken store keeps the legacy volatile registry.
|
|
305
|
+
_deleg_store = None
|
|
306
|
+
try:
|
|
307
|
+
from agents.task.agent.autonomy_state import get_autonomy_state_store
|
|
308
|
+
_deleg_store = get_autonomy_state_store()
|
|
309
|
+
except Exception:
|
|
310
|
+
_deleg_store = None
|
|
311
|
+
self.async_delegation = AsyncDelegationRegistry(
|
|
312
|
+
self.sub_agent_manager, deliver=self._deliver_async_delegation,
|
|
313
|
+
store=_deleg_store, session_id=self.session_id, user_id=self.user_id,
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Track initialization status
|
|
317
|
+
self._initialized = False
|
|
318
|
+
# LLM concurrency management with validation
|
|
319
|
+
self.session_llm_limit = max(1, int(os.environ.get('SESSION_LLM_LIMIT', '5')))
|
|
320
|
+
self._session_llm_semaphore = asyncio.Semaphore(self.session_llm_limit)
|
|
321
|
+
self.agent_llm_limits = {} # agent_id -> limit mapping
|
|
322
|
+
|
|
323
|
+
# Register with session manager
|
|
324
|
+
try:
|
|
325
|
+
self.session_manager.register_agent(
|
|
326
|
+
session_id=self.session_id,
|
|
327
|
+
agent_name="orchestrator",
|
|
328
|
+
agent_id=f"orchestrator_{self.session_id}",
|
|
329
|
+
agent_type="SessionOrchestrator",
|
|
330
|
+
role="coordinator",
|
|
331
|
+
user_id=self.user_id
|
|
332
|
+
)
|
|
333
|
+
self.logger.debug("Registered orchestrator with session manager")
|
|
334
|
+
except Exception as e:
|
|
335
|
+
self.logger.warning(f"Failed to register orchestrator with session manager: {e}")
|
|
336
|
+
|
|
337
|
+
# Update session status
|
|
338
|
+
try:
|
|
339
|
+
self.session_manager.update_session_status(self.session_id, "initializing")
|
|
340
|
+
self.logger.debug("Updated session status to initializing")
|
|
341
|
+
except Exception as e:
|
|
342
|
+
self.logger.warning(f"Failed to update session status: {e}")
|
|
343
|
+
|
|
344
|
+
# Initialize async task tracking with better management
|
|
345
|
+
self._execution_tasks = []
|
|
346
|
+
|
|
347
|
+
# Track browser contexts separately from agents for proper cleanup
|
|
348
|
+
# This prevents context leaks if agent is removed before cleanup
|
|
349
|
+
self._browser_contexts = set() # Set of context IDs (agent_id)
|
|
350
|
+
|
|
351
|
+
async def initialize(self,
|
|
352
|
+
tool_ids=None,
|
|
353
|
+
tools_config=None,
|
|
354
|
+
controller=None) -> None:
|
|
355
|
+
"""Initialize the orchestrator with tools.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
tool_ids: List of tool IDs to load from container
|
|
359
|
+
tools_config: Tool configuration dict
|
|
360
|
+
controller: Pre-created controller instance
|
|
361
|
+
"""
|
|
362
|
+
if self._initialized:
|
|
363
|
+
self.logger.debug("Orchestrator already initialized")
|
|
364
|
+
return
|
|
365
|
+
|
|
366
|
+
try:
|
|
367
|
+
self.logger.info(f"Initializing orchestrator for session {self.session_id}")
|
|
368
|
+
|
|
369
|
+
# Use provided controller if available, otherwise keep existing one
|
|
370
|
+
if controller:
|
|
371
|
+
self.controller = controller
|
|
372
|
+
self.shared_controller = controller
|
|
373
|
+
self.logger.debug("Using provided controller instance")
|
|
374
|
+
|
|
375
|
+
# REMOVED: Early TodoManager initialization - now handled by TaskTool
|
|
376
|
+
|
|
377
|
+
# Check if tools were already loaded
|
|
378
|
+
existing_tools = self.controller.list_tools() if self.controller else []
|
|
379
|
+
if existing_tools:
|
|
380
|
+
self.logger.info(f"Controller already has {len(existing_tools)} tools loaded: {existing_tools}")
|
|
381
|
+
|
|
382
|
+
# Determine which tools to load
|
|
383
|
+
if not tool_ids and not existing_tools:
|
|
384
|
+
# No tools specified and none loaded - use comprehensive default
|
|
385
|
+
# Note: twitter removed from defaults - use the anysite tool for social media
|
|
386
|
+
from agents.task.tool_defaults import server_default_tools
|
|
387
|
+
tool_ids = server_default_tools()
|
|
388
|
+
self.logger.info(f"No tool_ids specified, loading comprehensive defaults: {tool_ids}")
|
|
389
|
+
elif tool_ids:
|
|
390
|
+
# Tools specified - ensure defaults are included
|
|
391
|
+
tools_to_load = list(tool_ids) # Copy to avoid modifying caller's list
|
|
392
|
+
for default in DEFAULT_TOOLS:
|
|
393
|
+
if default not in tools_to_load and default not in existing_tools:
|
|
394
|
+
tools_to_load.append(default)
|
|
395
|
+
tool_ids = tools_to_load
|
|
396
|
+
|
|
397
|
+
# Parse MCP server specifications from tool_ids AND tools_config
|
|
398
|
+
# Format: "mcp:servername" or "mcp:user:servername"
|
|
399
|
+
# If "mcp:X" and X is a registered tool class, load that tool directly
|
|
400
|
+
# Otherwise treat it as a regular MCP server
|
|
401
|
+
from tools.descriptors import get_tool_class
|
|
402
|
+
|
|
403
|
+
mcp_servers_requested = []
|
|
404
|
+
parsed_tool_ids = []
|
|
405
|
+
|
|
406
|
+
# Helper to check if mcp:X should load a tool or an MCP server
|
|
407
|
+
def process_mcp_spec(server_id: str):
|
|
408
|
+
"""Process mcp:X spec - load tool if registered, otherwise MCP server."""
|
|
409
|
+
if not server_id.startswith("mcp:"):
|
|
410
|
+
return
|
|
411
|
+
spec = server_id[4:] # Remove "mcp:" prefix
|
|
412
|
+
|
|
413
|
+
# Check if this is a registered tool (not a regular MCP server)
|
|
414
|
+
# NOTE: Most MCP servers (like polymarket) are NOT registered as tools.
|
|
415
|
+
# They're accessed purely via mcp_execute_tool.
|
|
416
|
+
if get_tool_class(spec) is not None:
|
|
417
|
+
if spec not in parsed_tool_ids:
|
|
418
|
+
parsed_tool_ids.append(spec)
|
|
419
|
+
self.logger.info(f"Tool '{spec}' requested via {server_id}")
|
|
420
|
+
else:
|
|
421
|
+
# Regular MCP server (or pseudo-MCP server like polymarket)
|
|
422
|
+
if spec not in mcp_servers_requested:
|
|
423
|
+
mcp_servers_requested.append(spec)
|
|
424
|
+
if "mcp" not in parsed_tool_ids:
|
|
425
|
+
parsed_tool_ids.append("mcp")
|
|
426
|
+
|
|
427
|
+
# Check tools_config["mcp_servers"] (UI sends mcp_servers separately)
|
|
428
|
+
mcp_servers_from_config = (tools_config or {}).get("mcp_servers", [])
|
|
429
|
+
if mcp_servers_from_config:
|
|
430
|
+
self.logger.info(f"MCP servers from tools_config: {mcp_servers_from_config}")
|
|
431
|
+
for server_id in mcp_servers_from_config:
|
|
432
|
+
process_mcp_spec(server_id)
|
|
433
|
+
|
|
434
|
+
for tool_id in (tool_ids or []):
|
|
435
|
+
if tool_id.startswith("mcp:"):
|
|
436
|
+
# Use generic processor for mcp: specs
|
|
437
|
+
process_mcp_spec(tool_id)
|
|
438
|
+
else:
|
|
439
|
+
parsed_tool_ids.append(tool_id)
|
|
440
|
+
|
|
441
|
+
tool_ids = parsed_tool_ids
|
|
442
|
+
|
|
443
|
+
if mcp_servers_requested:
|
|
444
|
+
self.logger.info(f"MCP servers requested: {mcp_servers_requested}")
|
|
445
|
+
|
|
446
|
+
# Filter out already loaded tools
|
|
447
|
+
if tool_ids and existing_tools:
|
|
448
|
+
tool_ids = [t for t in tool_ids if t not in existing_tools]
|
|
449
|
+
if not tool_ids:
|
|
450
|
+
self.logger.info("All requested tools are already loaded, skipping tool loading")
|
|
451
|
+
|
|
452
|
+
if tool_ids:
|
|
453
|
+
try:
|
|
454
|
+
# Load tools through the controller
|
|
455
|
+
loaded_tools = await self.controller.load_tools_from_container(tool_ids)
|
|
456
|
+
total_actions = len(self.controller.registry.list_action_names())
|
|
457
|
+
self.logger.info(f"✓ Loaded {len(loaded_tools)} tools: {', '.join(f'{k}({len(self.controller.registry.get_actions_by_tool(k))})' for k in loaded_tools.keys())}")
|
|
458
|
+
|
|
459
|
+
# Special handling for browser tool
|
|
460
|
+
if "browser" in tool_ids and "browser" not in loaded_tools:
|
|
461
|
+
# Try to get from browser_manager
|
|
462
|
+
if self.browser_manager:
|
|
463
|
+
try:
|
|
464
|
+
browser = await self._ensure_browser_available()
|
|
465
|
+
if browser:
|
|
466
|
+
self.controller.add_tool("browser", browser)
|
|
467
|
+
loaded_tools["browser"] = browser
|
|
468
|
+
self.logger.info("Loaded browser tool from browser_manager")
|
|
469
|
+
except Exception as e:
|
|
470
|
+
self.logger.error(f"Failed to get browser from browser_manager: {e}")
|
|
471
|
+
else:
|
|
472
|
+
self.logger.warning("Browser requested but browser_manager not available")
|
|
473
|
+
|
|
474
|
+
# Initialize MCP if available and configured
|
|
475
|
+
if "mcp" in loaded_tools:
|
|
476
|
+
self.logger.debug("MCP tool available")
|
|
477
|
+
mcp_service = loaded_tools["mcp"]
|
|
478
|
+
|
|
479
|
+
# Determine which servers to use
|
|
480
|
+
requested_servers = mcp_servers_requested if mcp_servers_requested else None
|
|
481
|
+
|
|
482
|
+
# Check tools_config for server list (multiple formats supported)
|
|
483
|
+
if not requested_servers and tools_config:
|
|
484
|
+
# Format 1: tools_config["mcp_servers"] = ["mcp:anysite", "mcp:ghost"]
|
|
485
|
+
if "mcp_servers" in tools_config:
|
|
486
|
+
mcp_servers_list = tools_config.get("mcp_servers", [])
|
|
487
|
+
# Strip "mcp:" prefix and filter out registered tools
|
|
488
|
+
# (those are loaded as tools, not MCP servers)
|
|
489
|
+
requested_servers = []
|
|
490
|
+
for s in mcp_servers_list:
|
|
491
|
+
name = s[4:] if s.startswith("mcp:") else s
|
|
492
|
+
# Skip if this is a registered tool (handled separately)
|
|
493
|
+
if get_tool_class(name) is None:
|
|
494
|
+
requested_servers.append(name)
|
|
495
|
+
if requested_servers:
|
|
496
|
+
self.logger.info(f"MCP servers from tools_config[mcp_servers]: {requested_servers}")
|
|
497
|
+
# Format 2 (legacy): tools_config["mcp"]["servers"] = ["anysite", "ghost"]
|
|
498
|
+
elif "mcp" in tools_config:
|
|
499
|
+
mcp_config = tools_config.get("mcp", {})
|
|
500
|
+
if "servers" in mcp_config:
|
|
501
|
+
requested_servers = mcp_config["servers"]
|
|
502
|
+
|
|
503
|
+
# Set up user context for user-specific MCP servers
|
|
504
|
+
if self.user_id and hasattr(mcp_service, 'set_user_context'):
|
|
505
|
+
mcp_service.set_user_context(self.user_id)
|
|
506
|
+
|
|
507
|
+
# Set up user context for ALL tools that support it (generic approach)
|
|
508
|
+
if self.user_id:
|
|
509
|
+
for tool_name, tool_instance in loaded_tools.items():
|
|
510
|
+
if hasattr(tool_instance, 'set_user_context'):
|
|
511
|
+
tool_instance.set_user_context(self.user_id)
|
|
512
|
+
self.logger.info(f"User context set for tool '{tool_name}'")
|
|
513
|
+
|
|
514
|
+
# Continue MCP setup if MCP was loaded
|
|
515
|
+
if "mcp" in loaded_tools:
|
|
516
|
+
mcp_service = loaded_tools["mcp"]
|
|
517
|
+
# Load user MCP service if available
|
|
518
|
+
if self.user_id and self.container and hasattr(mcp_service, 'set_user_mcp_service'):
|
|
519
|
+
try:
|
|
520
|
+
user_mcp_service = self.container.get_service('user_mcp_service')
|
|
521
|
+
if user_mcp_service:
|
|
522
|
+
mcp_service.set_user_mcp_service(user_mcp_service)
|
|
523
|
+
self.logger.info("User MCP service configured")
|
|
524
|
+
except Exception as e:
|
|
525
|
+
self.logger.warning(f"Could not get user MCP service: {e}")
|
|
526
|
+
|
|
527
|
+
# Load user servers if any user: servers were requested
|
|
528
|
+
user_servers_requested = [s for s in (requested_servers or []) if s.startswith("user:")]
|
|
529
|
+
if user_servers_requested and hasattr(mcp_service, 'load_user_servers'):
|
|
530
|
+
# C6: pass this session's tenant explicitly (don't rely on the
|
|
531
|
+
# shared self._current_user_id, which a concurrent session mutates).
|
|
532
|
+
load_result = await mcp_service.load_user_servers(user_id=self.user_id)
|
|
533
|
+
# Handle both old (int) and new (UserServersLoadResult) return types
|
|
534
|
+
if hasattr(load_result, 'loaded_count'):
|
|
535
|
+
user_count = load_result.loaded_count
|
|
536
|
+
if load_result.failed_count > 0:
|
|
537
|
+
self.logger.warning(
|
|
538
|
+
f"Failed to load {load_result.failed_count} user MCP servers: "
|
|
539
|
+
f"{[f['name'] for f in load_result.failed_servers]}"
|
|
540
|
+
)
|
|
541
|
+
if load_result.timed_out:
|
|
542
|
+
self.logger.warning("User MCP server loading timed out")
|
|
543
|
+
else:
|
|
544
|
+
user_count = load_result # Old int return type
|
|
545
|
+
self.logger.info(f"Loaded {user_count} user MCP servers")
|
|
546
|
+
|
|
547
|
+
if requested_servers:
|
|
548
|
+
self.logger.info(
|
|
549
|
+
f"Configuring MCP with servers: {requested_servers}"
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
# FIX #4: Convert user server request format to internal format
|
|
553
|
+
# API format: "user:servername" (after stripping mcp: prefix)
|
|
554
|
+
# Internal format: "user_{user_id}::servername"
|
|
555
|
+
# This ensures set_requested_servers() filter matches actual server names
|
|
556
|
+
normalized_servers = []
|
|
557
|
+
for server in requested_servers:
|
|
558
|
+
if server.startswith("user:") and self.user_id:
|
|
559
|
+
# Convert "user:servername" → "user_{user_id}::servername"
|
|
560
|
+
server_name = server[5:] # Remove "user:" prefix
|
|
561
|
+
internal_name = f"user_{self.user_id}::{server_name}"
|
|
562
|
+
normalized_servers.append(internal_name)
|
|
563
|
+
self.logger.debug(f"Normalized user server: {server} → {internal_name}")
|
|
564
|
+
else:
|
|
565
|
+
normalized_servers.append(server)
|
|
566
|
+
|
|
567
|
+
requested_servers = normalized_servers
|
|
568
|
+
|
|
569
|
+
# Verify requested servers are available (optional check)
|
|
570
|
+
if hasattr(mcp_service, 'server_manager') and mcp_service.server_manager:
|
|
571
|
+
available = mcp_service.server_manager.get_all_tools()
|
|
572
|
+
# Check against internal format (user servers now normalized)
|
|
573
|
+
# Note: polymarket uses gateway, not server_manager, so it won't appear here
|
|
574
|
+
missing = [s for s in requested_servers if s not in available and s != 'polymarket']
|
|
575
|
+
if missing:
|
|
576
|
+
self.logger.warning(
|
|
577
|
+
f"Requested MCP servers not in server_manager: {missing}"
|
|
578
|
+
)
|
|
579
|
+
else:
|
|
580
|
+
self.logger.info(
|
|
581
|
+
f"All requested MCP servers are available: {list(available.keys())}"
|
|
582
|
+
)
|
|
583
|
+
|
|
584
|
+
# FIX (Jan 2026): ALWAYS apply server filtering, even without server_manager
|
|
585
|
+
# This is critical for polymarket which uses gateway, not server_manager
|
|
586
|
+
# Previously this was inside the server_manager check, causing polymarket
|
|
587
|
+
# to never appear in the prompt when it was the only MCP server selected
|
|
588
|
+
if hasattr(mcp_service, 'set_requested_servers'):
|
|
589
|
+
mcp_service.set_requested_servers(requested_servers)
|
|
590
|
+
self.logger.info(
|
|
591
|
+
f"Applied MCP server filter: {requested_servers}"
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
# MCP tools use discovery pattern - no waiting needed
|
|
595
|
+
# Agent discovers tools via mcp_list_tools and executes via mcp_execute_tool
|
|
596
|
+
if 'mcp' in loaded_tools:
|
|
597
|
+
self.logger.info("✅ MCP tool loaded - tools accessible via mcp_execute_tool")
|
|
598
|
+
|
|
599
|
+
# Log final tool count
|
|
600
|
+
total_actions = len(self.controller.registry.list_action_names())
|
|
601
|
+
self.logger.info(f"📊 Total registered actions: {total_actions}")
|
|
602
|
+
|
|
603
|
+
except Exception as e:
|
|
604
|
+
self.logger.error(f"Error loading tools: {e}")
|
|
605
|
+
# Load minimum required tools
|
|
606
|
+
try:
|
|
607
|
+
from tools.filesystem import FileSystem
|
|
608
|
+
fs_tool = FileSystem('filesystem', self.config, self.container)
|
|
609
|
+
await fs_tool.initialize()
|
|
610
|
+
self.controller.add_tool('filesystem', fs_tool)
|
|
611
|
+
self.logger.info("Loaded filesystem tool as fallback")
|
|
612
|
+
except Exception as fallback_error:
|
|
613
|
+
self.logger.error(f"Failed to load even fallback tools: {fallback_error}")
|
|
614
|
+
|
|
615
|
+
# Mark as initialized
|
|
616
|
+
self._initialized = True
|
|
617
|
+
self.logger.info(f"✅ Orchestrator initialized successfully for session {self.session_id}")
|
|
618
|
+
|
|
619
|
+
except Exception as e:
|
|
620
|
+
self.logger.error(f"Failed to initialize orchestrator: {e}")
|
|
621
|
+
raise
|
|
622
|
+
|
|
623
|
+
def cancel(self) -> None:
|
|
624
|
+
"""Cancel all agents in this session.
|
|
625
|
+
|
|
626
|
+
Sets cancellation flag on all agents, causing them to stop
|
|
627
|
+
execution at the next step boundary.
|
|
628
|
+
"""
|
|
629
|
+
self.logger.info(f"❌ Cancelling session {self.session_id}")
|
|
630
|
+
|
|
631
|
+
# Cancel all agents
|
|
632
|
+
cancelled_count = 0
|
|
633
|
+
for agent_id, agent in self.agents.items():
|
|
634
|
+
try:
|
|
635
|
+
if hasattr(agent, 'cancel'):
|
|
636
|
+
agent.cancel()
|
|
637
|
+
cancelled_count += 1
|
|
638
|
+
self.logger.debug(f"Cancelled agent {agent_id}")
|
|
639
|
+
except Exception as e:
|
|
640
|
+
self.logger.error(f"Error cancelling agent {agent_id}: {e}")
|
|
641
|
+
|
|
642
|
+
if cancelled_count > 0:
|
|
643
|
+
self.logger.info(f"Cancelled {cancelled_count} agent(s) in session {self.session_id}")
|
|
644
|
+
|
|
645
|
+
# Update session status
|
|
646
|
+
if hasattr(self, 'session_manager') and self.session_manager:
|
|
647
|
+
try:
|
|
648
|
+
self.session_manager.update_session_status(self.session_id, 'cancelled')
|
|
649
|
+
self.logger.debug(f"Updated session status to cancelled")
|
|
650
|
+
except Exception as e:
|
|
651
|
+
self.logger.error(f"Error updating session status: {e}")
|
|
652
|
+
|
|
653
|
+
# REMOVED: get_todo_manager() - Deprecated method removed
|
|
654
|
+
# Agent now accesses TaskTool directly via controller.get_tool('task')
|
|
655
|
+
|
|
656
|
+
# The run_session method has been removed as it references attributes that don't exist
|
|
657
|
+
# in the SessionOrchestrator class (active_sessions, task_available, container, etc.)
|
|
658
|
+
# This functionality should be implemented in the AutoV2Agent class instead.
|
|
659
|
+
|
|
660
|
+
async def create_agent_from_profile(
|
|
661
|
+
self,
|
|
662
|
+
profile_id: str,
|
|
663
|
+
task: str,
|
|
664
|
+
llm: Any = None,
|
|
665
|
+
overrides: Optional[Dict[str, Any]] = None,
|
|
666
|
+
share_controller: bool = True,
|
|
667
|
+
**kwargs
|
|
668
|
+
) -> Agent:
|
|
669
|
+
"""Create an agent from a profile configuration.
|
|
670
|
+
|
|
671
|
+
Args:
|
|
672
|
+
profile_id: Profile identifier to load
|
|
673
|
+
task: Task description
|
|
674
|
+
llm: Optional LLM override
|
|
675
|
+
overrides: Optional profile overrides
|
|
676
|
+
share_controller: Whether to use the shared controller (default: True)
|
|
677
|
+
**kwargs: Additional agent parameters
|
|
678
|
+
|
|
679
|
+
Returns:
|
|
680
|
+
Configured Agent instance
|
|
681
|
+
"""
|
|
682
|
+
self.logger.info(f"Creating agent from profile '{profile_id}' (share_controller={share_controller})")
|
|
683
|
+
|
|
684
|
+
# Pass profile_id and share_controller to create_agent
|
|
685
|
+
return await self.create_agent(
|
|
686
|
+
task=task,
|
|
687
|
+
llm=llm,
|
|
688
|
+
profile_id=profile_id,
|
|
689
|
+
profile_overrides=overrides,
|
|
690
|
+
share_controller=share_controller,
|
|
691
|
+
**kwargs
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
# --- SessionContext Protocol adapter methods ---
|
|
695
|
+
|
|
696
|
+
def get_agents(self) -> dict:
|
|
697
|
+
return self.agents
|
|
698
|
+
|
|
699
|
+
def get_tool_call_tracker(self):
|
|
700
|
+
if self.agents:
|
|
701
|
+
agent = next(iter(self.agents.values()))
|
|
702
|
+
return getattr(agent, 'tool_call_tracker', None)
|
|
703
|
+
return None
|
|
704
|
+
|
|
705
|
+
def get_sub_agent_manager(self):
|
|
706
|
+
return getattr(self, 'sub_agent_manager', None)
|
|
707
|
+
|
|
708
|
+
async def _deliver_async_delegation(self, rec, block: str) -> None:
|
|
709
|
+
"""Deliver a background-delegation completion back into THIS session as a new
|
|
710
|
+
turn (UP-12). Routes through submit_user_message (kind='delegation_result') so
|
|
711
|
+
it re-enters via the HITL queue -> run-loop drain -> inject_user_guidance,
|
|
712
|
+
preserving message-role alternation. submit_user_message itself parks the
|
|
713
|
+
message if no agent is active yet and can raise MessageQueueFullError if the
|
|
714
|
+
queue is full — AsyncDelegationRegistry wraps this call, so a failure here is
|
|
715
|
+
logged, not fatal.
|
|
716
|
+
|
|
717
|
+
P1-5: the RESULT is always delivered (submit_user_message parks it if no loop
|
|
718
|
+
is active, or hands it to an active loop). Only the WAKE KICK — forging a fresh
|
|
719
|
+
run to drain an idle session — is bounded by the shared ReentryBudget, because
|
|
720
|
+
that is the ping-pong risk (a self-triggering delegation chain re-forging runs
|
|
721
|
+
indefinitely). The budget is applied UNCONDITIONALLY (the kick fires regardless
|
|
722
|
+
of SELF_WAKE_ENABLED — previously it was only budgeted when self-wake was on, so
|
|
723
|
+
the server default left it unbounded) and FAIL-CLOSED (a budget-guard error
|
|
724
|
+
skips the kick rather than opening the storm rail). A genuine owner turn resets
|
|
725
|
+
the budget."""
|
|
726
|
+
# A3: single-source the forged-turn kind so producer, marker-recompute, and
|
|
727
|
+
# the security gate all reference one constant (see self_wake.FORGED_TURN_KINDS).
|
|
728
|
+
from agents.task.agent.core.self_wake import DELEGATION_RESULT_KIND
|
|
729
|
+
await self.submit_user_message(
|
|
730
|
+
agent_id=getattr(rec, "parent_agent_id", None),
|
|
731
|
+
text=block,
|
|
732
|
+
kind=DELEGATION_RESULT_KIND,
|
|
733
|
+
metadata={
|
|
734
|
+
"source": "async_delegation",
|
|
735
|
+
"delegation_id": rec.delegation_id,
|
|
736
|
+
"status": rec.status,
|
|
737
|
+
},
|
|
738
|
+
)
|
|
739
|
+
# SA-01: if the parent loop already ended (the agent called done() /
|
|
740
|
+
# conversational-exit before the background child finished), submit_user_message
|
|
741
|
+
# only PARKED the result in the HITL queue — nothing would ever drain it, so the
|
|
742
|
+
# deliverable the user was promised as "a new turn" silently vanished. Kick a
|
|
743
|
+
# fresh run to drain it. Idempotent: run_session refuses concurrent execution
|
|
744
|
+
# ("session is already executing"), so this is a cheap no-op when a loop is still
|
|
745
|
+
# active. The kick is set by TaskAgent.register_orchestrator; absent (bare
|
|
746
|
+
# orchestrator / tests) => byte-identical to the parked-only UP-12 behavior.
|
|
747
|
+
kick = getattr(self, "_wake_kick", None)
|
|
748
|
+
if kick is None:
|
|
749
|
+
return
|
|
750
|
+
# P1-5: budget the kick (unconditional + fail-CLOSED). An exhausted budget parks
|
|
751
|
+
# the result without forging another run; a budget-guard error also skips the
|
|
752
|
+
# kick (never open the storm rail on an error).
|
|
753
|
+
try:
|
|
754
|
+
from agents.task.agent.core.self_wake import get_reentry_budget
|
|
755
|
+
if not get_reentry_budget().try_consume(self.session_id):
|
|
756
|
+
self.logger.info(
|
|
757
|
+
f"🛌 re-entry budget exhausted — parking async-delegation result "
|
|
758
|
+
f"without a wake kick for session {self.session_id}"
|
|
759
|
+
)
|
|
760
|
+
return
|
|
761
|
+
except Exception:
|
|
762
|
+
self.logger.debug(
|
|
763
|
+
"re-entry budget guard errored — skipping wake kick (fail-closed)",
|
|
764
|
+
exc_info=True,
|
|
765
|
+
)
|
|
766
|
+
return
|
|
767
|
+
try:
|
|
768
|
+
import asyncio as _asyncio
|
|
769
|
+
t = _asyncio.create_task(kick())
|
|
770
|
+
_ASYNC_DELEGATION_KICKS.add(t)
|
|
771
|
+
t.add_done_callback(_ASYNC_DELEGATION_KICKS.discard)
|
|
772
|
+
except Exception:
|
|
773
|
+
self.logger.debug("async-delegation wake kick failed (fail-open)", exc_info=True)
|
|
774
|
+
|
|
775
|
+
def get_telemetry_manager(self):
|
|
776
|
+
return getattr(self, 'telemetry_manager', None)
|