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,506 @@
|
|
|
1
|
+
"""Message-retrieval / LLM-request-assembly mixin (roadmap P9; from message_manager/service.py).
|
|
2
|
+
|
|
3
|
+
get_messages / get_messages_for_llm (foundation + history assembly with the pinned
|
|
4
|
+
system/task/skill/H-MEM messages), ephemeral push, LLM timeout/param helpers, and
|
|
5
|
+
message-structure logging. Split out so MessageManager service.py drops under 700L.
|
|
6
|
+
MessageManager composes MessageRetrievalMixin; call sites (step.py, llm_runner.py,
|
|
7
|
+
logging_io, output_validation, tests) unchanged via MRO.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import logging
|
|
13
|
+
from collections import deque
|
|
14
|
+
from typing import Any, Dict, List, Optional
|
|
15
|
+
|
|
16
|
+
from modules.llm.messages import (
|
|
17
|
+
AIMessage, BaseMessage, HumanMessage, SystemMessage, ToolMessage,
|
|
18
|
+
MessageOrigin, make_control_message,
|
|
19
|
+
)
|
|
20
|
+
from agents.task.constants import hmem_tail_placement
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class MessageRetrievalMixin:
|
|
24
|
+
"""Message retrieval + LLM-request assembly for MessageManager."""
|
|
25
|
+
|
|
26
|
+
def get_messages(self) -> List[BaseMessage]:
|
|
27
|
+
"""Get current message list with foundation messages always prepended.
|
|
28
|
+
|
|
29
|
+
CRITICAL FIX: SystemMessage and InitialTask are stored separately to prevent deque eviction.
|
|
30
|
+
This method always returns [SystemMessage, InitialTask (if exists), ...conversation] to ensure
|
|
31
|
+
the LLM always receives brain state instructions and knows the original task.
|
|
32
|
+
|
|
33
|
+
FOUNDATION (never evicted):
|
|
34
|
+
[0] SystemMessage - Instructions, format, workflow
|
|
35
|
+
[1] Initial Task - Original user request (if new session)
|
|
36
|
+
|
|
37
|
+
CONVERSATION (deque, last 20 messages):
|
|
38
|
+
[2+] Recent messages (auto-evicts oldest)
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
# Build foundation messages (stored separately, never evicted)
|
|
42
|
+
foundation = [self._system_message]
|
|
43
|
+
|
|
44
|
+
# Runtime identity (model/provider actually running), pinned right after the
|
|
45
|
+
# system message so the agent answers "what model are you" from this line, not
|
|
46
|
+
# by reading env/config. Empty/unset => skipped. Matches get_messages_for_llm().
|
|
47
|
+
if getattr(self, '_runtime_identity_message', None) is not None:
|
|
48
|
+
foundation.append(self._runtime_identity_message)
|
|
49
|
+
|
|
50
|
+
# polyrob Phase C: SOUL/IDENTITY self-context, pinned right after the system
|
|
51
|
+
# message (identity precedes task), matching get_messages_for_llm().
|
|
52
|
+
if getattr(self, '_self_context_message', None) is not None:
|
|
53
|
+
foundation.append(self._self_context_message)
|
|
54
|
+
|
|
55
|
+
# C9: project context (CLAUDE.md/AGENTS.md/.cursorrules) pinned AFTER
|
|
56
|
+
# self-context and BEFORE the initial task, matching get_messages_for_llm().
|
|
57
|
+
if getattr(self, '_project_context_message', None) is not None:
|
|
58
|
+
foundation.append(self._project_context_message)
|
|
59
|
+
|
|
60
|
+
if self._initial_task_message is not None:
|
|
61
|
+
foundation.append(self._initial_task_message)
|
|
62
|
+
|
|
63
|
+
# Skills (PR13) - pinned in the foundation after the task, matching
|
|
64
|
+
# get_messages_for_llm() so logs and guidance-position math see the same layout.
|
|
65
|
+
if getattr(self, '_skill_message', None) is not None:
|
|
66
|
+
foundation.append(self._skill_message)
|
|
67
|
+
|
|
68
|
+
# Get conversation messages from history (recent messages in deque)
|
|
69
|
+
conversation_messages = [m.message for m in self.history.messages]
|
|
70
|
+
|
|
71
|
+
# Assemble: Foundation + Conversation
|
|
72
|
+
msg = foundation + conversation_messages
|
|
73
|
+
|
|
74
|
+
# Debug logging with correct indexing
|
|
75
|
+
total_input_tokens = self._system_message_tokens + self._initial_task_tokens
|
|
76
|
+
|
|
77
|
+
foundation_count = len(foundation)
|
|
78
|
+
self.logger.debug(f'Messages: {foundation_count} foundation (separate) + {len(self.history.messages)} conversation (deque)')
|
|
79
|
+
self.logger.debug(f'[0] SystemMessage - {self._system_message_tokens} tokens (separate)')
|
|
80
|
+
|
|
81
|
+
if self._initial_task_message:
|
|
82
|
+
self.logger.debug(f'[1] InitialTask - {self._initial_task_tokens} tokens (separate)')
|
|
83
|
+
|
|
84
|
+
for i, m in enumerate(self.history.messages):
|
|
85
|
+
total_input_tokens += m.metadata.input_tokens
|
|
86
|
+
msg_type = m.message.__class__.__name__
|
|
87
|
+
msg_index = foundation_count + i # Correct index accounting for foundation
|
|
88
|
+
|
|
89
|
+
# Add detailed logging for tool calls and responses
|
|
90
|
+
if isinstance(m.message, AIMessage) and hasattr(m.message, 'tool_calls') and m.message.tool_calls:
|
|
91
|
+
tool_call_ids = [tc.get('id') for tc in m.message.tool_calls if 'id' in tc]
|
|
92
|
+
self.logger.debug(f'[{msg_index}] {msg_type} - {m.metadata.input_tokens} tokens - tool_calls: {tool_call_ids}')
|
|
93
|
+
elif isinstance(m.message, ToolMessage) and hasattr(m.message, 'tool_call_id'):
|
|
94
|
+
self.logger.debug(f'[{msg_index}] {msg_type} - {m.metadata.input_tokens} tokens - tool_call_id: {m.message.tool_call_id}')
|
|
95
|
+
else:
|
|
96
|
+
self.logger.debug(f'[{msg_index}] {msg_type} - {m.metadata.input_tokens} tokens')
|
|
97
|
+
|
|
98
|
+
self.logger.debug(f'Total tokens: {total_input_tokens} (foundation: {self._system_message_tokens + self._initial_task_tokens}, conversation: {total_input_tokens - self._system_message_tokens - self._initial_task_tokens})')
|
|
99
|
+
|
|
100
|
+
return msg
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def push_ephemeral_message(self, message: BaseMessage) -> None:
|
|
104
|
+
"""Queue a one-shot message to be included on the next LLM call only.
|
|
105
|
+
|
|
106
|
+
This does not modify history or token counts and is cleared after use.
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
# Apply sensitive data filtering if configured, or the HISTORY_SECRET_SCRUB
|
|
110
|
+
# backstop (default ON) which scrubs unregistered secrets with no allowlist.
|
|
111
|
+
if self.sensitive_data or getattr(self, "_history_secret_scrub", True):
|
|
112
|
+
message = self._filter_sensitive_data(message)
|
|
113
|
+
self._ephemeral_messages.append(message)
|
|
114
|
+
self.logger.debug(f"Queued ephemeral message of type {type(message).__name__}")
|
|
115
|
+
except Exception as e:
|
|
116
|
+
self.logger.debug(f"Failed to queue ephemeral message: {e}")
|
|
117
|
+
|
|
118
|
+
def commit_ephemeral_consumption(self) -> None:
|
|
119
|
+
"""P2-14: drop the pending (delivered) ephemerals. Call once the LLM has
|
|
120
|
+
responded — even a parse-failed response means they WERE delivered."""
|
|
121
|
+
self._ephemeral_pending = []
|
|
122
|
+
|
|
123
|
+
def restore_ephemeral_on_failure(self) -> None:
|
|
124
|
+
"""P2-14: re-queue ephemerals consumed for an LLM call that ultimately failed
|
|
125
|
+
(transient invoke error), so the next step re-includes them instead of losing
|
|
126
|
+
a correspondent reply / RECALL forever."""
|
|
127
|
+
pending = getattr(self, '_ephemeral_pending', None)
|
|
128
|
+
if pending:
|
|
129
|
+
self._ephemeral_messages = pending + self._ephemeral_messages
|
|
130
|
+
self._ephemeral_pending = []
|
|
131
|
+
|
|
132
|
+
def get_messages_for_llm(self, consume_ephemeral: bool = True) -> List[BaseMessage]:
|
|
133
|
+
"""Get messages ready for LLM call with all processing applied.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
consume_ephemeral: when True (default, the real send-to-LLM path), any
|
|
137
|
+
queued one-shot ephemeral messages are cleared after being included
|
|
138
|
+
exactly once. Diagnostic/observational callers (e.g. context-usage
|
|
139
|
+
logging) must pass False so they peek without draining a one-shot
|
|
140
|
+
message (correspondent reply, deferral notice, memory-writer note)
|
|
141
|
+
before the real LLM call ever sees it (CX-H3).
|
|
142
|
+
|
|
143
|
+
✅ FIX (Nov 5, 2025): Refactored to protect H-MEM from repair
|
|
144
|
+
|
|
145
|
+
ARCHITECTURE:
|
|
146
|
+
1. Foundation (NEVER repaired): System message, Task message, H-MEM
|
|
147
|
+
2. Conversation (subject to repair): Recent messages, ephemeral messages
|
|
148
|
+
3. Repair ONLY conversation (foundation untouched)
|
|
149
|
+
4. Combine: foundation + repaired conversation
|
|
150
|
+
5. Model-specific handling (e.g., deepseek merging)
|
|
151
|
+
6. Return messages: Ready for LLM provider
|
|
152
|
+
|
|
153
|
+
This ensures H-MEM can never be lost because it's in the protected foundation layer.
|
|
154
|
+
"""
|
|
155
|
+
# Build foundation (never repaired): system, task, memory
|
|
156
|
+
foundation = []
|
|
157
|
+
|
|
158
|
+
# 1.1: System message (always first - instructions)
|
|
159
|
+
foundation.append(self._system_message)
|
|
160
|
+
|
|
161
|
+
# 1.1a: Runtime identity (model/provider actually running), pinned right after
|
|
162
|
+
# the system prompt so the agent answers its model from THIS line, not by
|
|
163
|
+
# reading env/config. Kept out of the system prompt (cacheable). Unset => skip.
|
|
164
|
+
if getattr(self, '_runtime_identity_message', None) is not None:
|
|
165
|
+
foundation.append(self._runtime_identity_message)
|
|
166
|
+
|
|
167
|
+
# 1.1b: polyrob Phase C - SOUL/IDENTITY self-context, pinned in the foundation
|
|
168
|
+
# right after the system prompt (NOT embedded in it, so the system prompt stays
|
|
169
|
+
# stable/cacheable). Identity precedes the task. Empty/unset => skipped.
|
|
170
|
+
if getattr(self, '_self_context_message', None) is not None:
|
|
171
|
+
foundation.append(self._self_context_message)
|
|
172
|
+
|
|
173
|
+
# 1.1c: C9 - project context (CLAUDE.md/AGENTS.md/.cursorrules), pinned AFTER
|
|
174
|
+
# self-context and BEFORE the initial task. CLI-only; empty/unset => skipped.
|
|
175
|
+
if getattr(self, '_project_context_message', None) is not None:
|
|
176
|
+
foundation.append(self._project_context_message)
|
|
177
|
+
|
|
178
|
+
# 1.2: Initial task message (if exists - defines what user wants)
|
|
179
|
+
if self._initial_task_message is not None:
|
|
180
|
+
foundation.append(self._initial_task_message)
|
|
181
|
+
|
|
182
|
+
# 1.2b: Skills (PR13) - pinned in the foundation, NOT the system prompt, so the
|
|
183
|
+
# system prompt stays stable/cacheable and skills read as a distinct block.
|
|
184
|
+
if getattr(self, '_skill_message', None) is not None:
|
|
185
|
+
foundation.append(self._skill_message)
|
|
186
|
+
|
|
187
|
+
# 1.3: Hierarchical memory.
|
|
188
|
+
# Placement (Phase 0.1): legacy = pinned in the foundation ahead of the
|
|
189
|
+
# conversation; tail = held here and appended as a dynamic SUFFIX after the
|
|
190
|
+
# conversation (see combine step below). The H-MEM block changes every step,
|
|
191
|
+
# so in the foundation prefix it broke the prompt cache for everything after
|
|
192
|
+
# it; as a tail suffix the stable foundation + growing conversation stay a
|
|
193
|
+
# cacheable prefix. Either way it is NEVER subject to tool-sequence repair.
|
|
194
|
+
memory_injected = False
|
|
195
|
+
hmem_msg = None # held for tail placement
|
|
196
|
+
tail_placement = hmem_tail_placement()
|
|
197
|
+
|
|
198
|
+
# FIX #1: Explicit warnings when H-MEM cannot be injected (was silently skipped)
|
|
199
|
+
if not self.task_context_manager:
|
|
200
|
+
self.logger.warning(
|
|
201
|
+
"⚠️ H-MEM DISABLED: TaskContextManager not available. "
|
|
202
|
+
"Agent will run without hierarchical memory context."
|
|
203
|
+
)
|
|
204
|
+
elif not self.session_id:
|
|
205
|
+
self.logger.warning(
|
|
206
|
+
"⚠️ H-MEM DISABLED: session_id not set. "
|
|
207
|
+
"Agent will run without hierarchical memory context."
|
|
208
|
+
)
|
|
209
|
+
else:
|
|
210
|
+
try:
|
|
211
|
+
context = self.task_context_manager.get_context_injection(self.session_id)
|
|
212
|
+
if context:
|
|
213
|
+
# Chat-schema: tag injected hierarchical memory as MEMORY control
|
|
214
|
+
# content (origin + <session-memory> envelope), not a user turn.
|
|
215
|
+
context_msg = make_control_message(context, MessageOrigin.MEMORY)
|
|
216
|
+
if tail_placement:
|
|
217
|
+
hmem_msg = context_msg # appended after conversation below
|
|
218
|
+
else:
|
|
219
|
+
foundation.append(context_msg)
|
|
220
|
+
memory_injected = True
|
|
221
|
+
context_tokens = len(context.split()) * 1.3 # Rough estimate
|
|
222
|
+
where = "tail suffix" if tail_placement else "foundation"
|
|
223
|
+
self.logger.debug(f"✅ H-MEM in {where} (protected from repair) - ~{context_tokens:.0f} tokens")
|
|
224
|
+
else:
|
|
225
|
+
self.logger.debug("H-MEM returned empty context (session may be new)")
|
|
226
|
+
except Exception as e:
|
|
227
|
+
self.logger.warning(f"Failed to inject hierarchical memory: {e}")
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
# Build conversation (may be repaired)
|
|
231
|
+
conversation = []
|
|
232
|
+
|
|
233
|
+
# 2.1: Recent conversation (from history deque)
|
|
234
|
+
# Use all available messages (up to MAX_RECENT_MESSAGES=10)
|
|
235
|
+
conversation_messages = [m.message for m in self.history.messages]
|
|
236
|
+
conversation.extend(conversation_messages)
|
|
237
|
+
|
|
238
|
+
# 2.2: Include any queued ephemeral messages (one-shot)
|
|
239
|
+
if hasattr(self, '_ephemeral_messages') and self._ephemeral_messages:
|
|
240
|
+
conversation.extend(self._ephemeral_messages)
|
|
241
|
+
self.logger.debug(f"Included {len(self._ephemeral_messages)} ephemeral messages")
|
|
242
|
+
if consume_ephemeral:
|
|
243
|
+
# P2-14: don't drop the one-shot ephemerals (correspondent replies,
|
|
244
|
+
# RECALL) at assembly time — the LLM call hasn't happened yet, so a
|
|
245
|
+
# transient invoke failure would lose them forever (correspondent data
|
|
246
|
+
# has no other delivery path). MOVE them to a pending buffer instead:
|
|
247
|
+
# commit_ephemeral_consumption() drops them once the LLM has responded,
|
|
248
|
+
# restore_ephemeral_on_failure() re-queues them if the call ultimately
|
|
249
|
+
# failed. Wired in the next_action wrapper (output_validation.py).
|
|
250
|
+
self._ephemeral_pending = list(self._ephemeral_messages)
|
|
251
|
+
self._ephemeral_messages.clear()
|
|
252
|
+
|
|
253
|
+
self.logger.debug(
|
|
254
|
+
f"Message composition: foundation={len(foundation)} (system=1, task={1 if self._initial_task_message else 0}, "
|
|
255
|
+
f"h_mem={1 if memory_injected else 0}), conversation={len(conversation)}"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
# Repair tool sequences in conversation
|
|
259
|
+
conversation = self._validate_and_repair_tool_sequences(conversation)
|
|
260
|
+
self.logger.debug(f"After repair: foundation={len(foundation)}, conversation={len(conversation)}")
|
|
261
|
+
|
|
262
|
+
# B2/B3 (Reference-parity media hygiene, see docs/REFERENCE_VS_ROB_CONTEXT_SYSTEM_2026-06.md §9):
|
|
263
|
+
# dedup byte-identical tool outputs, and strip base64 from every image-bearing turn
|
|
264
|
+
# EXCEPT the most recent. Conversation only — foundation (system/task/H-MEM/skills)
|
|
265
|
+
# is never touched. Both are idempotent and fail-soft no-ops when nothing matches.
|
|
266
|
+
try:
|
|
267
|
+
from agents.task.agent.messages.filters import dedup_tool_results, strip_historical_media
|
|
268
|
+
conversation = dedup_tool_results(conversation)
|
|
269
|
+
conversation = strip_historical_media(conversation)
|
|
270
|
+
except Exception as e:
|
|
271
|
+
self.logger.debug(f"media-hygiene pass skipped: {e}")
|
|
272
|
+
|
|
273
|
+
# Combine foundation + conversation (+ H-MEM tail suffix when relocated).
|
|
274
|
+
# The tail suffix goes AFTER the repaired conversation so it never participates
|
|
275
|
+
# in tool-sequence repair (same protection it had in the foundation), while
|
|
276
|
+
# keeping the foundation+conversation prefix cache-stable across steps.
|
|
277
|
+
messages = foundation + conversation
|
|
278
|
+
if hmem_msg is not None:
|
|
279
|
+
messages.append(hmem_msg)
|
|
280
|
+
|
|
281
|
+
# Model-specific handling
|
|
282
|
+
# Message types imported at module level from modules.llm.messages
|
|
283
|
+
if self.model_name == 'deepseek-reasoner':
|
|
284
|
+
messages = self.merge_successive_messages(messages, HumanMessage)
|
|
285
|
+
messages = self.merge_successive_messages(messages, AIMessage)
|
|
286
|
+
self.logger.debug("Applied deepseek-reasoner message merging")
|
|
287
|
+
|
|
288
|
+
# Step 5: Log the final message structure for debugging
|
|
289
|
+
if self.use_native_tools:
|
|
290
|
+
self._log_message_structure(messages)
|
|
291
|
+
|
|
292
|
+
# Step 6: Apply non-native conversion if not using native tools
|
|
293
|
+
if not self.use_native_tools:
|
|
294
|
+
messages = self.convert_messages_for_non_function_calling_models(messages)
|
|
295
|
+
|
|
296
|
+
# ✅ FIX #1.1: CRITICAL ASSERTION - Ensure system message is present
|
|
297
|
+
# This validates that the system message with brain state instructions reaches the LLM
|
|
298
|
+
# SystemMessage imported at module level from modules.llm.messages
|
|
299
|
+
if not messages or not isinstance(messages[0], SystemMessage):
|
|
300
|
+
error_msg = (
|
|
301
|
+
"CRITICAL ERROR: System message missing from message history! "
|
|
302
|
+
"This will cause the LLM to not return brain state JSON. "
|
|
303
|
+
f"Message count: {len(messages)}, "
|
|
304
|
+
f"First message type: {type(messages[0]).__name__ if messages else 'None'}"
|
|
305
|
+
)
|
|
306
|
+
self.logger.error(error_msg)
|
|
307
|
+
|
|
308
|
+
# Check if system message exists anywhere in history
|
|
309
|
+
system_messages = [m for m in messages if isinstance(m, SystemMessage)]
|
|
310
|
+
if system_messages:
|
|
311
|
+
self.logger.error(f"System message found at index {messages.index(system_messages[0])}, should be at index 0")
|
|
312
|
+
else:
|
|
313
|
+
self.logger.error("No system message found anywhere in message list")
|
|
314
|
+
|
|
315
|
+
# FAIL FAST - don't continue with broken state
|
|
316
|
+
from core.exceptions import AgentError
|
|
317
|
+
raise AgentError(error_msg)
|
|
318
|
+
|
|
319
|
+
# Verify system message contains brain state instructions
|
|
320
|
+
system_content = str(messages[0].content)
|
|
321
|
+
if "brain state" not in system_content.lower() and "text content" not in system_content.lower():
|
|
322
|
+
self.logger.warning(
|
|
323
|
+
"System message present but may not contain brain state instructions. "
|
|
324
|
+
f"Content preview: {system_content[:200]}"
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
self.logger.debug(f"Final messages for LLM: {len(messages)} messages")
|
|
328
|
+
self.logger.debug("✅ System message validation passed")
|
|
329
|
+
return messages
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def calculate_llm_timeout(self, tool_count: int = 0, use_vision: bool = False) -> float:
|
|
333
|
+
"""Calculate dynamic timeout based on CURRENT token count and complexity.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
tool_count: Number of tools available to the LLM
|
|
337
|
+
use_vision: Whether vision is enabled
|
|
338
|
+
|
|
339
|
+
Returns:
|
|
340
|
+
Timeout in seconds, scaled based on complexity
|
|
341
|
+
"""
|
|
342
|
+
import os
|
|
343
|
+
|
|
344
|
+
# Use current token count (no parameter needed!)
|
|
345
|
+
estimated_tokens = self.history.total_tokens + self._system_message_tokens + self._initial_task_tokens
|
|
346
|
+
|
|
347
|
+
# Base timeout on token count
|
|
348
|
+
# INCREASED: Complex code generation can take 120-180s even with small contexts
|
|
349
|
+
# The bottleneck is LLM generation time, not just token processing
|
|
350
|
+
if estimated_tokens < 5000:
|
|
351
|
+
timeout = 120.0 # 2 minutes for small contexts (code gen can be slow)
|
|
352
|
+
elif estimated_tokens < 10000:
|
|
353
|
+
timeout = 150.0 # 2.5 minutes for medium contexts
|
|
354
|
+
elif estimated_tokens < 30000:
|
|
355
|
+
timeout = 180.0 # 3 minutes for large contexts
|
|
356
|
+
elif estimated_tokens < 50000:
|
|
357
|
+
timeout = 240.0 # 4 minutes for very large contexts
|
|
358
|
+
elif estimated_tokens < 100000:
|
|
359
|
+
timeout = 360.0 # 6 minutes for huge contexts
|
|
360
|
+
elif estimated_tokens < 500000:
|
|
361
|
+
timeout = 480.0 # 8 minutes for massive contexts
|
|
362
|
+
else:
|
|
363
|
+
timeout = 600.0 # 10 minutes max for 500K+ contexts
|
|
364
|
+
|
|
365
|
+
# Adjust for tool count (large tool sets add processing time)
|
|
366
|
+
if tool_count > 20:
|
|
367
|
+
timeout += 30.0 # Extra 30s for 20+ tools
|
|
368
|
+
self.logger.debug(f"Added 30s timeout for {tool_count} tools")
|
|
369
|
+
elif tool_count > 10:
|
|
370
|
+
timeout += 15.0 # Extra 15s for 10-20 tools
|
|
371
|
+
self.logger.debug(f"Added 15s timeout for {tool_count} tools")
|
|
372
|
+
|
|
373
|
+
# Adjust for vision (image processing adds time)
|
|
374
|
+
if use_vision:
|
|
375
|
+
timeout += 20.0 # Extra 20s for vision processing
|
|
376
|
+
self.logger.debug(f"Added 20s timeout for vision")
|
|
377
|
+
|
|
378
|
+
# Minimum timeout for first call (model loading, code generation, etc.)
|
|
379
|
+
# Complex code generation tasks can take 120-180s even with small prompts
|
|
380
|
+
timeout = max(timeout, 120.0) # At least 2 minutes
|
|
381
|
+
|
|
382
|
+
# Add environment override for debugging
|
|
383
|
+
env_timeout = os.getenv('AUTOV2_LLM_TIMEOUT_OVERRIDE')
|
|
384
|
+
if env_timeout:
|
|
385
|
+
try:
|
|
386
|
+
timeout = float(env_timeout)
|
|
387
|
+
self.logger.info(f"Using environment timeout override: {timeout:.0f}s")
|
|
388
|
+
except ValueError:
|
|
389
|
+
self.logger.warning(f"Invalid timeout override value: {env_timeout}")
|
|
390
|
+
|
|
391
|
+
self.logger.info(f"Context: {estimated_tokens:,} tokens, {tool_count} tools, vision={use_vision} → {timeout:.0f}s timeout")
|
|
392
|
+
return timeout
|
|
393
|
+
|
|
394
|
+
def remove_last_state_message(self) -> None:
|
|
395
|
+
"""Remove the last state message from history to avoid bloat.
|
|
396
|
+
|
|
397
|
+
State messages are typically HumanMessages containing browser state or
|
|
398
|
+
other contextual information. This method removes them after processing
|
|
399
|
+
to prevent history bloat.
|
|
400
|
+
"""
|
|
401
|
+
# SECURITY FIX: Protect iteration and removal with lock
|
|
402
|
+
with self._history_lock:
|
|
403
|
+
if not self.history.messages:
|
|
404
|
+
return
|
|
405
|
+
|
|
406
|
+
# CX-H1: tag-first removal. A shape-based scan ("list content OR
|
|
407
|
+
# contains 'Current url:'") can't tell a minimal (non-browser)
|
|
408
|
+
# state message apart from a user's multimodal image turn — both
|
|
409
|
+
# can be the nearest match, and the wrong one gets deleted.
|
|
410
|
+
# add_state_message now stamps every state message it builds with
|
|
411
|
+
# metadata["state_message"]=True, so scan for the tag first across
|
|
412
|
+
# the WHOLE history. Only if no tagged message exists anywhere
|
|
413
|
+
# (e.g. history loaded from disk that predates the tag) fall back
|
|
414
|
+
# once to the old shape heuristic.
|
|
415
|
+
for i in range(len(self.history.messages) - 1, -1, -1):
|
|
416
|
+
msg = self.history.messages[i].message if hasattr(self.history.messages[i], 'message') else self.history.messages[i]
|
|
417
|
+
if isinstance(msg, HumanMessage) and (getattr(msg, "metadata", None) or {}).get("state_message"):
|
|
418
|
+
self.history.remove_message(i)
|
|
419
|
+
self.logger.debug(f"Removed tagged state message at index {i}")
|
|
420
|
+
return
|
|
421
|
+
|
|
422
|
+
# Legacy fallback: no tagged message found anywhere in history.
|
|
423
|
+
for i in range(len(self.history.messages) - 1, -1, -1):
|
|
424
|
+
msg = self.history.messages[i].message if hasattr(self.history.messages[i], 'message') else self.history.messages[i]
|
|
425
|
+
if isinstance(msg, HumanMessage):
|
|
426
|
+
# Check if it looks like a state message (pre-tag heuristic)
|
|
427
|
+
if isinstance(msg.content, list) or (isinstance(msg.content, str) and 'Current url:' in msg.content):
|
|
428
|
+
self.history.remove_message(i)
|
|
429
|
+
self.logger.debug(f"Removed state message (legacy shape fallback) at index {i}")
|
|
430
|
+
return
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def get_llm_parameters(self) -> dict:
|
|
434
|
+
"""Extract LLM parameters for logging and telemetry.
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
Dictionary of LLM parameters
|
|
438
|
+
"""
|
|
439
|
+
params = {}
|
|
440
|
+
|
|
441
|
+
# Get class name for type detection
|
|
442
|
+
class_name = self.llm.__class__.__name__
|
|
443
|
+
|
|
444
|
+
# Extract common parameters based on LLM type
|
|
445
|
+
if 'OpenAI' in class_name:
|
|
446
|
+
# OpenAI parameters
|
|
447
|
+
for attr in ['model_name', 'temperature', 'max_tokens', 'top_p',
|
|
448
|
+
'frequency_penalty', 'presence_penalty', 'timeout', 'seed']:
|
|
449
|
+
if hasattr(self.llm, attr):
|
|
450
|
+
params[attr if attr != 'model_name' else 'model'] = getattr(self.llm, attr)
|
|
451
|
+
|
|
452
|
+
elif 'Anthropic' in class_name:
|
|
453
|
+
# Anthropic parameters
|
|
454
|
+
for attr in ['model_name', 'temperature', 'max_tokens', 'top_k', 'top_p', 'timeout']:
|
|
455
|
+
if hasattr(self.llm, attr):
|
|
456
|
+
params[attr if attr != 'model_name' else 'model'] = getattr(self.llm, attr)
|
|
457
|
+
|
|
458
|
+
elif 'Google' in class_name or 'Gemini' in class_name:
|
|
459
|
+
# Google parameters
|
|
460
|
+
for attr in ['model_name', 'temperature', 'max_tokens', 'top_k', 'top_p']:
|
|
461
|
+
if hasattr(self.llm, attr):
|
|
462
|
+
params[attr if attr != 'model_name' else 'model'] = getattr(self.llm, attr)
|
|
463
|
+
|
|
464
|
+
else:
|
|
465
|
+
# Generic parameters
|
|
466
|
+
for attr in ['model_name', 'model', 'temperature', 'max_tokens']:
|
|
467
|
+
if hasattr(self.llm, attr):
|
|
468
|
+
value = getattr(self.llm, attr)
|
|
469
|
+
if value is not None:
|
|
470
|
+
params[attr] = value
|
|
471
|
+
|
|
472
|
+
# Add MessageManager-specific parameters
|
|
473
|
+
params['use_native_tools'] = self.use_native_tools
|
|
474
|
+
params['max_input_tokens'] = self.max_input_tokens
|
|
475
|
+
params['provider'] = self.provider_name
|
|
476
|
+
|
|
477
|
+
return params
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
def _log_message_structure(self, messages: List[BaseMessage]) -> None:
|
|
481
|
+
"""Log message structure for debugging native tools."""
|
|
482
|
+
if self.logger.level > logging.DEBUG:
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
self.logger.debug("[NATIVE_TOOLS] Final message structure:")
|
|
486
|
+
for i, msg in enumerate(messages[-10:]): # Last 10 messages
|
|
487
|
+
if isinstance(msg, SystemMessage):
|
|
488
|
+
self.logger.debug(f" [{i}] SystemMessage")
|
|
489
|
+
elif isinstance(msg, HumanMessage):
|
|
490
|
+
self.logger.debug(f" [{i}] HumanMessage: {msg.content[:50]}...")
|
|
491
|
+
elif isinstance(msg, AIMessage):
|
|
492
|
+
if hasattr(msg, 'tool_calls') and msg.tool_calls:
|
|
493
|
+
self.logger.debug(f" [{i}] AIMessage with {len(msg.tool_calls)} tool_calls")
|
|
494
|
+
else:
|
|
495
|
+
self.logger.debug(f" [{i}] AIMessage: {msg.content[:50]}...")
|
|
496
|
+
elif isinstance(msg, ToolMessage):
|
|
497
|
+
self.logger.debug(f" [{i}] ToolMessage for {msg.tool_call_id}: {msg.content[:50]}...")
|
|
498
|
+
|
|
499
|
+
|
|
500
|
+
# === Context overflow recovery ===
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""SqliteMessageStore — durable, queryable per-session message history.
|
|
2
|
+
|
|
3
|
+
Drop-in alternative to the JSON-blob persistence (PersistenceMixin.save_to_disk),
|
|
4
|
+
opt-in via MESSAGE_STORE_BACKEND=sqlite. WAL + jittered retry via core/sqlite_util,
|
|
5
|
+
matching the SESSION_REGISTRY_BACKEND=sqlite precedent. Stores each message as a JSON
|
|
6
|
+
row keyed by (session_id, seq) so ordering and per-session isolation are preserved.
|
|
7
|
+
"""
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import os
|
|
11
|
+
from typing import Any, Dict, List
|
|
12
|
+
|
|
13
|
+
from core.sqlite_util import execute_retry, wal_connect
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SqliteMessageStore:
|
|
19
|
+
def __init__(self, db_path: str):
|
|
20
|
+
parent = os.path.dirname(db_path)
|
|
21
|
+
if parent:
|
|
22
|
+
os.makedirs(parent, exist_ok=True)
|
|
23
|
+
self.db_path = db_path
|
|
24
|
+
self._init_schema()
|
|
25
|
+
|
|
26
|
+
def _init_schema(self) -> None:
|
|
27
|
+
conn = wal_connect(self.db_path)
|
|
28
|
+
try:
|
|
29
|
+
conn.execute(
|
|
30
|
+
"CREATE TABLE IF NOT EXISTS messages ("
|
|
31
|
+
" session_id TEXT NOT NULL,"
|
|
32
|
+
" seq INTEGER NOT NULL,"
|
|
33
|
+
" payload TEXT NOT NULL,"
|
|
34
|
+
" PRIMARY KEY (session_id, seq)"
|
|
35
|
+
")"
|
|
36
|
+
)
|
|
37
|
+
conn.commit()
|
|
38
|
+
finally:
|
|
39
|
+
conn.close()
|
|
40
|
+
|
|
41
|
+
def append(self, session_id: str, message: Dict[str, Any]) -> None:
|
|
42
|
+
row = execute_retry(
|
|
43
|
+
self.db_path,
|
|
44
|
+
"SELECT COALESCE(MAX(seq), -1) AS m FROM messages WHERE session_id = ?",
|
|
45
|
+
(session_id,),
|
|
46
|
+
fetch="one",
|
|
47
|
+
)
|
|
48
|
+
next_seq = (row["m"] if row else -1) + 1
|
|
49
|
+
execute_retry(
|
|
50
|
+
self.db_path,
|
|
51
|
+
"INSERT INTO messages (session_id, seq, payload) VALUES (?, ?, ?)",
|
|
52
|
+
(session_id, next_seq, json.dumps(message)),
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
def replace_all(self, session_id: str, messages: List[Dict[str, Any]]) -> None:
|
|
56
|
+
"""Atomically replace every row for a session in ONE connection/transaction.
|
|
57
|
+
|
|
58
|
+
Preferred over clear()+append()-loop: avoids the SELECT-MAX+INSERT PK race
|
|
59
|
+
(seq is assigned by enumerate inside the txn) and collapses ~2n+1 connection
|
|
60
|
+
open/close cycles per save down to 1 (MED-4).
|
|
61
|
+
"""
|
|
62
|
+
conn = wal_connect(self.db_path)
|
|
63
|
+
try:
|
|
64
|
+
conn.execute("DELETE FROM messages WHERE session_id = ?", (session_id,))
|
|
65
|
+
conn.executemany(
|
|
66
|
+
"INSERT INTO messages (session_id, seq, payload) VALUES (?, ?, ?)",
|
|
67
|
+
[(session_id, i, json.dumps(m)) for i, m in enumerate(messages)],
|
|
68
|
+
)
|
|
69
|
+
conn.commit()
|
|
70
|
+
finally:
|
|
71
|
+
conn.close()
|
|
72
|
+
|
|
73
|
+
def load(self, session_id: str) -> List[Dict[str, Any]]:
|
|
74
|
+
rows = execute_retry(
|
|
75
|
+
self.db_path,
|
|
76
|
+
"SELECT payload FROM messages WHERE session_id = ? ORDER BY seq ASC",
|
|
77
|
+
(session_id,),
|
|
78
|
+
fetch="all",
|
|
79
|
+
)
|
|
80
|
+
return [json.loads(r["payload"]) for r in (rows or [])]
|
|
81
|
+
|
|
82
|
+
def clear(self, session_id: str) -> None:
|
|
83
|
+
execute_retry(
|
|
84
|
+
self.db_path, "DELETE FROM messages WHERE session_id = ?", (session_id,)
|
|
85
|
+
)
|