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,276 @@
|
|
|
1
|
+
"""Logging / conversation-file output mixin (roadmap P9 decomposition; code-motion from service.py).
|
|
2
|
+
|
|
3
|
+
The response/context/conversation/tool-output logging concern, moved verbatim off
|
|
4
|
+
the ``Agent`` god-file. ``Agent`` composes ``LoggingIOMixin`` so call sites
|
|
5
|
+
(``self._log_response``, ``self._log_context_breakdown``, ``self._save_conversation``,
|
|
6
|
+
``self._log_tool_outputs`` — invoked from step.py/llm_runner.py) are unchanged.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import os
|
|
11
|
+
from datetime import datetime
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, List
|
|
14
|
+
|
|
15
|
+
from modules.llm.messages import BaseMessage, ToolMessage
|
|
16
|
+
from agents.task.agent.views import AgentOutput, ActionResult
|
|
17
|
+
from agents.task.telemetry.views import AgentRunTelemetryEvent
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LoggingIOMixin:
|
|
21
|
+
"""Response/context/conversation/tool-output logging for Agent."""
|
|
22
|
+
|
|
23
|
+
def _log_response(self, response: AgentOutput) -> None:
|
|
24
|
+
"""Log the model's response"""
|
|
25
|
+
if 'Success' in response.current_state.evaluation_previous_goal:
|
|
26
|
+
status_indicator = "SUCCESS"
|
|
27
|
+
elif 'Failed' in response.current_state.evaluation_previous_goal:
|
|
28
|
+
status_indicator = "FAILED"
|
|
29
|
+
else:
|
|
30
|
+
status_indicator = "UNKNOWN"
|
|
31
|
+
|
|
32
|
+
# Log concise brain state summary (memory is most important for continuity)
|
|
33
|
+
self.logger.info(f"Evaluation: [{status_indicator}] {response.current_state.evaluation_previous_goal}")
|
|
34
|
+
self.logger.info(f"Memory: {response.current_state.memory}")
|
|
35
|
+
|
|
36
|
+
# Log other fields at debug level to reduce noise
|
|
37
|
+
self.logger.debug(f"Next goal: {response.current_state.next_goal}")
|
|
38
|
+
self.logger.debug(f"Reasoning: {response.current_state.reasoning}")
|
|
39
|
+
if response.current_state.phase:
|
|
40
|
+
self.logger.debug(f"Phase: {response.current_state.phase}")
|
|
41
|
+
|
|
42
|
+
if response.action:
|
|
43
|
+
self.logger.info(f"Actions: {len(response.action)} action(s)")
|
|
44
|
+
for i, action in enumerate(response.action):
|
|
45
|
+
try:
|
|
46
|
+
action_dump = action.model_dump(exclude_unset=True)
|
|
47
|
+
if not action_dump:
|
|
48
|
+
# Handle empty action data
|
|
49
|
+
self.logger.debug(f" Action {i+1}/{len(response.action)}: Empty action data")
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
# Safely get first key
|
|
53
|
+
action_keys = list(action_dump.keys())
|
|
54
|
+
if not action_keys:
|
|
55
|
+
self.logger.debug(f" Action {i+1}/{len(response.action)}: No keys in action data")
|
|
56
|
+
continue
|
|
57
|
+
|
|
58
|
+
action_type = action_keys[0]
|
|
59
|
+
action_params = action_dump[action_type]
|
|
60
|
+
|
|
61
|
+
# Log both the action type and its parameters at INFO level
|
|
62
|
+
self.logger.info(f" Action {i+1}/{len(response.action)}: {action_type} - Parameters: {action_params}")
|
|
63
|
+
# Still keep the detailed JSON in debug for complete data
|
|
64
|
+
self.logger.debug(f" Details: {action.model_dump_json(exclude_unset=True)}")
|
|
65
|
+
except Exception as e:
|
|
66
|
+
# Log error but continue processing other actions
|
|
67
|
+
self.logger.error(f"Error logging action {i+1}: {str(e)}", exc_info=True)
|
|
68
|
+
continue
|
|
69
|
+
|
|
70
|
+
def _log_context_breakdown(self):
|
|
71
|
+
"""Log detailed context usage breakdown.
|
|
72
|
+
|
|
73
|
+
FIX (Context Optimization Phase 2): Real-time visibility into context consumption.
|
|
74
|
+
Logs breakdown by category: Foundation, H-MEM, Conversation, Tools
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
# Diagnostic/logging-only call — must peek, not drain, one-shot
|
|
78
|
+
# ephemeral messages (CX-H3). The real provider call in
|
|
79
|
+
# next_action_internal.py keeps the draining default.
|
|
80
|
+
messages = self.message_manager.get_messages_for_llm(consume_ephemeral=False)
|
|
81
|
+
|
|
82
|
+
# Count by type
|
|
83
|
+
foundation_tokens = 0
|
|
84
|
+
hmem_tokens = 0
|
|
85
|
+
conversation_tokens = 0
|
|
86
|
+
tool_tokens = 0
|
|
87
|
+
|
|
88
|
+
for msg in messages:
|
|
89
|
+
tokens = self.message_manager._count_message_tokens(msg)
|
|
90
|
+
|
|
91
|
+
if msg == self.message_manager._system_message:
|
|
92
|
+
foundation_tokens += tokens
|
|
93
|
+
elif msg == self.message_manager._initial_task_message:
|
|
94
|
+
foundation_tokens += tokens
|
|
95
|
+
elif hasattr(msg, 'content') and '[Session Memory]' in str(msg.content):
|
|
96
|
+
hmem_tokens += tokens
|
|
97
|
+
elif isinstance(msg, ToolMessage):
|
|
98
|
+
tool_tokens += tokens
|
|
99
|
+
else:
|
|
100
|
+
conversation_tokens += tokens
|
|
101
|
+
|
|
102
|
+
total_tokens = sum([foundation_tokens, hmem_tokens, conversation_tokens, tool_tokens])
|
|
103
|
+
usage_pct = (total_tokens / self.message_manager.max_input_tokens) * 100 if self.message_manager.max_input_tokens > 0 else 0
|
|
104
|
+
|
|
105
|
+
# Determine emoji
|
|
106
|
+
if usage_pct >= 65:
|
|
107
|
+
emoji = "🔴"
|
|
108
|
+
elif usage_pct >= 55:
|
|
109
|
+
emoji = "🟡"
|
|
110
|
+
else:
|
|
111
|
+
emoji = "🟢"
|
|
112
|
+
|
|
113
|
+
self.logger.info(
|
|
114
|
+
f"{emoji} Step {self.state.n_steps} Context: {total_tokens:,}/{self.message_manager.max_input_tokens:,} "
|
|
115
|
+
f"({usage_pct:.1f}%) | "
|
|
116
|
+
f"Foundation: {foundation_tokens:,} | "
|
|
117
|
+
f"H-MEM: {hmem_tokens:,} | "
|
|
118
|
+
f"Conv: {conversation_tokens:,} | "
|
|
119
|
+
f"Tools: {tool_tokens:,}"
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Warning if approaching compaction
|
|
123
|
+
if usage_pct >= 55:
|
|
124
|
+
self.logger.warning(
|
|
125
|
+
f"⚠️ Context at {usage_pct:.0f}% - will compact at 65%"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
except Exception as e:
|
|
129
|
+
self.logger.debug(f"Context breakdown failed: {e}")
|
|
130
|
+
|
|
131
|
+
def _save_conversation(self, input_messages: list[BaseMessage], response: Any) -> None:
|
|
132
|
+
"""Save conversation to disk or telemetry for later retrieval"""
|
|
133
|
+
from agents.task.utils import safe_operation
|
|
134
|
+
|
|
135
|
+
# If no save path specified, just return
|
|
136
|
+
if not self.save_conversation_path:
|
|
137
|
+
return
|
|
138
|
+
|
|
139
|
+
def save_messages():
|
|
140
|
+
# Ensure path exists
|
|
141
|
+
path = Path(self.save_conversation_path)
|
|
142
|
+
os.makedirs(path.parent, exist_ok=True)
|
|
143
|
+
|
|
144
|
+
# Get encoding to use
|
|
145
|
+
encoding = self.save_conversation_path_encoding or 'utf-8'
|
|
146
|
+
|
|
147
|
+
with open(path, 'a', encoding=encoding) as f:
|
|
148
|
+
# If file is empty, add headers
|
|
149
|
+
if os.path.getsize(path) == 0:
|
|
150
|
+
f.write(f'# Task: {self.task}\n')
|
|
151
|
+
f.write(f'# Created at: {datetime.now().isoformat()}\n')
|
|
152
|
+
f.write(f'# Model: {self.model_name}\n')
|
|
153
|
+
f.write(f'# Agent ID: {self.agent_id}\n')
|
|
154
|
+
f.write(f'# Session ID: {self.session_id}\n\n')
|
|
155
|
+
|
|
156
|
+
# Write timestamped step header
|
|
157
|
+
f.write(f'\n## Step {self.state.n_steps} ({datetime.now().isoformat()})\n\n')
|
|
158
|
+
|
|
159
|
+
# Write messages
|
|
160
|
+
f.write('### Input Messages\n')
|
|
161
|
+
self._write_messages_to_file(f, input_messages)
|
|
162
|
+
|
|
163
|
+
# Write response
|
|
164
|
+
f.write('\n### Response\n')
|
|
165
|
+
self._write_response_to_file(f, response)
|
|
166
|
+
|
|
167
|
+
# Write horizontal rule
|
|
168
|
+
f.write('\n---\n')
|
|
169
|
+
|
|
170
|
+
safe_operation(
|
|
171
|
+
save_messages,
|
|
172
|
+
self.logger,
|
|
173
|
+
f"Failed to save conversation to {self.save_conversation_path}",
|
|
174
|
+
default_value=None
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def _write_messages_to_file(self, f: Any, messages: list[BaseMessage]) -> None:
|
|
178
|
+
"""Write messages to conversation file (sanitised)."""
|
|
179
|
+
import json
|
|
180
|
+
for message in messages:
|
|
181
|
+
f.write(f' {message.__class__.__name__} \n')
|
|
182
|
+
|
|
183
|
+
if isinstance(message.content, list):
|
|
184
|
+
for item in message.content:
|
|
185
|
+
if isinstance(item, dict) and item.get('type') == 'text':
|
|
186
|
+
clean_txt = self._sanitize_text_for_log(item.get('text', ''))
|
|
187
|
+
f.write(clean_txt.strip() + '\n')
|
|
188
|
+
elif isinstance(message.content, str):
|
|
189
|
+
try:
|
|
190
|
+
content = json.loads(message.content)
|
|
191
|
+
content = self._sanitize_structure_for_log(content)
|
|
192
|
+
f.write(json.dumps(content, indent=2) + '\n')
|
|
193
|
+
except json.JSONDecodeError:
|
|
194
|
+
f.write(self._sanitize_text_for_log(message.content.strip()) + '\n')
|
|
195
|
+
|
|
196
|
+
f.write('\n')
|
|
197
|
+
|
|
198
|
+
def _write_response_to_file(self, f: Any, response: Any) -> None:
|
|
199
|
+
"""Write model response to conversation file (sanitised)."""
|
|
200
|
+
import json
|
|
201
|
+
f.write(' RESPONSE\n')
|
|
202
|
+
try:
|
|
203
|
+
resp_dict = json.loads(response.model_dump_json(exclude_unset=True))
|
|
204
|
+
resp_dict = self._sanitize_structure_for_log(resp_dict)
|
|
205
|
+
f.write(json.dumps(resp_dict, indent=2))
|
|
206
|
+
except Exception:
|
|
207
|
+
# Fallback – write raw if sanitisation fails
|
|
208
|
+
f.write(json.dumps(json.loads(response.model_dump_json(exclude_unset=True)), indent=2))
|
|
209
|
+
|
|
210
|
+
def _log_agent_run(self) -> None:
|
|
211
|
+
"""Log the agent run"""
|
|
212
|
+
|
|
213
|
+
self.logger.debug(f'Version: {self.version}, Source: {self.source}')
|
|
214
|
+
self.telemetry_manager.capture_event(
|
|
215
|
+
AgentRunTelemetryEvent(
|
|
216
|
+
agent_id=self.agent_id,
|
|
217
|
+
use_vision=self.use_vision,
|
|
218
|
+
task=self.task,
|
|
219
|
+
model_name=self.model_name,
|
|
220
|
+
chat_model_library=self.chat_model_library,
|
|
221
|
+
version=self.version,
|
|
222
|
+
source=self.source,
|
|
223
|
+
)
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def _log_tool_outputs(self, actions: List[Any], results: List[ActionResult], step_number: int):
|
|
227
|
+
"""Log exact tool outputs with item counts (OPTIMIZATION: Task 1 - Nov 14, 2025)"""
|
|
228
|
+
if not self.tool_output_log_path:
|
|
229
|
+
return
|
|
230
|
+
|
|
231
|
+
try:
|
|
232
|
+
import json
|
|
233
|
+
from datetime import datetime as _dt
|
|
234
|
+
|
|
235
|
+
for idx, (action, result) in enumerate(zip(actions, results)):
|
|
236
|
+
tool_name = action.get('name') if isinstance(action, dict) else getattr(action, 'name', 'unknown')
|
|
237
|
+
arguments = action.get('arguments', {}) if isinstance(action, dict) else {}
|
|
238
|
+
|
|
239
|
+
log_entry = {
|
|
240
|
+
"timestamp": _dt.now().isoformat(),
|
|
241
|
+
"step": step_number,
|
|
242
|
+
"tool": tool_name,
|
|
243
|
+
"arguments": arguments,
|
|
244
|
+
"result": {
|
|
245
|
+
"content": result.extracted_content,
|
|
246
|
+
"error": result.error,
|
|
247
|
+
"bytes": len(result.extracted_content or "")
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
# Add item count for lists
|
|
252
|
+
if result.extracted_content:
|
|
253
|
+
try:
|
|
254
|
+
parsed = json.loads(result.extracted_content)
|
|
255
|
+
if isinstance(parsed, list):
|
|
256
|
+
log_entry["result"]["item_count"] = len(parsed)
|
|
257
|
+
log_entry["result"]["first_item"] = parsed[0] if parsed else None
|
|
258
|
+
except (json.JSONDecodeError, TypeError, AttributeError):
|
|
259
|
+
pass # Content may not be JSON - skip enhanced logging
|
|
260
|
+
|
|
261
|
+
# Write to JSONL
|
|
262
|
+
with open(self.tool_output_log_path, 'a') as f:
|
|
263
|
+
f.write(json.dumps(log_entry) + '\n')
|
|
264
|
+
|
|
265
|
+
# Console log with count
|
|
266
|
+
if "item_count" in log_entry["result"]:
|
|
267
|
+
count = log_entry["result"]["item_count"]
|
|
268
|
+
requested = arguments.get('count') or arguments.get('limit')
|
|
269
|
+
|
|
270
|
+
if requested:
|
|
271
|
+
self.logger.info(f"📊 {tool_name}: {count} items (requested: {requested})")
|
|
272
|
+
else:
|
|
273
|
+
self.logger.info(f"📊 {tool_name}: {count} items")
|
|
274
|
+
|
|
275
|
+
except Exception as e:
|
|
276
|
+
self.logger.error(f"Failed to log tool outputs: {e}")
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"""Loop-detection + intervention mixin (roadmap P9 decomposition; code-motion from service.py).
|
|
2
|
+
|
|
3
|
+
Stop-flag checks, multi-signal action-loop detection (action repetition + memory
|
|
4
|
+
similarity + goal stagnation), and the guidance-injection intervention. Moved
|
|
5
|
+
verbatim off the ``Agent`` god-file; ``Agent`` composes ``LoopDetectionMixin``
|
|
6
|
+
(call sites in the step loop and flow-efficiency tests unchanged via MRO).
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any, Dict, List, Optional
|
|
11
|
+
|
|
12
|
+
from modules.llm.messages import make_control_message, MessageOrigin
|
|
13
|
+
from agents.task.agent.views import ActionResult
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class LoopDetectionMixin:
|
|
17
|
+
"""Stop-flag handling + action-loop detection/intervention for Agent."""
|
|
18
|
+
|
|
19
|
+
def _check_if_stopped(self) -> bool:
|
|
20
|
+
"""Handle stop flag. Returns True if execution should stop.
|
|
21
|
+
|
|
22
|
+
NOTE: Pause functionality removed. This method now only checks for stop.
|
|
23
|
+
When stopped, it raises InterruptedError to break out of the current step.
|
|
24
|
+
Users can send a new message to continue with a different task.
|
|
25
|
+
"""
|
|
26
|
+
if self.state.stopped:
|
|
27
|
+
self.logger.info('⏹️ Agent execution stopped by control flag')
|
|
28
|
+
raise InterruptedError("Agent stopped")
|
|
29
|
+
# Legacy: treat paused as stopped for backwards compatibility
|
|
30
|
+
if self.state.paused:
|
|
31
|
+
self.logger.info('⏹️ Agent paused flag set - treating as stop (pause deprecated)')
|
|
32
|
+
self.state.stopped = True # Normalize to stopped
|
|
33
|
+
raise InterruptedError("Agent stopped (paused flag deprecated)")
|
|
34
|
+
return False
|
|
35
|
+
|
|
36
|
+
def _trigger_loop_intervention(self, reason: str, clear_history: bool = False) -> None:
|
|
37
|
+
"""Trigger loop intervention guidance without clearing history.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
reason: Human-readable reason for intervention
|
|
41
|
+
clear_history: Deprecated, always False. History is preserved.
|
|
42
|
+
"""
|
|
43
|
+
clear_history = False # History clearing disabled - causes amnesia loops
|
|
44
|
+
self.logger.warning(f"🔄 LOOP DETECTED: {reason}")
|
|
45
|
+
|
|
46
|
+
# Check if this looks like a validation/parameter error loop
|
|
47
|
+
is_validation_error = any(x in reason.lower() for x in [
|
|
48
|
+
'validation', 'parameter', 'missing required', 'invalid argument',
|
|
49
|
+
'schema', 'type error', 'arguments'
|
|
50
|
+
])
|
|
51
|
+
|
|
52
|
+
# Also check if recent results have validation errors
|
|
53
|
+
if hasattr(self, '_last_result') and self._last_result:
|
|
54
|
+
for result in self._last_result[-3:]:
|
|
55
|
+
if hasattr(result, 'error') and result.error:
|
|
56
|
+
error_lower = str(result.error).lower()
|
|
57
|
+
if any(x in error_lower for x in ['missing required parameter', 'validation failed', 'invalid']):
|
|
58
|
+
is_validation_error = True
|
|
59
|
+
break
|
|
60
|
+
|
|
61
|
+
# History clearing disabled - preserves context
|
|
62
|
+
# if clear_history and hasattr(self, 'message_manager'):
|
|
63
|
+
# self.message_manager.clear_history_keep_system(keep_last_n=2)
|
|
64
|
+
|
|
65
|
+
# Build intervention message based on error type.
|
|
66
|
+
# Chat-schema: tag as INTERVENTION control content (origin + <system-directive>
|
|
67
|
+
# envelope) so it is not indistinguishable from a genuine user turn.
|
|
68
|
+
if is_validation_error:
|
|
69
|
+
intervention_msg = make_control_message(f"""🚨 TOOL CALL ERROR LOOP DETECTED: {reason}
|
|
70
|
+
|
|
71
|
+
⚠️ Your tool calls are failing repeatedly with the SAME validation error.
|
|
72
|
+
The error message above tells you exactly what's wrong - READ IT CAREFULLY.
|
|
73
|
+
|
|
74
|
+
🔍 DIAGNOSIS FOR MCP TOOLS:
|
|
75
|
+
If calling mcp_execute_tool, the 'arguments' field must be a proper JSON object with the required parameters.
|
|
76
|
+
For example, a search tool requires: {{"count": 100, "query": "your search query"}}
|
|
77
|
+
|
|
78
|
+
🛑 TO FIX THIS:
|
|
79
|
+
1. READ the error message - it tells you which parameter is missing
|
|
80
|
+
2. INCLUDE all required parameters in your tool call
|
|
81
|
+
3. For mcp_execute_tool, ensure 'arguments' contains the nested tool's parameters as a JSON object
|
|
82
|
+
|
|
83
|
+
Example correct mcp_execute_tool call:
|
|
84
|
+
{{
|
|
85
|
+
"server_name": "example_server",
|
|
86
|
+
"tool_name": "search",
|
|
87
|
+
"arguments": {{"count": 100, "query": "crypto tweets"}}
|
|
88
|
+
}}
|
|
89
|
+
|
|
90
|
+
DO NOT just repeat the same broken call. FIX the parameters first.""", MessageOrigin.INTERVENTION)
|
|
91
|
+
else:
|
|
92
|
+
intervention_msg = make_control_message(f"""🚨 CRITICAL LOOP DETECTED: {reason}
|
|
93
|
+
|
|
94
|
+
⚠️ You have been repeating the same actions without making progress. This is wasting resources.
|
|
95
|
+
|
|
96
|
+
🛑 YOU MUST DO SOMETHING DIFFERENT NOW:
|
|
97
|
+
1. DO NOT read the same files again - you already have their content
|
|
98
|
+
2. DO NOT repeat the same action sequence
|
|
99
|
+
3. If you need to CREATE something, use filesystem_write_file NOW
|
|
100
|
+
4. If you're truly stuck, call the 'done' action with your current progress
|
|
101
|
+
|
|
102
|
+
📋 Your available actions include:
|
|
103
|
+
- filesystem_write_file: CREATE new files (HTML, text, etc.)
|
|
104
|
+
- done: Mark task complete with summary
|
|
105
|
+
|
|
106
|
+
TAKE A DIFFERENT ACTION IMMEDIATELY. Reading the same files again will result in task termination.""", MessageOrigin.INTERVENTION)
|
|
107
|
+
|
|
108
|
+
self.message_manager.add_message(intervention_msg)
|
|
109
|
+
|
|
110
|
+
# Add error to force reconsideration
|
|
111
|
+
if not hasattr(self, '_last_result') or not self._last_result:
|
|
112
|
+
self._last_result = []
|
|
113
|
+
|
|
114
|
+
error_msg = f"LOOP DETECTED: {reason}."
|
|
115
|
+
if clear_history:
|
|
116
|
+
error_msg += " Context cleared."
|
|
117
|
+
error_msg += " You MUST fix the issue - DO NOT repeat the same broken call."
|
|
118
|
+
|
|
119
|
+
self._last_result.append(ActionResult(
|
|
120
|
+
error=error_msg,
|
|
121
|
+
include_in_memory=True
|
|
122
|
+
))
|
|
123
|
+
|
|
124
|
+
# Reset counter and clear action history after intervention
|
|
125
|
+
self._action_repetition_counter = 0
|
|
126
|
+
self._previous_actions.clear()
|
|
127
|
+
|
|
128
|
+
# FIX #4: Signal to AgentState for tracking
|
|
129
|
+
if hasattr(self, 'state'):
|
|
130
|
+
self.state.loop_warning_count += 1
|
|
131
|
+
self.state.reset_loop_detection()
|
|
132
|
+
|
|
133
|
+
# CRITICAL: History is NEVER cleared - agent retains full context
|
|
134
|
+
self.logger.info(f"✅ Loop intervention complete. Full context preserved, guidance injected.")
|
|
135
|
+
|
|
136
|
+
def detect_action_loop(
|
|
137
|
+
self,
|
|
138
|
+
current_brain: Any,
|
|
139
|
+
current_actions: List[Dict],
|
|
140
|
+
lookback_steps: int = 5
|
|
141
|
+
) -> tuple[bool, Optional[str]]:
|
|
142
|
+
"""Detect if agent is repeating the same action pattern.
|
|
143
|
+
|
|
144
|
+
PHASE 2 FIX (Nov 4, 2025): Multi-signal loop detection prevents infinite loops
|
|
145
|
+
where agent repeats same actions without progress.
|
|
146
|
+
|
|
147
|
+
Detection Signals:
|
|
148
|
+
- Action repetition (same tool called N+ times in recent steps)
|
|
149
|
+
- Memory similarity (semantic overlap >70% in brain state)
|
|
150
|
+
- Goal stagnation (same next_goal for N+ consecutive steps)
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
current_brain: Current step's brain state
|
|
154
|
+
current_actions: Actions agent wants to take
|
|
155
|
+
lookback_steps: How many recent steps to check (default: 5)
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
(is_loop, warning_message) tuple
|
|
159
|
+
"""
|
|
160
|
+
if not self.task_context_manager or not self.session_id:
|
|
161
|
+
return False, None
|
|
162
|
+
|
|
163
|
+
# Get session data using correct API
|
|
164
|
+
session_data = self.task_context_manager.get_session(self.session_id)
|
|
165
|
+
if not session_data or not session_data.memory:
|
|
166
|
+
return False, None
|
|
167
|
+
|
|
168
|
+
memory = session_data.memory
|
|
169
|
+
recent_steps = memory.recent_steps[-lookback_steps:] if hasattr(memory, 'recent_steps') else []
|
|
170
|
+
|
|
171
|
+
if len(recent_steps) < 3:
|
|
172
|
+
return False, None # Need at least 3 steps to detect pattern
|
|
173
|
+
|
|
174
|
+
# SIGNAL 1: Action Repetition
|
|
175
|
+
current_action_names = set()
|
|
176
|
+
for action_dict in current_actions:
|
|
177
|
+
if isinstance(action_dict, dict):
|
|
178
|
+
current_action_names.update(action_dict.keys())
|
|
179
|
+
|
|
180
|
+
if not current_action_names:
|
|
181
|
+
return False, None
|
|
182
|
+
|
|
183
|
+
repeated_action_count = 0
|
|
184
|
+
repeated_step_nums = []
|
|
185
|
+
for step in recent_steps:
|
|
186
|
+
step_summary_lower = step.action_summary.lower()
|
|
187
|
+
if any(action.lower() in step_summary_lower for action in current_action_names):
|
|
188
|
+
repeated_action_count += 1
|
|
189
|
+
repeated_step_nums.append(step.step_number)
|
|
190
|
+
|
|
191
|
+
action_repeat_ratio = repeated_action_count / len(recent_steps)
|
|
192
|
+
|
|
193
|
+
# SIGNAL 2: Memory Similarity
|
|
194
|
+
similar_memory_count = 0
|
|
195
|
+
similar_memories = []
|
|
196
|
+
|
|
197
|
+
if hasattr(current_brain, 'memory') and current_brain.memory:
|
|
198
|
+
import re
|
|
199
|
+
current_keywords = set(re.findall(r'\b\w{4,}\b', current_brain.memory.lower()))
|
|
200
|
+
|
|
201
|
+
for step in recent_steps:
|
|
202
|
+
if step.finding:
|
|
203
|
+
step_keywords = set(re.findall(r'\b\w{4,}\b', step.finding.lower()))
|
|
204
|
+
if current_keywords and step_keywords:
|
|
205
|
+
overlap = len(current_keywords & step_keywords)
|
|
206
|
+
total = len(current_keywords | step_keywords)
|
|
207
|
+
if total > 0:
|
|
208
|
+
similarity = overlap / total
|
|
209
|
+
if similarity > 0.70: # 70% keyword overlap
|
|
210
|
+
similar_memory_count += 1
|
|
211
|
+
similar_memories.append((step.step_number, similarity, step.finding[:60]))
|
|
212
|
+
|
|
213
|
+
memory_similarity_ratio = similar_memory_count / len(recent_steps) if recent_steps else 0
|
|
214
|
+
|
|
215
|
+
# SIGNAL 3: Goal Stagnation (if available)
|
|
216
|
+
goal_stagnation = False
|
|
217
|
+
if hasattr(current_brain, 'next_goal') and current_brain.next_goal:
|
|
218
|
+
stagnant_goals = 0
|
|
219
|
+
for step in recent_steps:
|
|
220
|
+
if hasattr(step, 'brain_state') and step.brain_state:
|
|
221
|
+
if hasattr(step.brain_state, 'next_goal'):
|
|
222
|
+
if step.brain_state.next_goal == current_brain.next_goal:
|
|
223
|
+
stagnant_goals += 1
|
|
224
|
+
if stagnant_goals >= len(recent_steps) * 0.6:
|
|
225
|
+
goal_stagnation = True
|
|
226
|
+
|
|
227
|
+
# DETECTION LOGIC: Multiple signals = higher confidence
|
|
228
|
+
is_loop = False
|
|
229
|
+
warning_message = None
|
|
230
|
+
|
|
231
|
+
if action_repeat_ratio >= 0.60: # Action repeated in 60%+ of steps
|
|
232
|
+
severity = "🔄 LOOP DETECTED"
|
|
233
|
+
if memory_similarity_ratio >= 0.60: # AND memory is similar
|
|
234
|
+
severity = "🚨 SEVERE LOOP"
|
|
235
|
+
is_loop = True
|
|
236
|
+
elif goal_stagnation: # AND goal hasn't changed
|
|
237
|
+
severity = "⚠️ POSSIBLE LOOP"
|
|
238
|
+
is_loop = True
|
|
239
|
+
else:
|
|
240
|
+
severity = "⚠️ ACTION REPETITION"
|
|
241
|
+
is_loop = True
|
|
242
|
+
|
|
243
|
+
warning_message = (
|
|
244
|
+
f"{severity} at step {self.state.n_steps}:\n\n"
|
|
245
|
+
f"📊 Detection Signals:\n"
|
|
246
|
+
f" • Action repetition: {action_repeat_ratio:.0%} ({repeated_action_count}/{len(recent_steps)} steps)\n"
|
|
247
|
+
f" • Memory similarity: {memory_similarity_ratio:.0%} ({similar_memory_count}/{len(recent_steps)} steps)\n"
|
|
248
|
+
f" • Goal stagnation: {'YES' if goal_stagnation else 'NO'}\n\n"
|
|
249
|
+
f"🔍 Pattern Analysis:\n"
|
|
250
|
+
f" • Repeated action: {', '.join(current_action_names)}\n"
|
|
251
|
+
f" • Steps with this action: {repeated_step_nums}\n"
|
|
252
|
+
f" • Current memory: {current_brain.memory[:100] if hasattr(current_brain, 'memory') else 'N/A'}...\n\n"
|
|
253
|
+
f"💡 Recommended Actions:\n"
|
|
254
|
+
f" 1. Review recent steps to avoid repeating work\n"
|
|
255
|
+
f" 2. Check if data/result already available from previous steps\n"
|
|
256
|
+
f" 3. Try a DIFFERENT approach or tool\n"
|
|
257
|
+
f" 4. Verify your goal hasn't already been achieved\n"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
self.logger.warning(warning_message)
|
|
261
|
+
|
|
262
|
+
return is_loop, warning_message
|