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,448 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
import json
|
|
5
|
+
import logging
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any, Dict, List, Optional
|
|
9
|
+
|
|
10
|
+
from modules.llm.messages import (
|
|
11
|
+
AIMessage,
|
|
12
|
+
BaseMessage,
|
|
13
|
+
HumanMessage,
|
|
14
|
+
MessageOrigin,
|
|
15
|
+
SystemMessage,
|
|
16
|
+
ToolMessage,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from agents.task.agent.message_manager.views import MessageHistory, MessageMetadata, ManagedMessage
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
# UP-10 2.3: one-time guard so the write-only sqlite mirror warning fires once per
|
|
24
|
+
# process, not on every save_to_disk.
|
|
25
|
+
_warned_sqlite_mirror = False
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PersistenceMixin:
|
|
29
|
+
# Empty slots so the composed MessageManager keeps its own __slots__ (no __dict__).
|
|
30
|
+
__slots__ = ()
|
|
31
|
+
|
|
32
|
+
def checkpoint_history(self, filepath: Optional[Path] = None) -> None:
|
|
33
|
+
"""Create a checkpoint of current message history.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
filepath: Path to save checkpoint file
|
|
37
|
+
"""
|
|
38
|
+
try:
|
|
39
|
+
from datetime import datetime
|
|
40
|
+
# Message types imported at module level from modules.llm.messages
|
|
41
|
+
|
|
42
|
+
# Create checkpoint data
|
|
43
|
+
checkpoint_data = {
|
|
44
|
+
'version': '1.0',
|
|
45
|
+
'timestamp': datetime.now().isoformat(),
|
|
46
|
+
'session_id': self.session_id,
|
|
47
|
+
'total_tokens': self.history.total_tokens,
|
|
48
|
+
'max_messages': self.history.max_messages,
|
|
49
|
+
'messages': []
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
# Serialize messages
|
|
53
|
+
for managed_msg in self.history.messages:
|
|
54
|
+
msg = managed_msg.message
|
|
55
|
+
|
|
56
|
+
# Serialize based on message type
|
|
57
|
+
msg_data = {
|
|
58
|
+
'type': msg.__class__.__name__,
|
|
59
|
+
'metadata': managed_msg.metadata.model_dump(),
|
|
60
|
+
# CX-M1: mirror save_to_disk — persist the true source so a
|
|
61
|
+
# checkpoint restore doesn't silently demote every
|
|
62
|
+
# SELF_WAKE/CORRESPONDENT/COMPACTION_SUMMARY turn to USER.
|
|
63
|
+
'origin': getattr(msg, 'origin', MessageOrigin.USER),
|
|
64
|
+
# CX-M1: mirror save_to_disk — the state-message tag dict
|
|
65
|
+
# stamped by add_state_message (distinct from the typed
|
|
66
|
+
# ManagedMessage.metadata above).
|
|
67
|
+
'msg_metadata': getattr(msg, 'metadata', None) or {},
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
# Handle different message types
|
|
71
|
+
if isinstance(msg, SystemMessage):
|
|
72
|
+
msg_data['content'] = msg.content
|
|
73
|
+
elif isinstance(msg, HumanMessage):
|
|
74
|
+
msg_data['content'] = msg.content if isinstance(msg.content, str) else str(msg.content)
|
|
75
|
+
elif isinstance(msg, AIMessage):
|
|
76
|
+
msg_data['content'] = msg.content
|
|
77
|
+
if hasattr(msg, 'tool_calls') and msg.tool_calls:
|
|
78
|
+
msg_data['tool_calls'] = [
|
|
79
|
+
{
|
|
80
|
+
'id': tc.get('id') if isinstance(tc, dict) else getattr(tc, 'id', None),
|
|
81
|
+
'name': tc.get('name') if isinstance(tc, dict) else getattr(tc, 'name', None),
|
|
82
|
+
'args': tc.get('args', {}) if isinstance(tc, dict) else getattr(tc, 'args', {})
|
|
83
|
+
}
|
|
84
|
+
for tc in msg.tool_calls
|
|
85
|
+
]
|
|
86
|
+
elif isinstance(msg, ToolMessage):
|
|
87
|
+
msg_data['content'] = msg.content
|
|
88
|
+
msg_data['tool_call_id'] = getattr(msg, 'tool_call_id', None)
|
|
89
|
+
else:
|
|
90
|
+
msg_data['content'] = str(msg.content) if hasattr(msg, 'content') else str(msg)
|
|
91
|
+
|
|
92
|
+
checkpoint_data['messages'].append(msg_data)
|
|
93
|
+
|
|
94
|
+
# Store checkpoint in memory
|
|
95
|
+
self._history_checkpoint = [
|
|
96
|
+
ManagedMessage(
|
|
97
|
+
message=copy.deepcopy(m.message),
|
|
98
|
+
metadata=copy.deepcopy(m.metadata)
|
|
99
|
+
)
|
|
100
|
+
for m in self.history.messages
|
|
101
|
+
]
|
|
102
|
+
self._checkpoint_token_count = self.history.total_tokens
|
|
103
|
+
|
|
104
|
+
# Save to file if path provided
|
|
105
|
+
if filepath:
|
|
106
|
+
# Atomic write
|
|
107
|
+
import tempfile
|
|
108
|
+
import os
|
|
109
|
+
|
|
110
|
+
filepath = Path(filepath)
|
|
111
|
+
filepath.parent.mkdir(parents=True, exist_ok=True)
|
|
112
|
+
|
|
113
|
+
temp_fd, temp_path = tempfile.mkstemp(
|
|
114
|
+
dir=filepath.parent,
|
|
115
|
+
prefix='.message_checkpoint_',
|
|
116
|
+
suffix='.tmp'
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
try:
|
|
120
|
+
with os.fdopen(temp_fd, 'w') as f:
|
|
121
|
+
json.dump(checkpoint_data, f, indent=2)
|
|
122
|
+
f.flush()
|
|
123
|
+
os.fsync(f.fileno())
|
|
124
|
+
|
|
125
|
+
os.replace(temp_path, str(filepath))
|
|
126
|
+
self.logger.info(f"Saved message checkpoint to {filepath}")
|
|
127
|
+
|
|
128
|
+
except Exception:
|
|
129
|
+
if os.path.exists(temp_path):
|
|
130
|
+
os.unlink(temp_path)
|
|
131
|
+
raise
|
|
132
|
+
else:
|
|
133
|
+
self.logger.debug(f"Created history checkpoint with {len(self._history_checkpoint)} messages")
|
|
134
|
+
|
|
135
|
+
except Exception as e:
|
|
136
|
+
self.logger.warning(f"Failed to create checkpoint: {e}")
|
|
137
|
+
|
|
138
|
+
def restore_from_checkpoint(self) -> bool:
|
|
139
|
+
"""Restore message history from checkpoint if available."""
|
|
140
|
+
if hasattr(self, '_history_checkpoint') and self._history_checkpoint:
|
|
141
|
+
try:
|
|
142
|
+
# CX-M2: rewrap into a FRESH deque(maxlen=...) rather than aliasing
|
|
143
|
+
# the checkpoint list directly. Assigning the list itself let later
|
|
144
|
+
# appends mutate `_history_checkpoint` in place (no true rollback
|
|
145
|
+
# point) and dropped the deque's maxlen eviction behaviour.
|
|
146
|
+
from collections import deque
|
|
147
|
+
self.history.messages = deque(
|
|
148
|
+
list(self._history_checkpoint),
|
|
149
|
+
maxlen=self.history.max_messages,
|
|
150
|
+
)
|
|
151
|
+
self.history.total_tokens = self._checkpoint_token_count
|
|
152
|
+
self.logger.info(f"Restored history from checkpoint with {len(self.history.messages)} messages")
|
|
153
|
+
return True
|
|
154
|
+
except Exception as e:
|
|
155
|
+
self.logger.warning(f"Failed to restore from checkpoint: {e}")
|
|
156
|
+
return False
|
|
157
|
+
|
|
158
|
+
def restore_from_checkpoint_file(self, filepath: Path) -> bool:
|
|
159
|
+
"""Restore message history from checkpoint file.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
filepath: Path to checkpoint file
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
True if restoration successful
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
# Message types imported at module level from modules.llm.messages
|
|
169
|
+
|
|
170
|
+
if not filepath.exists():
|
|
171
|
+
self.logger.debug(f"Checkpoint file not found: {filepath}")
|
|
172
|
+
return False
|
|
173
|
+
|
|
174
|
+
with open(filepath, 'r') as f:
|
|
175
|
+
checkpoint_data = json.load(f)
|
|
176
|
+
|
|
177
|
+
# Validate version
|
|
178
|
+
if checkpoint_data.get('version') != '1.0':
|
|
179
|
+
self.logger.warning(f"Checkpoint version mismatch: {checkpoint_data.get('version')}")
|
|
180
|
+
return False
|
|
181
|
+
|
|
182
|
+
# Validate session_id
|
|
183
|
+
if self.session_id and checkpoint_data.get('session_id') != self.session_id:
|
|
184
|
+
self.logger.warning(f"Checkpoint session_id mismatch")
|
|
185
|
+
return False
|
|
186
|
+
|
|
187
|
+
# SECURITY FIX: Protect clear + restore operations with lock
|
|
188
|
+
with self._history_lock:
|
|
189
|
+
# Clear current history
|
|
190
|
+
self.history.messages.clear()
|
|
191
|
+
self.history.total_tokens = 0
|
|
192
|
+
|
|
193
|
+
# Restore messages
|
|
194
|
+
message_classes = {
|
|
195
|
+
'SystemMessage': SystemMessage,
|
|
196
|
+
'HumanMessage': HumanMessage,
|
|
197
|
+
'AIMessage': AIMessage,
|
|
198
|
+
'ToolMessage': ToolMessage
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
for msg_data in checkpoint_data.get('messages', []):
|
|
202
|
+
msg_type = msg_data.get('type')
|
|
203
|
+
msg_class = message_classes.get(msg_type)
|
|
204
|
+
|
|
205
|
+
if not msg_class:
|
|
206
|
+
self.logger.warning(f"Unknown message type: {msg_type}")
|
|
207
|
+
continue
|
|
208
|
+
|
|
209
|
+
# Reconstruct message
|
|
210
|
+
try:
|
|
211
|
+
if msg_type == 'AIMessage' and 'tool_calls' in msg_data:
|
|
212
|
+
msg = AIMessage(
|
|
213
|
+
content=msg_data['content'],
|
|
214
|
+
tool_calls=msg_data['tool_calls']
|
|
215
|
+
)
|
|
216
|
+
elif msg_type == 'ToolMessage':
|
|
217
|
+
msg = ToolMessage(
|
|
218
|
+
content=msg_data['content'],
|
|
219
|
+
tool_call_id=msg_data.get('tool_call_id')
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
msg = msg_class(content=msg_data['content'])
|
|
223
|
+
|
|
224
|
+
# CX-M1: mirror load_from_disk — restore the true source
|
|
225
|
+
# (pre-fix checkpoint files have no key -> USER, the
|
|
226
|
+
# historical/legacy behaviour).
|
|
227
|
+
msg.origin = msg_data.get('origin', MessageOrigin.USER)
|
|
228
|
+
# CX-M1: mirror load_from_disk — restore the state-message
|
|
229
|
+
# tag dict so a checkpoint-restored session can still
|
|
230
|
+
# target it by identity.
|
|
231
|
+
if msg_data.get('msg_metadata'):
|
|
232
|
+
msg.metadata = msg_data['msg_metadata']
|
|
233
|
+
|
|
234
|
+
# Restore metadata
|
|
235
|
+
metadata_dict = msg_data.get('metadata', {})
|
|
236
|
+
metadata = MessageMetadata(**metadata_dict)
|
|
237
|
+
|
|
238
|
+
# Add to history
|
|
239
|
+
self.history.messages.append(ManagedMessage(
|
|
240
|
+
message=msg,
|
|
241
|
+
metadata=metadata
|
|
242
|
+
))
|
|
243
|
+
self.history.total_tokens += metadata.input_tokens
|
|
244
|
+
|
|
245
|
+
except Exception as e:
|
|
246
|
+
self.logger.warning(f"Failed to restore message: {e}")
|
|
247
|
+
continue
|
|
248
|
+
|
|
249
|
+
# Restore totals
|
|
250
|
+
if 'total_tokens' in checkpoint_data:
|
|
251
|
+
# Verify token count
|
|
252
|
+
calculated = sum(m.metadata.input_tokens for m in self.history.messages)
|
|
253
|
+
saved = checkpoint_data['total_tokens']
|
|
254
|
+
if abs(calculated - saved) > 100: # Allow small variance
|
|
255
|
+
self.logger.warning(f"Token count mismatch: calculated={calculated}, saved={saved}")
|
|
256
|
+
self.history.total_tokens = saved
|
|
257
|
+
|
|
258
|
+
self.logger.info(f"Restored {len(self.history.messages)} messages from checkpoint")
|
|
259
|
+
return True
|
|
260
|
+
|
|
261
|
+
except Exception as e:
|
|
262
|
+
self.logger.error(f"Failed to restore from checkpoint file: {e}")
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
def save_to_disk(self, session_id: str, user_id: Optional[str] = None) -> None:
|
|
266
|
+
"""Save message history to disk for persistence across session eviction/restart.
|
|
267
|
+
|
|
268
|
+
Args:
|
|
269
|
+
session_id: Session identifier
|
|
270
|
+
user_id: Optional user identifier for path construction
|
|
271
|
+
"""
|
|
272
|
+
from agents.task.path import pm
|
|
273
|
+
|
|
274
|
+
try:
|
|
275
|
+
# Create path
|
|
276
|
+
save_path = pm().create_file_path(
|
|
277
|
+
session_id=session_id,
|
|
278
|
+
subdir_name="memory",
|
|
279
|
+
filename="message_history.json",
|
|
280
|
+
user_id=user_id
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
# Serialize messages
|
|
284
|
+
history_data = {
|
|
285
|
+
"session_id": session_id,
|
|
286
|
+
"messages": [],
|
|
287
|
+
"total_tokens": self.history.total_tokens,
|
|
288
|
+
"saved_at": datetime.now().isoformat()
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
# Serialize each message in history
|
|
292
|
+
for msg_wrapper in self.history.messages:
|
|
293
|
+
msg = msg_wrapper.message
|
|
294
|
+
# metadata.timestamp is an ISO string (MessageMetadata.timestamp: Optional[str]);
|
|
295
|
+
# tolerate a datetime for defensive back-compat.
|
|
296
|
+
ts = msg_wrapper.metadata.timestamp
|
|
297
|
+
if ts is not None and not isinstance(ts, str):
|
|
298
|
+
ts = ts.isoformat()
|
|
299
|
+
msg_data = {
|
|
300
|
+
"type": msg.__class__.__name__,
|
|
301
|
+
"content": msg.content,
|
|
302
|
+
# P4: persist the true source so a rehydrated session can tell a
|
|
303
|
+
# forged/self-wake/system turn from a genuine user message.
|
|
304
|
+
"origin": getattr(msg, "origin", MessageOrigin.USER),
|
|
305
|
+
# CX-H1: persist the state-message tag (metadata dict stamped by
|
|
306
|
+
# add_state_message) so remove_last_state_message can find it by
|
|
307
|
+
# identity after a session is reloaded from disk, not just via
|
|
308
|
+
# the legacy shape heuristic.
|
|
309
|
+
"msg_metadata": getattr(msg, "metadata", None) or {},
|
|
310
|
+
"metadata": {
|
|
311
|
+
"input_tokens": msg_wrapper.metadata.input_tokens,
|
|
312
|
+
"output_tokens": msg_wrapper.metadata.output_tokens,
|
|
313
|
+
"source": msg_wrapper.metadata.source,
|
|
314
|
+
"timestamp": ts,
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
# Include tool call data if present
|
|
319
|
+
if hasattr(msg, 'tool_calls') and msg.tool_calls:
|
|
320
|
+
msg_data["tool_calls"] = msg.tool_calls
|
|
321
|
+
if hasattr(msg, 'tool_call_id') and msg.tool_call_id:
|
|
322
|
+
msg_data["tool_call_id"] = msg.tool_call_id
|
|
323
|
+
|
|
324
|
+
history_data["messages"].append(msg_data)
|
|
325
|
+
|
|
326
|
+
# Save to disk
|
|
327
|
+
save_path.parent.mkdir(parents=True, exist_ok=True)
|
|
328
|
+
with open(save_path, 'w') as f:
|
|
329
|
+
json.dump(history_data, f, indent=2)
|
|
330
|
+
|
|
331
|
+
self.logger.info(f"💾 Saved {len(self.history.messages)} messages to {save_path}")
|
|
332
|
+
|
|
333
|
+
# Opt-in durable mirror (MESSAGE_STORE_BACKEND=sqlite). Additive: JSON stays
|
|
334
|
+
# the source of truth. UP-10 2.3: the read path is intentionally NOT wired —
|
|
335
|
+
# load_from_disk only reads message_history.json (see below), so this mirror
|
|
336
|
+
# is write-only and can silently diverge. A correct read path needs payload
|
|
337
|
+
# parity (tool_call_id + metadata) and a JSON/DB reconciliation policy; that is
|
|
338
|
+
# scoped to a dedicated durable-store proposal. We keep the pre-staged write but
|
|
339
|
+
# emit a one-time WARN so enabling the flag is honest about being write-only.
|
|
340
|
+
import os
|
|
341
|
+
if os.getenv("MESSAGE_STORE_BACKEND", "").lower() == "sqlite":
|
|
342
|
+
global _warned_sqlite_mirror
|
|
343
|
+
if not _warned_sqlite_mirror:
|
|
344
|
+
_warned_sqlite_mirror = True
|
|
345
|
+
self.logger.warning(
|
|
346
|
+
"MESSAGE_STORE_BACKEND=sqlite is a write-only mirror; reads still come "
|
|
347
|
+
"from message_history.json. The sqlite DB is pre-staged but never read "
|
|
348
|
+
"(no read path wired yet — see UP-10 2.3). It may diverge from JSON."
|
|
349
|
+
)
|
|
350
|
+
try:
|
|
351
|
+
from agents.task.agent.messages.sqlite_persistence import SqliteMessageStore
|
|
352
|
+
store = SqliteMessageStore(os.path.join(str(getattr(pm(), "data_root", "data")), "messages.db"))
|
|
353
|
+
# MED-4: atomic single-transaction swap (no clear()+append() race/churn).
|
|
354
|
+
store.replace_all(session_id, [
|
|
355
|
+
{
|
|
356
|
+
"type": managed.message.__class__.__name__,
|
|
357
|
+
"content": getattr(managed.message, "content", ""),
|
|
358
|
+
"tool_calls": getattr(managed.message, "tool_calls", None),
|
|
359
|
+
}
|
|
360
|
+
for managed in self.history.messages
|
|
361
|
+
])
|
|
362
|
+
except Exception as e:
|
|
363
|
+
self.logger.debug(f"sqlite message mirror skipped: {e}")
|
|
364
|
+
|
|
365
|
+
except Exception as e:
|
|
366
|
+
self.logger.error(f"Failed to save message history: {e}", exc_info=True)
|
|
367
|
+
|
|
368
|
+
def load_from_disk(self, session_id: str, user_id: Optional[str] = None) -> bool:
|
|
369
|
+
"""Load message history from disk to restore session state.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
session_id: Session identifier
|
|
373
|
+
user_id: Optional user identifier for path construction
|
|
374
|
+
|
|
375
|
+
Returns:
|
|
376
|
+
True if messages were loaded, False if no saved history exists
|
|
377
|
+
"""
|
|
378
|
+
from agents.task.path import pm
|
|
379
|
+
|
|
380
|
+
load_path = pm().create_file_path(
|
|
381
|
+
session_id=session_id,
|
|
382
|
+
subdir_name="memory",
|
|
383
|
+
filename="message_history.json",
|
|
384
|
+
user_id=user_id
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
if not load_path.exists():
|
|
388
|
+
self.logger.info(f"No saved message history found for session {session_id}")
|
|
389
|
+
return False
|
|
390
|
+
|
|
391
|
+
try:
|
|
392
|
+
with open(load_path, 'r') as f:
|
|
393
|
+
history_data = json.load(f)
|
|
394
|
+
|
|
395
|
+
# Reconstruct messages
|
|
396
|
+
message_classes = {
|
|
397
|
+
"HumanMessage": HumanMessage,
|
|
398
|
+
"AIMessage": AIMessage,
|
|
399
|
+
"SystemMessage": SystemMessage,
|
|
400
|
+
"ToolMessage": ToolMessage
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
messages_loaded = 0
|
|
404
|
+
for msg_data in history_data["messages"]:
|
|
405
|
+
msg_class = message_classes.get(msg_data["type"])
|
|
406
|
+
if not msg_class:
|
|
407
|
+
self.logger.warning(f"Unknown message type: {msg_data['type']}, skipping")
|
|
408
|
+
continue
|
|
409
|
+
|
|
410
|
+
# Reconstruct message
|
|
411
|
+
message = msg_class(content=msg_data["content"])
|
|
412
|
+
# P4: restore the true source (pre-P4 files have no key → USER, the
|
|
413
|
+
# historical behaviour).
|
|
414
|
+
message.origin = msg_data.get("origin", MessageOrigin.USER)
|
|
415
|
+
# CX-H1: restore the state-message tag so a rehydrated session can
|
|
416
|
+
# still target it by identity (pre-tag files have no key → {}, the
|
|
417
|
+
# legacy shape-heuristic fallback in remove_last_state_message
|
|
418
|
+
# still applies to those).
|
|
419
|
+
if msg_data.get("msg_metadata"):
|
|
420
|
+
message.metadata = msg_data["msg_metadata"]
|
|
421
|
+
|
|
422
|
+
# Add tool call data if present
|
|
423
|
+
if msg_data.get("tool_calls"):
|
|
424
|
+
message.tool_calls = msg_data["tool_calls"]
|
|
425
|
+
if msg_data.get("tool_call_id"):
|
|
426
|
+
message.tool_call_id = msg_data["tool_call_id"]
|
|
427
|
+
|
|
428
|
+
# Add to history (this will recalculate tokens). Restoring persisted
|
|
429
|
+
# AI/Tool messages is a trusted internal path, so bypass the single-writer
|
|
430
|
+
# guard (which otherwise rejects re-adding AIMessage/ToolMessage and aborts
|
|
431
|
+
# the whole restore → a warm session resume gets stuck 'initializing').
|
|
432
|
+
self.add_message(message, _internal=True)
|
|
433
|
+
messages_loaded += 1
|
|
434
|
+
# P4: keep the ORIGINAL turn timestamp/source (add_message stamps
|
|
435
|
+
# "now", which would erase message age on every rehydrate).
|
|
436
|
+
saved_meta = msg_data.get("metadata") or {}
|
|
437
|
+
restored = self.history.messages[-1].metadata
|
|
438
|
+
if saved_meta.get("timestamp"):
|
|
439
|
+
restored.timestamp = saved_meta["timestamp"]
|
|
440
|
+
if saved_meta.get("source"):
|
|
441
|
+
restored.source = saved_meta["source"]
|
|
442
|
+
|
|
443
|
+
self.logger.info(f"📂 Loaded {messages_loaded} messages from {load_path}")
|
|
444
|
+
return True
|
|
445
|
+
|
|
446
|
+
except Exception as e:
|
|
447
|
+
self.logger.error(f"Failed to load message history: {e}", exc_info=True)
|
|
448
|
+
return False
|