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,612 @@
|
|
|
1
|
+
"""Session cleanup mixin (roadmap P9; code-motion from orchestrator.py).
|
|
2
|
+
|
|
3
|
+
The session teardown method (browser release, agent/tool cleanup, status update),
|
|
4
|
+
split out of SessionOrchestrator as a whole cohesive method so orchestrator.py
|
|
5
|
+
shrinks. SessionOrchestrator composes SessionCleanupMixin; callers
|
|
6
|
+
(task_agent_lite, tests) use orchestrator.cleanup() unchanged via MRO.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import asyncio
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
import time
|
|
14
|
+
from datetime import datetime
|
|
15
|
+
from agents.task.path import pm
|
|
16
|
+
from typing import Any, Dict, List, Optional
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
20
|
+
# cleanup(status=...) is called with free-form strings from a handful of production
|
|
21
|
+
# sites (task_agent_lite.py's TTL/LRU eviction passes "suspended"; app-shutdown
|
|
22
|
+
# reap passes the default "completed"). Map the ones we've actually seen to the
|
|
23
|
+
# small terminal vocabulary finalize_episode's outcome accepts
|
|
24
|
+
# ({"done","failed","partial","cancelled"}); anything unrecognized is dropped
|
|
25
|
+
# (no episode written) rather than guessed at.
|
|
26
|
+
_CLEANUP_STATUS_TO_OUTCOME = {
|
|
27
|
+
"completed": "done",
|
|
28
|
+
"suspended": "partial", # paged-out/evicted, not a genuine completion
|
|
29
|
+
"error": "failed",
|
|
30
|
+
"failed": "failed",
|
|
31
|
+
"cancelled": "cancelled",
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
_SUMMARY_MAX_CHARS = 500
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _derive_closing_chat_summary(orchestrator) -> Optional[str]:
|
|
38
|
+
"""Best-effort short H-MEM summary of the CLOSING chat session (Task 6 Part A).
|
|
39
|
+
|
|
40
|
+
Sourced from the (non-sub-agent) session's ``ContextRetriever._format_session_summary()``
|
|
41
|
+
— the same Layer-1 "[HIERARCHICAL MEMORY - SESSION CONTEXT]" text the retriever
|
|
42
|
+
already builds for in-session context injection, reached via
|
|
43
|
+
``agent.task_context_manager.get_session(session_id).context_retriever``.
|
|
44
|
+
|
|
45
|
+
Gated by ``AutonomyConfig.continuity_bridge_enabled()`` at the call site (this
|
|
46
|
+
function is only invoked when the flag is on) so behaviour when the bridge is
|
|
47
|
+
disabled is byte-identical to before Task 6 (summary stays None).
|
|
48
|
+
|
|
49
|
+
Fail-open: ANY error (missing attribute, no H-MEM session, provider hiccup) is
|
|
50
|
+
swallowed and returns None — a summary failure must never break cleanup.
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
agent = None
|
|
54
|
+
for candidate in getattr(orchestrator, "agents", {}).values():
|
|
55
|
+
if not getattr(candidate, "_is_sub_agent", False):
|
|
56
|
+
agent = candidate
|
|
57
|
+
break
|
|
58
|
+
if agent is None:
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
task_context_manager = getattr(agent, "task_context_manager", None)
|
|
62
|
+
if task_context_manager is None:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
session_id = getattr(orchestrator, "session_id", None)
|
|
66
|
+
session_data = task_context_manager.get_session(session_id)
|
|
67
|
+
retriever = getattr(session_data, "context_retriever", None) if session_data else None
|
|
68
|
+
if retriever is None:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
text = retriever._format_session_summary()
|
|
72
|
+
text = (text or "").strip()
|
|
73
|
+
return text[:_SUMMARY_MAX_CHARS] or None
|
|
74
|
+
except Exception:
|
|
75
|
+
logger.warning("continuity bridge summary derivation failed", exc_info=True)
|
|
76
|
+
return None
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class SessionCleanupMixin:
|
|
80
|
+
"""Session teardown for SessionOrchestrator."""
|
|
81
|
+
|
|
82
|
+
async def cleanup(
|
|
83
|
+
self,
|
|
84
|
+
preserve_workspace: bool = False,
|
|
85
|
+
preserve_agents: bool = True,
|
|
86
|
+
status: str = "completed",
|
|
87
|
+
full_cleanup: bool = False
|
|
88
|
+
) -> None:
|
|
89
|
+
"""Clean up orchestrator resources.
|
|
90
|
+
|
|
91
|
+
This method releases resources owned by THIS session only.
|
|
92
|
+
It does NOT cleanup the shared BrowserManager (that's for app shutdown only).
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
preserve_workspace: Whether to preserve workspace files
|
|
96
|
+
preserve_agents: Whether to keep agents in registry for continuous chat (default True)
|
|
97
|
+
status: Status to mark in metadata (completed, error, cancelled)
|
|
98
|
+
full_cleanup: If True, release ALL resources (agents, LLMs, tools).
|
|
99
|
+
If False, only release browser contexts (for continuous chat).
|
|
100
|
+
"""
|
|
101
|
+
# C-N1: fire on_session_end once, on a full cleanup (fail-open; no-op unless
|
|
102
|
+
# hooks registered). Runs before teardown so hooks can still read session state.
|
|
103
|
+
if full_cleanup and not getattr(self, "_session_end_fired", False):
|
|
104
|
+
self._session_end_fired = True
|
|
105
|
+
run_hooks = getattr(self, "_run_session_end_hooks", None)
|
|
106
|
+
if run_hooks is not None:
|
|
107
|
+
await run_hooks(
|
|
108
|
+
session_id=getattr(self, "session_id", None),
|
|
109
|
+
user_id=getattr(self, "user_id", None),
|
|
110
|
+
status=status,
|
|
111
|
+
)
|
|
112
|
+
# Episodic-memory write: one row per genuine CHAT session completion.
|
|
113
|
+
# Goal/cron runs already record their OWN episode at the dispatcher/
|
|
114
|
+
# runner site — they're marked via autonomy_marker.mark_autonomous, so
|
|
115
|
+
# skip them here to avoid a double-recorded episode for the same run.
|
|
116
|
+
# Fail-open: a memory error must NEVER block session teardown.
|
|
117
|
+
try:
|
|
118
|
+
from agents.task.goals.autonomy_marker import is_autonomous
|
|
119
|
+
_episode_session_id = getattr(self, "session_id", None)
|
|
120
|
+
if not is_autonomous(_episode_session_id):
|
|
121
|
+
outcome = _CLEANUP_STATUS_TO_OUTCOME.get((status or "").lower())
|
|
122
|
+
if outcome is not None:
|
|
123
|
+
from modules.memory.episodic import finalize_episode
|
|
124
|
+
# Task 6 Part A: best-effort H-MEM summary of the closing
|
|
125
|
+
# session, ONLY when the continuity bridge is enabled (keeps
|
|
126
|
+
# the flag-off path byte-identical: summary stays None).
|
|
127
|
+
_summary = None
|
|
128
|
+
try:
|
|
129
|
+
from agents.task.constants import AutonomyConfig
|
|
130
|
+
if AutonomyConfig.continuity_bridge_enabled():
|
|
131
|
+
_summary = _derive_closing_chat_summary(self)
|
|
132
|
+
except Exception:
|
|
133
|
+
_summary = None
|
|
134
|
+
# P2-5: chat episodes were written with no task, no spend, and
|
|
135
|
+
# no steps, so the session-start digest rendered every chat row
|
|
136
|
+
# as `- chat:done $0.00 ""` (and under-reported total spend).
|
|
137
|
+
# Pass the main agent's task and collect_provenance (the goal
|
|
138
|
+
# path already does this). Fail-open — a provenance hiccup must
|
|
139
|
+
# never block the episode write.
|
|
140
|
+
_task = None
|
|
141
|
+
try:
|
|
142
|
+
for _a in (getattr(self, "agents", None) or {}).values():
|
|
143
|
+
_t = getattr(_a, "task", None)
|
|
144
|
+
if _t:
|
|
145
|
+
_task = str(_t)[:500]
|
|
146
|
+
break
|
|
147
|
+
except Exception:
|
|
148
|
+
_task = None
|
|
149
|
+
_prov = {"spend_usd": 0.0, "steps": 0, "artifacts": []}
|
|
150
|
+
try:
|
|
151
|
+
from modules.memory.episodic import collect_provenance
|
|
152
|
+
_prov = await collect_provenance(self)
|
|
153
|
+
except Exception:
|
|
154
|
+
pass
|
|
155
|
+
await finalize_episode(
|
|
156
|
+
session_id=_episode_session_id,
|
|
157
|
+
user_id=getattr(self, "user_id", None),
|
|
158
|
+
kind="chat",
|
|
159
|
+
task=_task,
|
|
160
|
+
outcome=outcome,
|
|
161
|
+
summary=_summary,
|
|
162
|
+
spend_usd=_prov.get("spend_usd", 0.0),
|
|
163
|
+
steps=_prov.get("steps", 0),
|
|
164
|
+
artifacts=_prov.get("artifacts", []),
|
|
165
|
+
thread_key=getattr(self, "_chat_session_key", None),
|
|
166
|
+
meta={"source": "session_end", "status": status},
|
|
167
|
+
)
|
|
168
|
+
except Exception:
|
|
169
|
+
self.logger.warning("chat episodic write failed", exc_info=True)
|
|
170
|
+
|
|
171
|
+
# Computer-use posture: release this session's persistent shell sandbox
|
|
172
|
+
# container + revoke its published loopback ports (best-effort, no-op
|
|
173
|
+
# unless the shell tool was used at posture>=1). Without this the container
|
|
174
|
+
# only gets swept by reap_orphans (>1h) and its loopback ports linger in
|
|
175
|
+
# the SSRF allowlist. Import-guarded + fail-open — never block teardown.
|
|
176
|
+
try:
|
|
177
|
+
from tools.shell.backend_pool import teardown_session as _shell_teardown
|
|
178
|
+
await _shell_teardown(getattr(self, "session_id", None))
|
|
179
|
+
except Exception:
|
|
180
|
+
pass
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
# Release browser contexts for THIS session
|
|
184
|
+
# Use tracked contexts instead of agents dict to prevent leaks
|
|
185
|
+
if self.browser_manager:
|
|
186
|
+
try:
|
|
187
|
+
released_count = 0
|
|
188
|
+
failed_count = 0
|
|
189
|
+
|
|
190
|
+
# Get contexts to release from dedicated tracking set
|
|
191
|
+
contexts_to_release = set()
|
|
192
|
+
if hasattr(self, '_browser_contexts'):
|
|
193
|
+
contexts_to_release = self._browser_contexts.copy()
|
|
194
|
+
else:
|
|
195
|
+
# Fallback: use agents dict if tracking set not available
|
|
196
|
+
contexts_to_release = set(self.agents.keys())
|
|
197
|
+
self.logger.warning("Using fallback agent keys for browser cleanup (tracking set not found)")
|
|
198
|
+
|
|
199
|
+
# Release each tracked browser context
|
|
200
|
+
for agent_id in contexts_to_release:
|
|
201
|
+
try:
|
|
202
|
+
await self.browser_manager.release_context(agent_id, close=True)
|
|
203
|
+
released_count += 1
|
|
204
|
+
self.logger.debug(f"Released browser context for {agent_id}")
|
|
205
|
+
|
|
206
|
+
# Remove from tracking set
|
|
207
|
+
if hasattr(self, '_browser_contexts'):
|
|
208
|
+
self._browser_contexts.discard(agent_id)
|
|
209
|
+
|
|
210
|
+
# Track context release with telemetry
|
|
211
|
+
if hasattr(self, 'telemetry_manager') and self.telemetry_manager:
|
|
212
|
+
try:
|
|
213
|
+
self.telemetry_manager.capture_event(
|
|
214
|
+
event_type="browser_context_released",
|
|
215
|
+
data={
|
|
216
|
+
"agent_id": agent_id,
|
|
217
|
+
"session_id": self.session_id,
|
|
218
|
+
"status": status
|
|
219
|
+
}
|
|
220
|
+
)
|
|
221
|
+
except Exception:
|
|
222
|
+
pass
|
|
223
|
+
except Exception as e:
|
|
224
|
+
failed_count += 1
|
|
225
|
+
self.logger.error(f"Error releasing context for {agent_id}: {e}")
|
|
226
|
+
|
|
227
|
+
if released_count > 0:
|
|
228
|
+
self.logger.info(
|
|
229
|
+
f"Released {released_count} browser context(s) for session {self.session_id}"
|
|
230
|
+
+ (f" ({failed_count} failed)" if failed_count > 0 else "")
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
except Exception as e:
|
|
234
|
+
self.logger.error(f"Error releasing browser contexts: {e}", exc_info=True)
|
|
235
|
+
|
|
236
|
+
# NOTE: Do NOT call browser_manager.cleanup() here!
|
|
237
|
+
# That method closes the ENTIRE shared browser instance and is only
|
|
238
|
+
# meant for application shutdown, not per-session cleanup.
|
|
239
|
+
|
|
240
|
+
# On full (permanent) teardown, forget any cached per-agent browser
|
|
241
|
+
# configs for this session. Best-effort over every agent_id this
|
|
242
|
+
# session ever created (self.agents), not just currently-tracked
|
|
243
|
+
# contexts (_browser_contexts only has ones still allocated -- an
|
|
244
|
+
# agent_id whose context was already released in an earlier turn
|
|
245
|
+
# would otherwise never get its config forgotten).
|
|
246
|
+
if full_cleanup and self.browser_manager:
|
|
247
|
+
for agent_id in list(getattr(self, "agents", {}).keys()):
|
|
248
|
+
try:
|
|
249
|
+
self.browser_manager.forget_session_config(agent_id)
|
|
250
|
+
except Exception:
|
|
251
|
+
pass
|
|
252
|
+
|
|
253
|
+
# REMOVED: TodoManager cleanup - now handled by TaskTool
|
|
254
|
+
# TaskTool manages its own cleanup via _cleanup() method
|
|
255
|
+
|
|
256
|
+
# If full cleanup, release all resources (for session eviction)
|
|
257
|
+
if full_cleanup:
|
|
258
|
+
self.logger.info(f"Performing full cleanup for session {self.session_id}")
|
|
259
|
+
|
|
260
|
+
# PERSISTENCE: Save message history and agent state before cleanup
|
|
261
|
+
# Skip sub-agents - they don't need persistence (their results are captured)
|
|
262
|
+
for agent_id, agent in list(self.agents.items()):
|
|
263
|
+
# Skip sub-agents to prevent context pollution
|
|
264
|
+
if hasattr(agent, '_is_sub_agent') and agent._is_sub_agent:
|
|
265
|
+
self.logger.debug(f"Skipping persistence for sub-agent {agent_id}")
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
try:
|
|
269
|
+
# Save message history
|
|
270
|
+
if hasattr(agent, 'message_manager') and agent.message_manager:
|
|
271
|
+
agent.message_manager.save_to_disk(
|
|
272
|
+
session_id=self.session_id,
|
|
273
|
+
user_id=self.user_id
|
|
274
|
+
)
|
|
275
|
+
self.logger.info(f"💾 Saved message history for agent {agent_id}")
|
|
276
|
+
|
|
277
|
+
# Save agent state
|
|
278
|
+
if hasattr(agent, 'state') and hasattr(agent.state, 'save_to_file'):
|
|
279
|
+
from agents.task.path import pm
|
|
280
|
+
state_file = pm().create_file_path(
|
|
281
|
+
session_id=self.session_id,
|
|
282
|
+
subdir_name="data",
|
|
283
|
+
filename="agent_state.json",
|
|
284
|
+
user_id=self.user_id
|
|
285
|
+
)
|
|
286
|
+
if agent.state.save_to_file(state_file):
|
|
287
|
+
self.logger.info(f"💾 Saved agent state for agent {agent_id}")
|
|
288
|
+
|
|
289
|
+
# Save + release hierarchical memory (H-MEM). M1: close_session
|
|
290
|
+
# persists to disk AND removes the in-memory HierarchicalMemory
|
|
291
|
+
# entry — save_session alone left TaskContextManager._sessions (a
|
|
292
|
+
# process-wide singleton) growing unbounded on a long-running
|
|
293
|
+
# server / the autonomous goal-cron loop (close_session had no callers).
|
|
294
|
+
if hasattr(agent, 'task_context_manager') and agent.task_context_manager:
|
|
295
|
+
try:
|
|
296
|
+
# P0-7: run close (which may fire a session-close reflection
|
|
297
|
+
# aux-LLM call) OFF the event loop — synchronously here it
|
|
298
|
+
# froze the loop up to ~30s (and deadlocked its own metering).
|
|
299
|
+
# Mirrors the per-step reflection offload (memory_writer.py).
|
|
300
|
+
import asyncio as _asyncio
|
|
301
|
+
closed, _drained = await _asyncio.to_thread(
|
|
302
|
+
agent.task_context_manager.close_session_and_drain,
|
|
303
|
+
self.session_id,
|
|
304
|
+
self.user_id,
|
|
305
|
+
)
|
|
306
|
+
if closed:
|
|
307
|
+
self.logger.info(f"💾 Saved + released hierarchical memory for agent {agent_id}")
|
|
308
|
+
# P0-7: push the consolidated summary + any unsynced
|
|
309
|
+
# findings to the cross-session store (same path the
|
|
310
|
+
# per-step loop uses). Previously discarded at close.
|
|
311
|
+
if _drained:
|
|
312
|
+
try:
|
|
313
|
+
from modules.memory.registry import memory_sync_turn
|
|
314
|
+
await memory_sync_turn(
|
|
315
|
+
getattr(agent, 'task', '') or '',
|
|
316
|
+
"\n".join(_drained),
|
|
317
|
+
session_id=self.session_id,
|
|
318
|
+
user_id=self.user_id,
|
|
319
|
+
)
|
|
320
|
+
except Exception as sync_err:
|
|
321
|
+
self.logger.debug(
|
|
322
|
+
f"close-time memory sync skipped: {sync_err}"
|
|
323
|
+
)
|
|
324
|
+
else:
|
|
325
|
+
self.logger.warning(f"⚠️ No H-MEM session to close for agent {agent_id}")
|
|
326
|
+
except Exception as hmem_error:
|
|
327
|
+
self.logger.error(f"Failed to close H-MEM for agent {agent_id}: {hmem_error}")
|
|
328
|
+
except Exception as e:
|
|
329
|
+
self.logger.error(f"Failed to save state for agent {agent_id}: {e}")
|
|
330
|
+
|
|
331
|
+
# Close all agents
|
|
332
|
+
for agent_id, agent in list(self.agents.items()):
|
|
333
|
+
try:
|
|
334
|
+
if hasattr(agent, 'cleanup'):
|
|
335
|
+
await agent.cleanup()
|
|
336
|
+
self.logger.debug(f"Cleaned up agent {agent_id}")
|
|
337
|
+
except Exception as e:
|
|
338
|
+
self.logger.error(f"Error cleaning up agent {agent_id}: {e}")
|
|
339
|
+
|
|
340
|
+
# M2 FIX: close per-session ISOLATED aux/reflection LLM clients. These are
|
|
341
|
+
# built fresh (not cached, not in self.llm_clients) by
|
|
342
|
+
# _provision_aux_llm(isolated=True) for compaction/reflection, so nothing
|
|
343
|
+
# else closes them — their httpx connection pools would leak per session
|
|
344
|
+
# whenever COMPACTION_MODEL / AUX_* / REFLECTION_LLM_ENABLED is set.
|
|
345
|
+
for agent_id, agent in list(self.agents.items()):
|
|
346
|
+
for owner, attr in (
|
|
347
|
+
(getattr(agent, 'message_manager', None), 'aux_llm'),
|
|
348
|
+
(getattr(agent, 'task_context_manager', None), 'reflection_llm'),
|
|
349
|
+
# P2-8: the cheap 'judge' aux client is cached on the agent by
|
|
350
|
+
# _validate_output / background review / goal judge but was never
|
|
351
|
+
# closed here — its httpx pool leaked per session when
|
|
352
|
+
# AUX_MODEL_JUDGE / VALIDATE_OUTPUT / background review was active.
|
|
353
|
+
(agent, '_judge_llm'),
|
|
354
|
+
):
|
|
355
|
+
if owner is None:
|
|
356
|
+
continue
|
|
357
|
+
aux = getattr(owner, attr, None)
|
|
358
|
+
if aux is None:
|
|
359
|
+
continue
|
|
360
|
+
# aux is a BaseChatModel adapter wrapping the real client in ._client
|
|
361
|
+
underlying = getattr(aux, '_client', aux)
|
|
362
|
+
try:
|
|
363
|
+
cleanup_fn = getattr(underlying, 'cleanup', None) or getattr(underlying, 'aclose', None)
|
|
364
|
+
if cleanup_fn is not None:
|
|
365
|
+
import asyncio
|
|
366
|
+
if asyncio.iscoroutinefunction(cleanup_fn):
|
|
367
|
+
await cleanup_fn()
|
|
368
|
+
else:
|
|
369
|
+
cleanup_fn()
|
|
370
|
+
self.logger.debug(f"✓ Closed isolated {attr} for agent {agent_id}")
|
|
371
|
+
except Exception as e:
|
|
372
|
+
self.logger.warning(f"Error closing isolated {attr} for {agent_id}: {e}")
|
|
373
|
+
finally:
|
|
374
|
+
try:
|
|
375
|
+
setattr(owner, attr, None)
|
|
376
|
+
except Exception:
|
|
377
|
+
pass
|
|
378
|
+
|
|
379
|
+
# Clear message queues and callbacks from HITL managers
|
|
380
|
+
for agent_id, agent in list(self.agents.items()):
|
|
381
|
+
try:
|
|
382
|
+
if hasattr(agent, 'hitl_manager') and agent.hitl_manager:
|
|
383
|
+
agent.hitl_manager.clear_all_queues()
|
|
384
|
+
agent.hitl_manager.clear_callbacks()
|
|
385
|
+
self.logger.debug(f"Cleared HITL queues/callbacks for agent {agent_id}")
|
|
386
|
+
except Exception as e:
|
|
387
|
+
self.logger.error(f"Error clearing HITL resources for {agent_id}: {e}")
|
|
388
|
+
|
|
389
|
+
# Cancel background tasks
|
|
390
|
+
if hasattr(self, '_execution_tasks') and self._execution_tasks:
|
|
391
|
+
try:
|
|
392
|
+
for task in self._execution_tasks:
|
|
393
|
+
if not task.done():
|
|
394
|
+
task.cancel()
|
|
395
|
+
self._execution_tasks.clear()
|
|
396
|
+
self.logger.debug("Cancelled and cleared background tasks")
|
|
397
|
+
except Exception as e:
|
|
398
|
+
self.logger.error(f"Error cancelling background tasks: {e}")
|
|
399
|
+
|
|
400
|
+
# FIX #14: Clear workspace context cache for this session
|
|
401
|
+
try:
|
|
402
|
+
from agents.task.workspace_context import get_workspace_context
|
|
403
|
+
ws_ctx = get_workspace_context()
|
|
404
|
+
if ws_ctx:
|
|
405
|
+
ws_ctx.clear_session(self.session_id)
|
|
406
|
+
self.logger.debug(f"Cleared workspace context cache for session {self.session_id}")
|
|
407
|
+
except Exception as e:
|
|
408
|
+
self.logger.warning(f"Failed to clear workspace context cache: {e}")
|
|
409
|
+
|
|
410
|
+
# FIX: Cleanup user MCP servers loaded for this session
|
|
411
|
+
try:
|
|
412
|
+
if self.controller:
|
|
413
|
+
mcp_tool = self.controller.get_tool('mcp')
|
|
414
|
+
if mcp_tool and hasattr(mcp_tool, 'unload_user_servers'):
|
|
415
|
+
# C6: unload only THIS session's tenant servers, explicitly.
|
|
416
|
+
# Do NOT set_user_context(None) here — on the shared singleton
|
|
417
|
+
# that would null the context out from under a concurrent
|
|
418
|
+
# session; the per-action path re-sets it before every action.
|
|
419
|
+
unloaded_count = await mcp_tool.unload_user_servers(user_id=self.user_id)
|
|
420
|
+
if unloaded_count > 0:
|
|
421
|
+
self.logger.info(f"🔌 Unloaded {unloaded_count} user MCP servers")
|
|
422
|
+
except Exception as e:
|
|
423
|
+
self.logger.warning(f"Failed to cleanup user MCP servers: {e}")
|
|
424
|
+
|
|
425
|
+
# Clear agent references (only if not explicitly preserving them)
|
|
426
|
+
# Note: We're inside the full_cleanup block, so this gives a way to preserve
|
|
427
|
+
# agents even during full cleanup (useful for graceful session handoff)
|
|
428
|
+
if not preserve_agents:
|
|
429
|
+
agent_count = len(self.agents)
|
|
430
|
+
self.agents.clear()
|
|
431
|
+
self.agent_types.clear()
|
|
432
|
+
self.agent_names.clear()
|
|
433
|
+
self.agent_models.clear()
|
|
434
|
+
self.logger.info(f"Cleared {agent_count} agents from orchestrator during full_cleanup")
|
|
435
|
+
else:
|
|
436
|
+
self.logger.info(
|
|
437
|
+
f"Preserved {len(self.agents)} agents even during full_cleanup "
|
|
438
|
+
f"(agent IDs: {list(self.agents.keys())})"
|
|
439
|
+
)
|
|
440
|
+
|
|
441
|
+
# Close LLM clients if available
|
|
442
|
+
if hasattr(self, 'llm_clients') and self.llm_clients:
|
|
443
|
+
for client_name, client in list(self.llm_clients.items()):
|
|
444
|
+
try:
|
|
445
|
+
# Try multiple cleanup methods - different clients use different patterns
|
|
446
|
+
closed = False
|
|
447
|
+
|
|
448
|
+
# Method 1: async close()
|
|
449
|
+
if hasattr(client, 'close') and callable(client.close):
|
|
450
|
+
import asyncio
|
|
451
|
+
if asyncio.iscoroutinefunction(client.close):
|
|
452
|
+
await client.close()
|
|
453
|
+
else:
|
|
454
|
+
client.close()
|
|
455
|
+
closed = True
|
|
456
|
+
|
|
457
|
+
# Method 2: aclose() (aiohttp pattern)
|
|
458
|
+
elif hasattr(client, 'aclose') and callable(client.aclose):
|
|
459
|
+
await client.aclose()
|
|
460
|
+
closed = True
|
|
461
|
+
|
|
462
|
+
# Method 3: shutdown() (some HTTP clients)
|
|
463
|
+
elif hasattr(client, 'shutdown') and callable(client.shutdown):
|
|
464
|
+
await client.shutdown()
|
|
465
|
+
closed = True
|
|
466
|
+
|
|
467
|
+
# Method 4: Force close underlying HTTP client if accessible
|
|
468
|
+
elif hasattr(client, 'client') and hasattr(client.client, 'aclose'):
|
|
469
|
+
await client.client.aclose()
|
|
470
|
+
closed = True
|
|
471
|
+
|
|
472
|
+
# Method 5: Force close session if accessible
|
|
473
|
+
elif hasattr(client, 'session') and hasattr(client.session, 'close'):
|
|
474
|
+
await client.session.close()
|
|
475
|
+
closed = True
|
|
476
|
+
|
|
477
|
+
if closed:
|
|
478
|
+
self.logger.debug(f"✓ Closed LLM client {client_name}")
|
|
479
|
+
else:
|
|
480
|
+
self.logger.warning(
|
|
481
|
+
f"⚠️ LLM client {client_name} has no close method - "
|
|
482
|
+
f"connection may leak (type: {type(client).__name__})"
|
|
483
|
+
)
|
|
484
|
+
except Exception as e:
|
|
485
|
+
self.logger.error(f"Error closing LLM client {client_name}: {e}", exc_info=True)
|
|
486
|
+
self.llm_clients.clear()
|
|
487
|
+
|
|
488
|
+
# Flush telemetry
|
|
489
|
+
if hasattr(self, 'telemetry_manager') and self.telemetry_manager:
|
|
490
|
+
try:
|
|
491
|
+
await self.telemetry_manager.flush_buffers()
|
|
492
|
+
self.logger.debug("Flushed telemetry buffers")
|
|
493
|
+
except Exception as e:
|
|
494
|
+
self.logger.debug(f"Error flushing telemetry: {e}")
|
|
495
|
+
|
|
496
|
+
# Release controller tools with comprehensive cleanup
|
|
497
|
+
if self.controller:
|
|
498
|
+
try:
|
|
499
|
+
cleaned_count = 0
|
|
500
|
+
failed_count = 0
|
|
501
|
+
|
|
502
|
+
for tool_name in self.controller.list_tools():
|
|
503
|
+
tool = self.controller.get_tool(tool_name)
|
|
504
|
+
if not tool:
|
|
505
|
+
continue
|
|
506
|
+
|
|
507
|
+
try:
|
|
508
|
+
# Try multiple cleanup patterns
|
|
509
|
+
cleaned = False
|
|
510
|
+
|
|
511
|
+
# Method 1: _cleanup() (preferred async)
|
|
512
|
+
if hasattr(tool, '_cleanup') and callable(tool._cleanup):
|
|
513
|
+
import asyncio
|
|
514
|
+
if asyncio.iscoroutinefunction(tool._cleanup):
|
|
515
|
+
await tool._cleanup()
|
|
516
|
+
else:
|
|
517
|
+
tool._cleanup()
|
|
518
|
+
cleaned = True
|
|
519
|
+
|
|
520
|
+
# Method 2: cleanup() (alternative)
|
|
521
|
+
elif hasattr(tool, 'cleanup') and callable(tool.cleanup):
|
|
522
|
+
import asyncio
|
|
523
|
+
if asyncio.iscoroutinefunction(tool.cleanup):
|
|
524
|
+
await tool.cleanup()
|
|
525
|
+
else:
|
|
526
|
+
tool.cleanup()
|
|
527
|
+
cleaned = True
|
|
528
|
+
|
|
529
|
+
# Method 3: close() (HTTP clients, etc.)
|
|
530
|
+
elif hasattr(tool, 'close') and callable(tool.close):
|
|
531
|
+
import asyncio
|
|
532
|
+
if asyncio.iscoroutinefunction(tool.close):
|
|
533
|
+
await tool.close()
|
|
534
|
+
else:
|
|
535
|
+
tool.close()
|
|
536
|
+
cleaned = True
|
|
537
|
+
|
|
538
|
+
if cleaned:
|
|
539
|
+
cleaned_count += 1
|
|
540
|
+
self.logger.debug(f"✓ Cleaned up tool: {tool_name}")
|
|
541
|
+
else:
|
|
542
|
+
self.logger.debug(
|
|
543
|
+
f"⚠️ Tool {tool_name} has no cleanup method "
|
|
544
|
+
f"(type: {type(tool).__name__})"
|
|
545
|
+
)
|
|
546
|
+
except Exception as e:
|
|
547
|
+
failed_count += 1
|
|
548
|
+
self.logger.error(f"Error cleaning up tool {tool_name}: {e}")
|
|
549
|
+
|
|
550
|
+
if cleaned_count > 0 or failed_count > 0:
|
|
551
|
+
self.logger.info(
|
|
552
|
+
f"Cleaned up {cleaned_count} tool(s)"
|
|
553
|
+
+ (f" ({failed_count} failed)" if failed_count > 0 else "")
|
|
554
|
+
)
|
|
555
|
+
except Exception as e:
|
|
556
|
+
self.logger.error(f"Error during tool cleanup: {e}")
|
|
557
|
+
|
|
558
|
+
# Create a cleanup marker in the workspace if preserving workspace files
|
|
559
|
+
if preserve_workspace:
|
|
560
|
+
try:
|
|
561
|
+
# Create metadata file to indicate session status
|
|
562
|
+
import json
|
|
563
|
+
import time
|
|
564
|
+
from datetime import datetime
|
|
565
|
+
import os
|
|
566
|
+
|
|
567
|
+
from agents.task.path import pm
|
|
568
|
+
metadata_path = pm().create_file_path(self.session_id, ".", ".session_metadata.json")
|
|
569
|
+
metadata = {
|
|
570
|
+
"session_id": self.session_id,
|
|
571
|
+
"user_id": self.user_id,
|
|
572
|
+
"status": status,
|
|
573
|
+
"cleanup_time": datetime.now().isoformat(),
|
|
574
|
+
"timestamp": time.time(),
|
|
575
|
+
"workspace_preserved": True
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
with open(metadata_path, "w") as f:
|
|
579
|
+
json.dump(metadata, f, indent=2)
|
|
580
|
+
|
|
581
|
+
self.logger.info(f"Created workspace metadata file to mark preserved workspace")
|
|
582
|
+
except Exception as e:
|
|
583
|
+
self.logger.error(f"Error creating workspace metadata: {e}")
|
|
584
|
+
|
|
585
|
+
# Update session status if provided and not overwriting terminal states
|
|
586
|
+
# DEFENSIVE: Don't overwrite terminal statuses (error, failed, cancelled) with non-terminal
|
|
587
|
+
# Terminal states should only be changed by explicit user action or retry logic
|
|
588
|
+
TERMINAL_STATUSES = {'error', 'failed', 'cancelled'}
|
|
589
|
+
|
|
590
|
+
if self.session_manager and status:
|
|
591
|
+
current_status = self.session_manager.get_session_info(self.session_id)
|
|
592
|
+
if current_status:
|
|
593
|
+
current_status_value = current_status.get('status', '').lower()
|
|
594
|
+
|
|
595
|
+
# Protect terminal statuses from being overwritten with non-terminal status
|
|
596
|
+
if current_status_value in TERMINAL_STATUSES and status.lower() not in TERMINAL_STATUSES:
|
|
597
|
+
self.logger.debug(
|
|
598
|
+
f"Preserved terminal status '{current_status_value}', "
|
|
599
|
+
f"not overwriting with: {status}"
|
|
600
|
+
)
|
|
601
|
+
else:
|
|
602
|
+
self.session_manager.update_session_status(self.session_id, status)
|
|
603
|
+
self.logger.debug(f"Updated session status to: {status}")
|
|
604
|
+
else:
|
|
605
|
+
self.session_manager.update_session_status(self.session_id, status)
|
|
606
|
+
|
|
607
|
+
cleanup_type = "full" if full_cleanup else "partial"
|
|
608
|
+
self.logger.info(f"Session {self.session_id} {cleanup_type} cleanup complete, status: {status}")
|
|
609
|
+
|
|
610
|
+
except Exception as e:
|
|
611
|
+
self.logger.error(f"Error during cleanup: {e}")
|
|
612
|
+
|