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,328 @@
|
|
|
1
|
+
"""User-guidance injection mixin (roadmap P9; code-motion from message_manager/service.py).
|
|
2
|
+
|
|
3
|
+
inject_user_guidance is policy (how user/continuation guidance enters the message
|
|
4
|
+
context), not raw storage — §1.3(vi). Split into its own mixin so MessageManager's
|
|
5
|
+
service.py shrinks toward storage/retrieval. MessageManager composes GuidanceMixin;
|
|
6
|
+
callers (agent service.py, step.py) use message_manager.inject_user_guidance unchanged.
|
|
7
|
+
"""
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
from modules.llm.messages import HumanMessage, MessageOrigin, SystemMessage
|
|
12
|
+
|
|
13
|
+
# Forged (non-human) intake kinds → their true origin. Anything else — comment,
|
|
14
|
+
# continuation, correction, approval — is a genuine human turn (origin USER).
|
|
15
|
+
# A batch is only marked forged when EVERY drained message is forged: a real
|
|
16
|
+
# user message must never be demoted by a co-drained wake turn.
|
|
17
|
+
_FORGED_KIND_ORIGINS = {
|
|
18
|
+
"self_wake": MessageOrigin.SELF_WAKE,
|
|
19
|
+
"delegation_result": MessageOrigin.SYSTEM_NOTE,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _elide_middle(text: str, keep_head: int, keep_tail: int) -> str:
|
|
24
|
+
"""Head+tail middle-elision (P0-1).
|
|
25
|
+
|
|
26
|
+
Keeps the first ``keep_head`` and last ``keep_tail`` characters with an explicit
|
|
27
|
+
marker between them. Unlike a plain ``text[:n]`` cut this preserves BOTH the
|
|
28
|
+
opening context and the trailing content — critical for pre-wrapped forged bodies
|
|
29
|
+
whose closing ``</untrusted_tool_result>`` / ``</delegation-result>`` delimiter
|
|
30
|
+
lives at the end (a head-only cut would leave the tag open).
|
|
31
|
+
"""
|
|
32
|
+
limit = keep_head + keep_tail
|
|
33
|
+
if not isinstance(text, str) or len(text) <= limit:
|
|
34
|
+
return text
|
|
35
|
+
elided = len(text) - limit
|
|
36
|
+
return f"{text[:keep_head]}\n[... {elided} chars elided ...]\n{text[-keep_tail:]}"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class GuidanceMixin:
|
|
40
|
+
"""User/continuation guidance injection for MessageManager."""
|
|
41
|
+
|
|
42
|
+
def inject_user_guidance(self, messages: List[Dict[str, Any]], session_context: Optional[Dict] = None) -> None:
|
|
43
|
+
"""Inject user guidance into message context for continuous conversation.
|
|
44
|
+
|
|
45
|
+
Implements Anthropic's "minimal, high-signal tokens" principle for continuous chat.
|
|
46
|
+
Reference: https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
messages: List of user message dictionaries with text, kind, metadata
|
|
50
|
+
session_context: Optional session context with fields:
|
|
51
|
+
- continuation: bool (True if user sent message to ongoing session)
|
|
52
|
+
"""
|
|
53
|
+
if not messages:
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
# Use RobustParseConfig for safety
|
|
57
|
+
from agents.task.robust_parse_config import RobustParseConfig
|
|
58
|
+
config = RobustParseConfig()
|
|
59
|
+
|
|
60
|
+
# Extract message texts. P0-1: forged (self-wake / delegation-result) bodies are
|
|
61
|
+
# pre-wrapped and bounded at their source — truncating them here would strip the
|
|
62
|
+
# closing delimiter and leave an open <untrusted_tool_result> tag, and the old
|
|
63
|
+
# 500-char cut delivered ZERO payload (preamble+boilerplate alone exceed 500).
|
|
64
|
+
# Genuine messages get head+tail elision so a long paste keeps both its ask and
|
|
65
|
+
# its trailing detail instead of a hard [:500] cut.
|
|
66
|
+
drained = messages[:config.MAX_USER_MESSAGES_PER_STEP]
|
|
67
|
+
message_texts = []
|
|
68
|
+
for msg in drained:
|
|
69
|
+
text = msg.get('text', '')
|
|
70
|
+
kind = msg.get('kind', 'comment')
|
|
71
|
+
if kind in _FORGED_KIND_ORIGINS:
|
|
72
|
+
# Bounded, delimiter-preserving ceiling only (no per-message cut).
|
|
73
|
+
text = _elide_middle(
|
|
74
|
+
text,
|
|
75
|
+
keep_head=config.FORGED_MESSAGE_MAX_CHARS - config.FORGED_MESSAGE_KEEP_TAIL,
|
|
76
|
+
keep_tail=config.FORGED_MESSAGE_KEEP_TAIL,
|
|
77
|
+
)
|
|
78
|
+
elif len(text) > config.USER_MESSAGE_TRUNCATE_LENGTH:
|
|
79
|
+
text = _elide_middle(
|
|
80
|
+
text,
|
|
81
|
+
keep_head=config.USER_MESSAGE_TRUNCATE_LENGTH - config.USER_MESSAGE_KEEP_TAIL,
|
|
82
|
+
keep_tail=config.USER_MESSAGE_KEEP_TAIL,
|
|
83
|
+
)
|
|
84
|
+
message_texts.append(text)
|
|
85
|
+
|
|
86
|
+
# P0-1: never silently drop queued messages beyond the per-step cap — the HITL
|
|
87
|
+
# drain already caps at MAX_USER_MESSAGES_PER_STEP and leaves the rest queued,
|
|
88
|
+
# but make it visible if a producer ever hands us more.
|
|
89
|
+
_overflow = len(messages) - len(drained)
|
|
90
|
+
if _overflow > 0:
|
|
91
|
+
message_texts.append(f"[... {_overflow} more message(s) queued for the next step ...]")
|
|
92
|
+
|
|
93
|
+
# Get session context (defaults if not provided)
|
|
94
|
+
ctx = session_context or {}
|
|
95
|
+
is_continuation = ctx.get('continuation', False)
|
|
96
|
+
workspace_changes = ctx.get('workspace_changes') # NEW
|
|
97
|
+
|
|
98
|
+
# Build workspace change summary (NEW)
|
|
99
|
+
change_summary = ""
|
|
100
|
+
if workspace_changes and workspace_changes.has_changes():
|
|
101
|
+
change_summary = workspace_changes.format_for_agent() + "\n\n"
|
|
102
|
+
|
|
103
|
+
# Wall-clock stamp: stale frames from earlier turns must not be able to
|
|
104
|
+
# masquerade as the current input (prod fa1212de: 9 undated PRIORITY
|
|
105
|
+
# frames accumulated, indistinguishable from each other).
|
|
106
|
+
received_at = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
|
|
107
|
+
|
|
108
|
+
# P4: record the true source. Only an all-forged batch (self-wake /
|
|
109
|
+
# delegation-result) is demoted; any genuine human kind keeps USER.
|
|
110
|
+
kinds = {
|
|
111
|
+
m.get('kind', 'comment')
|
|
112
|
+
for m in messages[:config.MAX_USER_MESSAGES_PER_STEP]
|
|
113
|
+
}
|
|
114
|
+
if kinds and all(k in _FORGED_KIND_ORIGINS for k in kinds):
|
|
115
|
+
guidance_origin = _FORGED_KIND_ORIGINS[next(iter(kinds))]
|
|
116
|
+
else:
|
|
117
|
+
guidance_origin = MessageOrigin.USER
|
|
118
|
+
|
|
119
|
+
# Build frame. P3: an all-forged batch must NOT masquerade as the user
|
|
120
|
+
# speaking — it gets an explicit autonomous re-entry frame instead of the
|
|
121
|
+
# "NEW USER MESSAGE" one, so the model never "answers the user" a turn the
|
|
122
|
+
# user never sent.
|
|
123
|
+
if guidance_origin != MessageOrigin.USER:
|
|
124
|
+
source_label = "self-wake" if guidance_origin == MessageOrigin.SELF_WAKE else "system"
|
|
125
|
+
frame = f"""{change_summary}🤖 AUTONOMOUS RE-ENTRY ({source_label}, {received_at})
|
|
126
|
+
|
|
127
|
+
{chr(10).join(message_texts)}
|
|
128
|
+
|
|
129
|
+
This is NOT a new user message — it is a system-scheduled continuation. If there
|
|
130
|
+
is pending productive work, do it. Otherwise call done() briefly. Do NOT re-answer
|
|
131
|
+
previous user questions and do NOT send the user redundant status messages.
|
|
132
|
+
""".strip()
|
|
133
|
+
elif is_continuation:
|
|
134
|
+
# High-signal marker: User sent new message during/after task work
|
|
135
|
+
user_request = "\n".join(message_texts)
|
|
136
|
+
|
|
137
|
+
# Get task phase if available
|
|
138
|
+
task_phase = ctx.get('task_phase', 1)
|
|
139
|
+
prev_phase = task_phase - 1 if task_phase > 1 else 0
|
|
140
|
+
|
|
141
|
+
# Build phase-aware continuation frame with memory override
|
|
142
|
+
if task_phase > 1:
|
|
143
|
+
# Multi-phase session - strong override required
|
|
144
|
+
frame = f"""{change_summary}🔄 NEW USER MESSAGE - PRIORITY INPUT (PHASE {task_phase}, received {received_at})
|
|
145
|
+
|
|
146
|
+
⚠️ CRITICAL: NEW TASK PHASE STARTING ⚠️
|
|
147
|
+
|
|
148
|
+
Your previous memory may show task completion. THAT WAS PHASE {prev_phase}.
|
|
149
|
+
This is PHASE {task_phase} - a BRAND NEW task building on previous work.
|
|
150
|
+
|
|
151
|
+
User's NEW request for Phase {task_phase}:
|
|
152
|
+
{user_request}
|
|
153
|
+
|
|
154
|
+
MANDATORY MEMORY UPDATE:
|
|
155
|
+
First line MUST be: "Phase {task_phase}: {message_texts[0][:50] if message_texts else ''}... (NEW TASK)"
|
|
156
|
+
Then reference: "Phase {prev_phase} complete: [brief summary]"
|
|
157
|
+
|
|
158
|
+
INSTRUCTIONS:
|
|
159
|
+
1. {'Check workspace changes above - NEW FILES UPLOADED' if change_summary else 'Read your memory field to see what you have already done'}
|
|
160
|
+
2. {'If new files are relevant, READ THEM FIRST using filesystem_read_file()' if change_summary else 'Update memory as shown above (Phase {task_phase} at start)'}
|
|
161
|
+
3. Set next_goal to: "Begin Phase {task_phase}: {message_texts[0][:40] if message_texts else ''}..."
|
|
162
|
+
4. CRITICAL: Provide BOTH brain state JSON AND tool calls for the NEW work
|
|
163
|
+
|
|
164
|
+
THIS IS NOT A STATUS CHECK - IT'S NEW WORK USING PREVIOUS PHASE AS INPUT.
|
|
165
|
+
""".strip()
|
|
166
|
+
else:
|
|
167
|
+
# First continuation or phase tracking not available
|
|
168
|
+
frame = f"""{change_summary}🔄 NEW USER MESSAGE - PRIORITY INPUT (received {received_at})
|
|
169
|
+
|
|
170
|
+
User sent you a new message:
|
|
171
|
+
{user_request}
|
|
172
|
+
|
|
173
|
+
INSTRUCTIONS:
|
|
174
|
+
1. {'⚠️ IMPORTANT: Check workspace changes above - user likely referring to NEW FILE' if change_summary else 'Read your memory field to see what you have already done'}
|
|
175
|
+
2. {'Read the new file(s) if relevant to the request' if change_summary else 'Incorporate this NEW USER MESSAGE into your next action immediately'}
|
|
176
|
+
3. Update your memory to reflect: "User requested: {message_texts[0][:40] if message_texts else ''}..."
|
|
177
|
+
4. CRITICAL: Provide BOTH brain state JSON (text content) AND tool calls
|
|
178
|
+
|
|
179
|
+
{'The user just uploaded new files - check them FIRST if they match the request.' if change_summary else 'This is a continuation of your session - your previous steps are in message history.'}
|
|
180
|
+
""".strip()
|
|
181
|
+
else:
|
|
182
|
+
# Mid-task guidance (less aggressive)
|
|
183
|
+
frame = f"""{change_summary}User guidance ({received_at}): {chr(10).join(message_texts)}
|
|
184
|
+
|
|
185
|
+
Incorporate this guidance while maintaining brain state format (JSON + tool calls).
|
|
186
|
+
""".strip()
|
|
187
|
+
|
|
188
|
+
# Apply overall token limit. P0-1: for a forged frame the closing
|
|
189
|
+
# </untrusted_tool_result> delimiter sits near the END (before the trusted
|
|
190
|
+
# footer), so a head-only `frame[:max_chars]` cut would strip it and leave the
|
|
191
|
+
# tag open. Use head+tail elision and, for forged frames, a budget large enough
|
|
192
|
+
# that a source-bounded wake payload passes intact.
|
|
193
|
+
if guidance_origin != MessageOrigin.USER:
|
|
194
|
+
max_chars = config.FORGED_MESSAGE_MAX_CHARS + 4000 # payload ceiling + frame overhead
|
|
195
|
+
keep_tail = config.FORGED_MESSAGE_KEEP_TAIL
|
|
196
|
+
else:
|
|
197
|
+
max_chars = config.MAX_USER_GUIDANCE_TOKENS * 3 # ~3 chars per token
|
|
198
|
+
keep_tail = config.USER_MESSAGE_KEEP_TAIL
|
|
199
|
+
if len(frame) > max_chars:
|
|
200
|
+
frame = _elide_middle(frame, keep_head=max_chars - keep_tail, keep_tail=keep_tail)
|
|
201
|
+
|
|
202
|
+
# NEW: Check for image attachments and create multimodal message if present
|
|
203
|
+
# FIXED: Collect images from ALL messages, not just first
|
|
204
|
+
image_attachments = []
|
|
205
|
+
image_sources = [] # Track which messages had images
|
|
206
|
+
|
|
207
|
+
for idx, msg in enumerate(messages):
|
|
208
|
+
msg_metadata = msg.get('metadata', {})
|
|
209
|
+
if msg_metadata and 'image_attachments' in msg_metadata:
|
|
210
|
+
imgs = msg_metadata['image_attachments']
|
|
211
|
+
if imgs: # Only process non-empty lists
|
|
212
|
+
image_attachments.extend(imgs)
|
|
213
|
+
image_sources.append(f"msg_{idx}")
|
|
214
|
+
self.logger.info(f"📷 Found {len(imgs)} image(s) in message {idx}: {msg.get('text', '')[:50]}...")
|
|
215
|
+
|
|
216
|
+
# Convert to None if empty (for consistency with existing code)
|
|
217
|
+
if not image_attachments:
|
|
218
|
+
image_attachments = None
|
|
219
|
+
else:
|
|
220
|
+
self.logger.info(
|
|
221
|
+
f"📷 Total images collected: {len(image_attachments)} from {len(image_sources)} message(s) "
|
|
222
|
+
f"(sources: {', '.join(image_sources)})"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
# Create message (multimodal if images present)
|
|
226
|
+
if image_attachments:
|
|
227
|
+
# CX-M3: the initial task message stays pinned (it lives outside the
|
|
228
|
+
# deque as a foundation message and is always prepended by
|
|
229
|
+
# get_messages/get_messages_for_llm — see MessageManager.__init__ and
|
|
230
|
+
# retrieval.py). Un-pinning it here (setting _initial_task_message to
|
|
231
|
+
# None and merging self.task into this turn's text) broke the
|
|
232
|
+
# "initial task is never evicted" invariant for the rest of the
|
|
233
|
+
# session: the merge was a leftover defensive fix from before the P1
|
|
234
|
+
# tail-append change below (this guidance message used to be inserted
|
|
235
|
+
# near deque position 1; now it is always appended at the tail), so
|
|
236
|
+
# there is no ordering hazard left to guard against — the foundation
|
|
237
|
+
# (system, initial task, ...) is always emitted ahead of the whole
|
|
238
|
+
# conversation deque regardless of where in the deque this message
|
|
239
|
+
# lands.
|
|
240
|
+
|
|
241
|
+
# Build multimodal content array: [text, image1, image2, ...]
|
|
242
|
+
content_parts = [
|
|
243
|
+
{'type': 'text', 'text': frame}
|
|
244
|
+
]
|
|
245
|
+
content_parts.extend(image_attachments)
|
|
246
|
+
guidance_msg = HumanMessage(content=content_parts, origin=guidance_origin)
|
|
247
|
+
|
|
248
|
+
# Log detailed structure (without full base64)
|
|
249
|
+
content_summary = []
|
|
250
|
+
for part in content_parts:
|
|
251
|
+
if part.get('type') == 'text':
|
|
252
|
+
text_preview = part['text'][:80] if len(part['text']) > 80 else part['text']
|
|
253
|
+
content_summary.append(f"text({len(part['text'])} chars: \"{text_preview}...\")")
|
|
254
|
+
elif part.get('type') == 'image_url':
|
|
255
|
+
url_length = len(part.get('image_url', {}).get('url', ''))
|
|
256
|
+
content_summary.append(f"image(data_url:{url_length} chars)")
|
|
257
|
+
else:
|
|
258
|
+
content_summary.append(f"unknown({part.get('type', 'no_type')})")
|
|
259
|
+
|
|
260
|
+
self.logger.info(
|
|
261
|
+
f"📷 Created multimodal user guidance:\n"
|
|
262
|
+
f" - Total parts: {len(content_parts)}\n"
|
|
263
|
+
f" - Images: {len(image_attachments)}\n"
|
|
264
|
+
f" - Structure: [{', '.join(content_summary)}]"
|
|
265
|
+
)
|
|
266
|
+
else:
|
|
267
|
+
# Text-only message (existing behavior)
|
|
268
|
+
guidance_msg = HumanMessage(content=frame, origin=guidance_origin)
|
|
269
|
+
self.logger.debug("Created text-only user guidance (no images)")
|
|
270
|
+
|
|
271
|
+
# P1 (2026-07-02): APPEND at the history tail. The frame historically went
|
|
272
|
+
# to position 1 of the deque — near the TOP of the conversation. On a
|
|
273
|
+
# resumed long-lived chat (prod fa1212de: 102 msgs) every real owner
|
|
274
|
+
# message piled up at the top while the tail filled with stale
|
|
275
|
+
# "Task Complete / no new input" turns, so the model kept dismissing
|
|
276
|
+
# genuine new input. The newest user turn must be the newest message.
|
|
277
|
+
self._add_message_with_tokens(guidance_msg)
|
|
278
|
+
|
|
279
|
+
def _tail_msg():
|
|
280
|
+
return self.history.messages[-1].message if self.history.messages else None
|
|
281
|
+
|
|
282
|
+
# VERIFY MESSAGE WAS ADDED CORRECTLY (tail of history)
|
|
283
|
+
added_msg = _tail_msg()
|
|
284
|
+
if isinstance(added_msg, HumanMessage):
|
|
285
|
+
if isinstance(added_msg.content, list):
|
|
286
|
+
image_count_in_msg = sum(1 for p in added_msg.content if isinstance(p, dict) and p.get('type') == 'image_url')
|
|
287
|
+
self.logger.info(f"✅ Multimodal message confirmed at history tail with {image_count_in_msg} image(s)")
|
|
288
|
+
elif image_attachments: # We expected images but got text
|
|
289
|
+
self.logger.error(
|
|
290
|
+
"🚨 BUG: Expected multimodal message but got text-only at history tail! "
|
|
291
|
+
"Images were dropped during message creation."
|
|
292
|
+
)
|
|
293
|
+
else:
|
|
294
|
+
self.logger.warning(f"⚠️ Unexpected message type at history tail: {type(added_msg).__name__}")
|
|
295
|
+
|
|
296
|
+
# Log injection details
|
|
297
|
+
self.logger.info(
|
|
298
|
+
f"Injected user guidance at history tail "
|
|
299
|
+
f"(continuation={is_continuation}, origin={guidance_origin}, "
|
|
300
|
+
f"workspace_changes={bool(change_summary)})"
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
# Force recalibration to ensure we don't exceed limits
|
|
304
|
+
# CRITICAL: This may modify messages - we verify afterward
|
|
305
|
+
self.recalibrate_token_counts()
|
|
306
|
+
|
|
307
|
+
# VERIFY MESSAGE SURVIVED RECALIBRATION
|
|
308
|
+
post_recal_msg = _tail_msg()
|
|
309
|
+
if isinstance(post_recal_msg, HumanMessage):
|
|
310
|
+
if isinstance(post_recal_msg.content, list):
|
|
311
|
+
image_count_after = sum(1 for p in post_recal_msg.content if isinstance(p, dict) and p.get('type') == 'image_url')
|
|
312
|
+
if image_attachments and image_count_after < len(image_attachments):
|
|
313
|
+
self.logger.error(
|
|
314
|
+
f"🚨 IMAGE LOSS: Started with {len(image_attachments)} images, "
|
|
315
|
+
f"only {image_count_after} remain after recalibration!"
|
|
316
|
+
)
|
|
317
|
+
elif image_attachments:
|
|
318
|
+
self.logger.error(
|
|
319
|
+
f"🚨 CRITICAL IMAGE LOSS: Multimodal guidance message was CONVERTED TO "
|
|
320
|
+
f"TEXT during recalibration! {len(image_attachments)} image(s) LOST."
|
|
321
|
+
)
|
|
322
|
+
elif image_attachments:
|
|
323
|
+
self.logger.error(
|
|
324
|
+
f"🚨 CRITICAL: Guidance message lost from history tail during recalibration! "
|
|
325
|
+
f"{len(image_attachments)} image(s) LOST."
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
|