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,349 @@
|
|
|
1
|
+
"""Tests for the Reference-parity compaction upgrade (Tiers A/B/C).
|
|
2
|
+
|
|
3
|
+
See docs/REFERENCE_VS_ROB_CONTEXT_SYSTEM_2026-06.md §7 (test plan) and §9 (patches).
|
|
4
|
+
|
|
5
|
+
These tests are deliberately light: a small harness composes CompactorMixin against
|
|
6
|
+
a real MessageHistory and a stub LLM, so the compaction logic is exercised with no
|
|
7
|
+
network and no browser state.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
import pytest
|
|
15
|
+
|
|
16
|
+
from modules.llm.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
|
|
17
|
+
from agents.task.agent.messages.compactor import CompactorMixin
|
|
18
|
+
from agents.task.agent.message_manager.views import (
|
|
19
|
+
ManagedMessage,
|
|
20
|
+
MessageHistory,
|
|
21
|
+
MessageMetadata,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# --------------------------------------------------------------------------- #
|
|
26
|
+
# Test harness
|
|
27
|
+
# --------------------------------------------------------------------------- #
|
|
28
|
+
class _StubLLM:
|
|
29
|
+
"""Records calls; echoes a configurable reply (or raises)."""
|
|
30
|
+
|
|
31
|
+
def __init__(self, reply: str = "STUB SUMMARY", fail: bool = False):
|
|
32
|
+
self.reply = reply
|
|
33
|
+
self.fail = fail
|
|
34
|
+
self.calls: list = []
|
|
35
|
+
|
|
36
|
+
async def ainvoke(self, messages):
|
|
37
|
+
self.calls.append(messages)
|
|
38
|
+
if self.fail:
|
|
39
|
+
raise RuntimeError("aux llm down")
|
|
40
|
+
return AIMessage(content=self.reply)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class _Harness(CompactorMixin):
|
|
44
|
+
"""Minimal MessageManager stand-in providing exactly what the mixin needs."""
|
|
45
|
+
|
|
46
|
+
def __init__(self, llm=None, aux_llm=None, max_input_tokens: int = 1000):
|
|
47
|
+
self.logger = logging.getLogger("test_compaction_upgrade")
|
|
48
|
+
self.history = MessageHistory()
|
|
49
|
+
self.llm = llm or _StubLLM()
|
|
50
|
+
if aux_llm is not None:
|
|
51
|
+
self.aux_llm = aux_llm
|
|
52
|
+
self.max_input_tokens = max_input_tokens
|
|
53
|
+
self._usage = 90.0
|
|
54
|
+
|
|
55
|
+
def _add_message_with_tokens(self, message, _internal: bool = False):
|
|
56
|
+
tokens = max(1, len(str(message.content)) // 4)
|
|
57
|
+
self.history.messages.append(
|
|
58
|
+
ManagedMessage(message=message, metadata=MessageMetadata(input_tokens=tokens))
|
|
59
|
+
)
|
|
60
|
+
self.history.total_tokens += tokens
|
|
61
|
+
|
|
62
|
+
def get_context_usage_percent(self) -> float:
|
|
63
|
+
return self._usage
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _fill(h: _Harness, n: int, size: int = 4000):
|
|
67
|
+
"""Add n large messages so the token-budget tail (C3) floors at min_keep=10."""
|
|
68
|
+
for i in range(n):
|
|
69
|
+
h._add_message_with_tokens(HumanMessage(content=("x" * size) + f"-m{i}"))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# --------------------------------------------------------------------------- #
|
|
73
|
+
# Tier A — compaction payload fixes
|
|
74
|
+
# --------------------------------------------------------------------------- #
|
|
75
|
+
def test_a1_oldest_middle_messages_reach_the_summarizer():
|
|
76
|
+
"""Defect A: the oldest messages must not be silently dropped (was [-50:])."""
|
|
77
|
+
h = _Harness()
|
|
78
|
+
msgs = [HumanMessage(content=f"MARK-{i}-zzz") for i in range(200)]
|
|
79
|
+
prompt = h._build_compaction_prompt(msgs)
|
|
80
|
+
assert "MARK-0-zzz" in prompt, "oldest message was dropped before summarization"
|
|
81
|
+
assert "MARK-199-zzz" in prompt
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def test_a2_tool_results_are_serialized_not_a_literal():
|
|
85
|
+
"""Defect B (part 1): tool output content must reach the summary input."""
|
|
86
|
+
h = _Harness()
|
|
87
|
+
payload = "TOOLDATA_NEEDLE_" + ("z" * 200)
|
|
88
|
+
prompt = h._build_compaction_prompt([ToolMessage(content=payload, tool_call_id="c1")])
|
|
89
|
+
assert "TOOLDATA_NEEDLE_" in prompt, "tool result reduced to a bare marker"
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_a2_message_not_hard_truncated_at_500_chars():
|
|
93
|
+
"""Defect B (part 2): content past 500 chars must survive."""
|
|
94
|
+
h = _Harness()
|
|
95
|
+
content = ("a" * 600) + "NEEDLE_PAST_500"
|
|
96
|
+
prompt = h._build_compaction_prompt([HumanMessage(content=content)])
|
|
97
|
+
assert "NEEDLE_PAST_500" in prompt
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def test_a3_structured_template_has_sections():
|
|
101
|
+
h = _Harness()
|
|
102
|
+
prompt = h._build_compaction_prompt([HumanMessage(content="hi")] * 6)
|
|
103
|
+
for section in (
|
|
104
|
+
"## Active Task",
|
|
105
|
+
"## In Progress",
|
|
106
|
+
"## Resolved Questions",
|
|
107
|
+
"## Pending User Asks",
|
|
108
|
+
"## Remaining Work",
|
|
109
|
+
):
|
|
110
|
+
assert section in prompt, f"missing template section {section!r}"
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def test_a4_prior_summary_is_fed_back_for_update():
|
|
114
|
+
h = _Harness()
|
|
115
|
+
prompt = h._build_compaction_prompt(
|
|
116
|
+
[HumanMessage(content="hi")], prior_summary="PRIOR_SUMMARY_BODY_123"
|
|
117
|
+
)
|
|
118
|
+
assert "PRIOR_SUMMARY_BODY_123" in prompt
|
|
119
|
+
assert "PRIOR SUMMARY" in prompt
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_a4_existing_compacted_block_detected_and_not_re_summarized():
|
|
123
|
+
"""An existing [COMPACTED SESSION HISTORY] in the middle is fed back, not re-summarized."""
|
|
124
|
+
h = _Harness(llm=_StubLLM(reply="NEW SUMMARY"))
|
|
125
|
+
h._add_message_with_tokens(
|
|
126
|
+
HumanMessage(content="[COMPACTED SESSION HISTORY]\n\nOLD_SUMMARY_TEXT\n\n[END]")
|
|
127
|
+
)
|
|
128
|
+
_fill(h, 19)
|
|
129
|
+
asyncio.run(h.llm_compact_history())
|
|
130
|
+
sent_prompt = h.llm.calls[0][0].content
|
|
131
|
+
assert "OLD_SUMMARY_TEXT" in sent_prompt
|
|
132
|
+
assert "PRIOR SUMMARY" in sent_prompt
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_a5_routes_to_aux_llm_when_present():
|
|
136
|
+
aux = _StubLLM(reply="AUX")
|
|
137
|
+
main = _StubLLM(reply="MAIN")
|
|
138
|
+
h = _Harness(llm=main, aux_llm=aux)
|
|
139
|
+
_fill(h, 20)
|
|
140
|
+
assert asyncio.run(h.llm_compact_history()) is True
|
|
141
|
+
assert len(aux.calls) == 1
|
|
142
|
+
assert len(main.calls) == 0
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def test_a5_falls_back_to_main_llm_without_aux():
|
|
146
|
+
main = _StubLLM(reply="MAIN")
|
|
147
|
+
h = _Harness(llm=main)
|
|
148
|
+
_fill(h, 20)
|
|
149
|
+
assert asyncio.run(h.llm_compact_history()) is True
|
|
150
|
+
assert len(main.calls) == 1
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def test_a6_static_fallback_used_when_llm_fails():
|
|
154
|
+
h = _Harness(llm=_StubLLM(fail=True))
|
|
155
|
+
emergency_called = {"v": False}
|
|
156
|
+
h.emergency_context_prune = lambda: emergency_called.__setitem__("v", True)
|
|
157
|
+
for i in range(20):
|
|
158
|
+
# large enough that the C3 token-budget tail floors at min_keep (so the
|
|
159
|
+
# middle is non-empty and compaction actually runs). Paired AIMessage →
|
|
160
|
+
# ToolMessage so the CX-H2 orphan-guard doesn't extend the tail past
|
|
161
|
+
# the whole (otherwise all-orphan) history and skip compaction.
|
|
162
|
+
body = ("padding " * 500) + f"ran tool /path/file{i}.py -> error: boom {i}"
|
|
163
|
+
h._add_message_with_tokens(
|
|
164
|
+
AIMessage(content="", tool_calls=[{"id": f"c{i}", "name": "run", "args": {}}])
|
|
165
|
+
)
|
|
166
|
+
h._add_message_with_tokens(ToolMessage(content=body, tool_call_id=f"c{i}"))
|
|
167
|
+
assert asyncio.run(h.llm_compact_history()) is True
|
|
168
|
+
assert emergency_called["v"] is False, "fell through to emergency prune instead of static fallback"
|
|
169
|
+
joined = " ".join(str(m.message.content) for m in h.history.messages)
|
|
170
|
+
assert "COMPACTED SESSION HISTORY" in joined
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# --------------------------------------------------------------------------- #
|
|
174
|
+
# Tier B — cache breadth, dedup, media hygiene, anti-thrash
|
|
175
|
+
# --------------------------------------------------------------------------- #
|
|
176
|
+
def _has_cache_control(block_holder) -> bool:
|
|
177
|
+
content = block_holder.get("content")
|
|
178
|
+
if isinstance(content, list):
|
|
179
|
+
return any(isinstance(b, dict) and b.get("cache_control") for b in content)
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def test_b1_marks_system_plus_last_three_messages():
|
|
184
|
+
from modules.llm.anthropic_client import _apply_conversation_cache
|
|
185
|
+
|
|
186
|
+
msgs = [{"role": "user", "content": f"m{i}"} for i in range(6)]
|
|
187
|
+
out = _apply_conversation_cache(msgs, n=3)
|
|
188
|
+
marked = [m for m in out if _has_cache_control(m)]
|
|
189
|
+
assert len(marked) == 3
|
|
190
|
+
# the three marked must be the last three
|
|
191
|
+
assert all(_has_cache_control(m) for m in out[-3:])
|
|
192
|
+
assert not any(_has_cache_control(m) for m in out[:-3])
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def test_b1_disabled_by_env(monkeypatch):
|
|
196
|
+
from modules.llm.anthropic_client import _apply_conversation_cache
|
|
197
|
+
|
|
198
|
+
monkeypatch.setenv("ANTHROPIC_PROMPT_CACHE", "0")
|
|
199
|
+
msgs = [{"role": "user", "content": f"m{i}"} for i in range(6)]
|
|
200
|
+
out = _apply_conversation_cache(msgs, n=3)
|
|
201
|
+
assert not any(_has_cache_control(m) for m in out)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def test_b2_identical_tool_results_deduped():
|
|
205
|
+
from agents.task.agent.messages.filters import dedup_tool_results
|
|
206
|
+
|
|
207
|
+
msgs = [
|
|
208
|
+
AIMessage(content="", tool_calls=[{"id": "c1", "name": "x", "args": {}}]),
|
|
209
|
+
ToolMessage(content="IDENTICAL_OUTPUT", tool_call_id="c1"),
|
|
210
|
+
AIMessage(content="", tool_calls=[{"id": "c2", "name": "x", "args": {}}]),
|
|
211
|
+
ToolMessage(content="IDENTICAL_OUTPUT", tool_call_id="c2"),
|
|
212
|
+
]
|
|
213
|
+
out = dedup_tool_results(msgs)
|
|
214
|
+
tool_contents = [str(m.content) for m in out if isinstance(m, ToolMessage)]
|
|
215
|
+
assert tool_contents[0] == "IDENTICAL_OUTPUT"
|
|
216
|
+
assert tool_contents[1] != "IDENTICAL_OUTPUT"
|
|
217
|
+
assert "duplicate" in tool_contents[1].lower() or "identical to" in tool_contents[1].lower()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_b3_keeps_latest_image_strips_older():
|
|
221
|
+
from agents.task.agent.messages.filters import strip_historical_media
|
|
222
|
+
|
|
223
|
+
def img():
|
|
224
|
+
return {"type": "image_url", "image_url": {"url": "data:image/png;base64,QUJDRA=="}}
|
|
225
|
+
|
|
226
|
+
txt = {"type": "text", "text": "hi"}
|
|
227
|
+
msgs = [
|
|
228
|
+
HumanMessage(content=[txt, img()]), # older -> strip
|
|
229
|
+
HumanMessage(content="middle"),
|
|
230
|
+
HumanMessage(content=[txt, img()]), # latest -> keep
|
|
231
|
+
]
|
|
232
|
+
out = strip_historical_media(msgs)
|
|
233
|
+
|
|
234
|
+
def has_b64(m):
|
|
235
|
+
c = m.content
|
|
236
|
+
if isinstance(c, list):
|
|
237
|
+
return any(
|
|
238
|
+
isinstance(b, dict)
|
|
239
|
+
and b.get("type") == "image_url"
|
|
240
|
+
and "base64" in str(b.get("image_url", {}).get("url", ""))
|
|
241
|
+
for b in c
|
|
242
|
+
)
|
|
243
|
+
return False
|
|
244
|
+
|
|
245
|
+
assert not has_b64(out[0]), "older image base64 should be stripped"
|
|
246
|
+
assert has_b64(out[2]), "latest image base64 should be preserved"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def test_b4_thrash_detector_needs_two_low_savings():
|
|
250
|
+
h = _Harness()
|
|
251
|
+
h._compaction_savings = [0.05, 0.04]
|
|
252
|
+
assert h._compaction_is_thrashing() is True
|
|
253
|
+
h._compaction_savings = [0.05, 0.50]
|
|
254
|
+
assert h._compaction_is_thrashing() is False
|
|
255
|
+
h._compaction_savings = [0.04]
|
|
256
|
+
assert h._compaction_is_thrashing() is False
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def test_b4_skips_llm_call_when_thrashing():
|
|
260
|
+
main = _StubLLM(reply="s")
|
|
261
|
+
h = _Harness(llm=main)
|
|
262
|
+
h._compaction_savings = [0.05, 0.04]
|
|
263
|
+
_fill(h, 20)
|
|
264
|
+
assert asyncio.run(h.llm_compact_history()) is False
|
|
265
|
+
assert len(main.calls) == 0
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def test_b4_records_savings_after_compaction():
|
|
269
|
+
h = _Harness(llm=_StubLLM(reply="tiny"))
|
|
270
|
+
_fill(h, 20)
|
|
271
|
+
asyncio.run(h.llm_compact_history())
|
|
272
|
+
assert len(h._compaction_savings) == 1
|
|
273
|
+
assert 0.0 <= h._compaction_savings[0] <= 1.0
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def test_b4_emergency_prune_resets_thrash_signal():
|
|
277
|
+
"""Recovery: a hard prune changes history, so the anti-thrash signal must clear
|
|
278
|
+
(otherwise LLM compaction stays permanently disabled for the session)."""
|
|
279
|
+
h = _Harness()
|
|
280
|
+
h._compaction_savings = [0.05, 0.04]
|
|
281
|
+
for i in range(20):
|
|
282
|
+
h._add_message_with_tokens(HumanMessage(content=f"m{i}"))
|
|
283
|
+
h.emergency_context_prune()
|
|
284
|
+
assert h._compaction_savings == []
|
|
285
|
+
assert h._compaction_is_thrashing() is False
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def test_a1_summarizes_in_windows_when_middle_is_huge():
|
|
289
|
+
"""A small aux model must not be overflowed: a huge middle is summarized in
|
|
290
|
+
iterative windows, and the oldest content still reaches the summarizer."""
|
|
291
|
+
h = _Harness(llm=_StubLLM(reply="WIN"), max_input_tokens=1000)
|
|
292
|
+
for i in range(250):
|
|
293
|
+
h._add_message_with_tokens(HumanMessage(content=f"MARK{i}-" + ("q" * 3500)))
|
|
294
|
+
assert asyncio.run(h.llm_compact_history()) is True
|
|
295
|
+
assert len(h.llm.calls) > 1, "huge middle should be windowed into multiple calls"
|
|
296
|
+
all_sent = " ".join(c[0].content for c in h.llm.calls)
|
|
297
|
+
assert "MARK0-" in all_sent, "oldest message must still reach the summarizer (no drop)"
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
# --------------------------------------------------------------------------- #
|
|
301
|
+
# Tier C — references, checkpoint, token-budget tail
|
|
302
|
+
# --------------------------------------------------------------------------- #
|
|
303
|
+
def test_c1_expands_file_reference(tmp_path):
|
|
304
|
+
from agents.task.agent.messages.context_references import preprocess_context_references
|
|
305
|
+
|
|
306
|
+
f = tmp_path / "note.txt"
|
|
307
|
+
f.write_text("HELLO_FROM_FILE_C1")
|
|
308
|
+
out = preprocess_context_references(f"see @file:{f} please", context_length=100_000)
|
|
309
|
+
assert "HELLO_FROM_FILE_C1" in out
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
def test_c1_hard_cap_blocks_oversized_injection(tmp_path):
|
|
313
|
+
from agents.task.agent.messages.context_references import preprocess_context_references
|
|
314
|
+
|
|
315
|
+
f = tmp_path / "big.txt"
|
|
316
|
+
f.write_text("Z" * 10_000)
|
|
317
|
+
# context_length tiny -> 50% hard cap is ~10 chars -> must NOT inline the 10k file
|
|
318
|
+
out = preprocess_context_references(f"@file:{f}", context_length=20)
|
|
319
|
+
assert "Z" * 10_000 not in out
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
def test_c2_writes_pre_compaction_checkpoint(tmp_path):
|
|
323
|
+
h = _Harness(llm=_StubLLM(reply="s"))
|
|
324
|
+
h._compaction_checkpoint_dir = str(tmp_path)
|
|
325
|
+
_fill(h, 20)
|
|
326
|
+
asyncio.run(h.llm_compact_history())
|
|
327
|
+
files = list(tmp_path.glob("compaction_*.json"))
|
|
328
|
+
assert len(files) == 1
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def test_c3_keeps_more_than_floor_when_budget_generous():
|
|
332
|
+
h = _Harness(max_input_tokens=10_000)
|
|
333
|
+
msgs = [HumanMessage(content="x" * 40) for _ in range(60)] # ~10 tok each
|
|
334
|
+
assert h._compaction_keep_recent(msgs, min_keep=10) > 10
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def test_c3_floors_at_min_keep_when_budget_tight():
|
|
338
|
+
h = _Harness(max_input_tokens=100)
|
|
339
|
+
msgs = [HumanMessage(content="x" * 4000) for _ in range(20)] # ~1000 tok each
|
|
340
|
+
assert h._compaction_keep_recent(msgs, min_keep=10) == 10
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def test_real_message_manager_declares_compaction_slots():
|
|
344
|
+
"""The real MessageManager is __slots__-based; the new attrs must be slotted
|
|
345
|
+
or construction.py / the mixin writes would raise AttributeError at runtime."""
|
|
346
|
+
from agents.task.agent.message_manager.service import MessageManager
|
|
347
|
+
|
|
348
|
+
for slot in ("aux_llm", "_compaction_savings", "_compaction_count", "_compaction_checkpoint_dir"):
|
|
349
|
+
assert slot in MessageManager.__slots__
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Regression: load_from_disk must call create_file_path with subdir_name= (not subdir=)."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
from agents.task.agent.messages.persistence import PersistenceMixin
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class _Host(PersistenceMixin):
|
|
8
|
+
def __init__(self):
|
|
9
|
+
import logging
|
|
10
|
+
self.logger = logging.getLogger("test_persistence_subdir_fix")
|
|
11
|
+
self.session_id = "sess_1"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_load_from_disk_uses_subdir_name_kwarg(monkeypatch):
|
|
15
|
+
captured = {}
|
|
16
|
+
|
|
17
|
+
class _PM:
|
|
18
|
+
def create_file_path(self, **kwargs):
|
|
19
|
+
captured.update(kwargs)
|
|
20
|
+
return Path("/nonexistent/message_history.json") # .exists() -> False, early return
|
|
21
|
+
|
|
22
|
+
monkeypatch.setattr("agents.task.path.pm", lambda: _PM())
|
|
23
|
+
|
|
24
|
+
host = _Host()
|
|
25
|
+
# Must NOT raise TypeError (the bug); returns False because the path doesn't exist.
|
|
26
|
+
assert host.load_from_disk("sess_1") is False
|
|
27
|
+
assert "subdir_name" in captured
|
|
28
|
+
assert "subdir" not in captured
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""SqliteMessageStore: append + load round-trip, per-session, durable across instances."""
|
|
2
|
+
from agents.task.agent.messages.sqlite_persistence import SqliteMessageStore
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def test_append_and_load_roundtrip(tmp_path):
|
|
6
|
+
store = SqliteMessageStore(str(tmp_path / "msgs.db"))
|
|
7
|
+
store.append("s1", {"type": "HumanMessage", "content": "hello"})
|
|
8
|
+
store.append("s1", {"type": "AIMessage", "content": "hi", "tool_calls": []})
|
|
9
|
+
rows = store.load("s1")
|
|
10
|
+
assert [r["type"] for r in rows] == ["HumanMessage", "AIMessage"]
|
|
11
|
+
assert rows[0]["content"] == "hello"
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_isolation_by_session(tmp_path):
|
|
15
|
+
store = SqliteMessageStore(str(tmp_path / "msgs.db"))
|
|
16
|
+
store.append("s1", {"type": "HumanMessage", "content": "a"})
|
|
17
|
+
store.append("s2", {"type": "HumanMessage", "content": "b"})
|
|
18
|
+
assert len(store.load("s1")) == 1
|
|
19
|
+
assert store.load("s2")[0]["content"] == "b"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def test_durable_across_instances(tmp_path):
|
|
23
|
+
db = str(tmp_path / "msgs.db")
|
|
24
|
+
SqliteMessageStore(db).append("s1", {"type": "HumanMessage", "content": "persisted"})
|
|
25
|
+
rows = SqliteMessageStore(db).load("s1") # fresh instance, same file
|
|
26
|
+
assert rows[0]["content"] == "persisted"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def test_clear_session(tmp_path):
|
|
30
|
+
store = SqliteMessageStore(str(tmp_path / "msgs.db"))
|
|
31
|
+
store.append("s1", {"type": "HumanMessage", "content": "x"})
|
|
32
|
+
store.clear("s1")
|
|
33
|
+
assert store.load("s1") == []
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_replace_all_atomic_swap_and_order(tmp_path):
|
|
37
|
+
# MED-4: replace_all does DELETE + executemany in one txn; seq from enumerate.
|
|
38
|
+
store = SqliteMessageStore(str(tmp_path / "m.db"))
|
|
39
|
+
store.replace_all("s1", [{"i": 0}, {"i": 1}, {"i": 2}])
|
|
40
|
+
assert store.load("s1") == [{"i": 0}, {"i": 1}, {"i": 2}]
|
|
41
|
+
# A second replace_all fully supersedes the first (no PK collision, no leftovers).
|
|
42
|
+
store.replace_all("s1", [{"i": 9}])
|
|
43
|
+
assert store.load("s1") == [{"i": 9}]
|
|
44
|
+
# Per-session isolation preserved.
|
|
45
|
+
store.replace_all("s2", [{"x": 1}])
|
|
46
|
+
assert store.load("s1") == [{"i": 9}]
|
|
47
|
+
assert store.load("s2") == [{"x": 1}]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def test_replace_all_empty_clears(tmp_path):
|
|
51
|
+
store = SqliteMessageStore(str(tmp_path / "m.db"))
|
|
52
|
+
store.replace_all("s1", [{"a": 1}])
|
|
53
|
+
store.replace_all("s1", [])
|
|
54
|
+
assert store.load("s1") == []
|