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,275 @@
|
|
|
1
|
+
"""Restart-durable autonomy state.
|
|
2
|
+
|
|
3
|
+
Two registries used to be volatile process memory, making autonomy turn-durable
|
|
4
|
+
but not restart-durable:
|
|
5
|
+
|
|
6
|
+
- ``AsyncDelegationRegistry`` (UP-12) — a background delegation dispatched and
|
|
7
|
+
then lost to a restart silently evaporated: the parent session was promised a
|
|
8
|
+
``delegation_result`` that never arrived.
|
|
9
|
+
- ``ReentryBudget`` (W1) — a forged-wake storm mid-ping-pong got a free depth
|
|
10
|
+
reset by crashing.
|
|
11
|
+
|
|
12
|
+
``AutonomyStateStore`` persists both on the standard WAL+jitter sidecar pattern
|
|
13
|
+
(``core/sqlite_util``, same as goals.db/cron.db) in ``autonomy_state.db`` under
|
|
14
|
+
the data root (registered in ``core/db_manifest.py`` so backup/rollback snapshot
|
|
15
|
+
it). Recovery is HONEST, never magic: a delegation still ``running`` at process
|
|
16
|
+
start was crash-interrupted — :func:`recover_interrupted_delegations` marks it
|
|
17
|
+
``interrupted`` and surfaces that back to the originating session via the
|
|
18
|
+
self-wake rail (or leaves the durable row as the record when the wake is
|
|
19
|
+
dropped/disabled). The child coroutine itself is never resumed.
|
|
20
|
+
|
|
21
|
+
Everything here is fail-open: a store error degrades the registries to their
|
|
22
|
+
legacy in-memory behavior, never blocks a dispatch or a wake.
|
|
23
|
+
"""
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
import logging
|
|
27
|
+
import os
|
|
28
|
+
import threading
|
|
29
|
+
import time
|
|
30
|
+
from typing import Any, List, Optional
|
|
31
|
+
|
|
32
|
+
from core.sqlite_util import execute_retry
|
|
33
|
+
|
|
34
|
+
logger = logging.getLogger(__name__)
|
|
35
|
+
|
|
36
|
+
_RESULT_TEXT_CAP = 4000
|
|
37
|
+
# Budget rows older than this are dropped on hydrate — a wake recorded days ago
|
|
38
|
+
# should not still be spacing/backing-off a fresh process.
|
|
39
|
+
STALE_BUDGET_SEC = 7 * 86400
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def default_autonomy_state_db() -> str:
|
|
43
|
+
"""Resolve autonomy_state.db NEXT TO its sibling autonomy DBs.
|
|
44
|
+
|
|
45
|
+
cron.db/goals.db/memory.db live under the container config's ``data_dir``
|
|
46
|
+
(server: ``{POLYROB_DATA_DIR}/data``; CLI: reconciled to the ``.polyrob``
|
|
47
|
+
root by build_cli_container) — prefer that, so ops tooling that assumes
|
|
48
|
+
co-location never misses this DB. Fall back to ``get_data_root()`` (the CLI
|
|
49
|
+
resolution) when no container config is available.
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
from core.container import DependencyContainer
|
|
53
|
+
cfg = DependencyContainer.get_instance().get_service("config")
|
|
54
|
+
data_dir = getattr(cfg, "data_dir", None)
|
|
55
|
+
if data_dir:
|
|
56
|
+
return os.path.join(str(data_dir), "autonomy_state.db")
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
from core.runtime_config import get_data_root
|
|
60
|
+
return os.path.join(get_data_root(), "autonomy_state.db")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class AutonomyStateStore:
|
|
64
|
+
"""WAL-backed store for delegations + reentry budgets (one per data root)."""
|
|
65
|
+
|
|
66
|
+
def __init__(self, db_path: str):
|
|
67
|
+
self.db_path = db_path
|
|
68
|
+
os.makedirs(os.path.dirname(db_path) or ".", exist_ok=True)
|
|
69
|
+
execute_retry(
|
|
70
|
+
db_path,
|
|
71
|
+
"""CREATE TABLE IF NOT EXISTS delegations (
|
|
72
|
+
session_id TEXT NOT NULL,
|
|
73
|
+
delegation_id TEXT NOT NULL,
|
|
74
|
+
user_id TEXT NOT NULL DEFAULT '',
|
|
75
|
+
goal TEXT,
|
|
76
|
+
profile TEXT,
|
|
77
|
+
parent_agent_id TEXT,
|
|
78
|
+
status TEXT NOT NULL DEFAULT 'running',
|
|
79
|
+
dispatched_at REAL,
|
|
80
|
+
completed_at REAL,
|
|
81
|
+
result_text TEXT,
|
|
82
|
+
PRIMARY KEY (session_id, delegation_id)
|
|
83
|
+
)""",
|
|
84
|
+
)
|
|
85
|
+
execute_retry(
|
|
86
|
+
db_path,
|
|
87
|
+
"CREATE INDEX IF NOT EXISTS idx_delegations_status ON delegations(status)",
|
|
88
|
+
)
|
|
89
|
+
execute_retry(
|
|
90
|
+
db_path,
|
|
91
|
+
"""CREATE TABLE IF NOT EXISTS reentry_budget (
|
|
92
|
+
session_id TEXT PRIMARY KEY,
|
|
93
|
+
user_id TEXT NOT NULL DEFAULT '',
|
|
94
|
+
count INTEGER NOT NULL DEFAULT 0,
|
|
95
|
+
last_wake_at REAL NOT NULL DEFAULT 0
|
|
96
|
+
)""",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
# -- delegations -----------------------------------------------------------
|
|
100
|
+
|
|
101
|
+
def record_dispatched(self, *, session_id: str, user_id: str, delegation_id: str,
|
|
102
|
+
goal: str, profile: str, parent_agent_id: Optional[str],
|
|
103
|
+
dispatched_at: float) -> None:
|
|
104
|
+
execute_retry(
|
|
105
|
+
self.db_path,
|
|
106
|
+
"""INSERT OR REPLACE INTO delegations
|
|
107
|
+
(session_id, delegation_id, user_id, goal, profile, parent_agent_id,
|
|
108
|
+
status, dispatched_at)
|
|
109
|
+
VALUES (?,?,?,?,?,?,'running',?)""",
|
|
110
|
+
(session_id, delegation_id, user_id, goal, profile,
|
|
111
|
+
parent_agent_id, dispatched_at),
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def record_terminal(self, session_id: str, delegation_id: str, *, status: str,
|
|
115
|
+
completed_at: float, result_text: str = "") -> None:
|
|
116
|
+
execute_retry(
|
|
117
|
+
self.db_path,
|
|
118
|
+
"""UPDATE delegations SET status=?, completed_at=?, result_text=?
|
|
119
|
+
WHERE session_id=? AND delegation_id=?""",
|
|
120
|
+
(status, completed_at, (result_text or "")[:_RESULT_TEXT_CAP],
|
|
121
|
+
session_id, delegation_id),
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
def get(self, session_id: str, delegation_id: str) -> Optional[dict]:
|
|
125
|
+
row = execute_retry(
|
|
126
|
+
self.db_path,
|
|
127
|
+
"SELECT * FROM delegations WHERE session_id=? AND delegation_id=?",
|
|
128
|
+
(session_id, delegation_id), fetch="one",
|
|
129
|
+
)
|
|
130
|
+
return dict(row) if row else None
|
|
131
|
+
|
|
132
|
+
def list_running(self) -> List[dict]:
|
|
133
|
+
rows = execute_retry(
|
|
134
|
+
self.db_path, "SELECT * FROM delegations WHERE status='running'",
|
|
135
|
+
fetch="all",
|
|
136
|
+
)
|
|
137
|
+
return [dict(r) for r in (rows or [])]
|
|
138
|
+
|
|
139
|
+
def max_counter(self, session_id: str) -> int:
|
|
140
|
+
"""Highest numeric suffix of this session's deleg_NNNN ids — seeds the
|
|
141
|
+
in-memory counter so a restarted session never reissues an id."""
|
|
142
|
+
rows = execute_retry(
|
|
143
|
+
self.db_path,
|
|
144
|
+
"SELECT delegation_id FROM delegations WHERE session_id=?",
|
|
145
|
+
(session_id,), fetch="all",
|
|
146
|
+
)
|
|
147
|
+
best = 0
|
|
148
|
+
for r in rows or []:
|
|
149
|
+
did = r["delegation_id"] or ""
|
|
150
|
+
if did.startswith("deleg_"):
|
|
151
|
+
try:
|
|
152
|
+
best = max(best, int(did.split("_", 1)[1]))
|
|
153
|
+
except ValueError:
|
|
154
|
+
continue
|
|
155
|
+
return best
|
|
156
|
+
|
|
157
|
+
# -- reentry budget ----------------------------------------------------------
|
|
158
|
+
|
|
159
|
+
def get_budget(self, session_id: str) -> Optional[dict]:
|
|
160
|
+
row = execute_retry(
|
|
161
|
+
self.db_path,
|
|
162
|
+
"SELECT count, last_wake_at FROM reentry_budget WHERE session_id=?",
|
|
163
|
+
(session_id,), fetch="one",
|
|
164
|
+
)
|
|
165
|
+
return dict(row) if row else None
|
|
166
|
+
|
|
167
|
+
def put_budget(self, session_id: str, user_id: str, *, count: int,
|
|
168
|
+
last_wake_at: float) -> None:
|
|
169
|
+
execute_retry(
|
|
170
|
+
self.db_path,
|
|
171
|
+
"""INSERT INTO reentry_budget (session_id, user_id, count, last_wake_at)
|
|
172
|
+
VALUES (?,?,?,?)
|
|
173
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
174
|
+
count=excluded.count, last_wake_at=excluded.last_wake_at""",
|
|
175
|
+
(session_id, user_id, count, last_wake_at),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def delete_budget(self, session_id: str) -> None:
|
|
179
|
+
execute_retry(
|
|
180
|
+
self.db_path, "DELETE FROM reentry_budget WHERE session_id=?",
|
|
181
|
+
(session_id,),
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
_STORE_LOCK = threading.Lock()
|
|
186
|
+
_STORE_CACHE: dict = {}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def get_autonomy_state_store() -> Optional[AutonomyStateStore]:
|
|
190
|
+
"""The store at the default path, or None when durability is off or the
|
|
191
|
+
store cannot be opened (fail-open to legacy in-memory behavior).
|
|
192
|
+
|
|
193
|
+
Memoized per resolved path — orchestrators are constructed per session and
|
|
194
|
+
the store's schema init is blocking sqlite I/O; it must run once per
|
|
195
|
+
process, not once per session.
|
|
196
|
+
"""
|
|
197
|
+
try:
|
|
198
|
+
from agents.task.constants import AutonomyConfig
|
|
199
|
+
if not AutonomyConfig.autonomy_state_durable():
|
|
200
|
+
return None
|
|
201
|
+
path = default_autonomy_state_db()
|
|
202
|
+
with _STORE_LOCK:
|
|
203
|
+
store = _STORE_CACHE.get(path)
|
|
204
|
+
if store is None:
|
|
205
|
+
store = AutonomyStateStore(path)
|
|
206
|
+
_STORE_CACHE[path] = store
|
|
207
|
+
return store
|
|
208
|
+
except Exception:
|
|
209
|
+
logger.warning("autonomy_state store unavailable — running in-memory only",
|
|
210
|
+
exc_info=True)
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def reset_autonomy_state_store_cache() -> None:
|
|
215
|
+
"""Test seam: drop memoized stores so path/env changes take effect."""
|
|
216
|
+
with _STORE_LOCK:
|
|
217
|
+
_STORE_CACHE.clear()
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
async def recover_interrupted_delegations(task_agent: Any, db_path: str) -> int:
|
|
221
|
+
"""Startup sweep: mark crash-interrupted delegations and surface them.
|
|
222
|
+
|
|
223
|
+
Returns the number of rows recovered. The self-wake delivery is best-effort
|
|
224
|
+
(SELF_WAKE_ENABLED off / non-resident session / budget exhausted → the wake
|
|
225
|
+
is dropped, but the durable ``interrupted`` row remains the honest record).
|
|
226
|
+
"""
|
|
227
|
+
if not os.path.exists(db_path):
|
|
228
|
+
return 0
|
|
229
|
+
try:
|
|
230
|
+
store = AutonomyStateStore(db_path)
|
|
231
|
+
rows = store.list_running()
|
|
232
|
+
except Exception:
|
|
233
|
+
logger.warning("delegation recovery sweep failed to read store", exc_info=True)
|
|
234
|
+
return 0
|
|
235
|
+
recovered = 0
|
|
236
|
+
for row in rows:
|
|
237
|
+
session_id, delegation_id = row["session_id"], row["delegation_id"]
|
|
238
|
+
try:
|
|
239
|
+
store.record_terminal(
|
|
240
|
+
session_id, delegation_id, status="interrupted",
|
|
241
|
+
completed_at=time.time(),
|
|
242
|
+
result_text="Process restarted while this delegation was running.",
|
|
243
|
+
)
|
|
244
|
+
recovered += 1
|
|
245
|
+
except Exception:
|
|
246
|
+
logger.warning("could not mark delegation %s/%s interrupted",
|
|
247
|
+
session_id, delegation_id, exc_info=True)
|
|
248
|
+
continue
|
|
249
|
+
try:
|
|
250
|
+
from agents.task.telemetry.event_log import get_event_log, event_log_enabled
|
|
251
|
+
if event_log_enabled():
|
|
252
|
+
get_event_log().record(
|
|
253
|
+
"delegation_interrupted", user_id=row.get("user_id", ""),
|
|
254
|
+
session_id=session_id, source="autonomy_state",
|
|
255
|
+
attrs={"delegation_id": delegation_id,
|
|
256
|
+
"goal": (row.get("goal") or "")[:200]},
|
|
257
|
+
)
|
|
258
|
+
except Exception:
|
|
259
|
+
pass
|
|
260
|
+
try:
|
|
261
|
+
deliver = getattr(task_agent, "deliver_self_wake", None)
|
|
262
|
+
if deliver is not None:
|
|
263
|
+
await deliver(
|
|
264
|
+
session_id, row.get("user_id", ""),
|
|
265
|
+
(f"Background delegation {delegation_id} "
|
|
266
|
+
f"({(row.get('goal') or '')[:200]}) was interrupted by a process "
|
|
267
|
+
"restart and did NOT complete. Re-dispatch it if the work is "
|
|
268
|
+
"still needed."),
|
|
269
|
+
metadata={"delegation_id": delegation_id,
|
|
270
|
+
"recovery": "interrupted_delegation"},
|
|
271
|
+
)
|
|
272
|
+
except Exception:
|
|
273
|
+
logger.warning("interrupted-delegation wake for %s failed (row remains "
|
|
274
|
+
"the durable record)", session_id, exc_info=True)
|
|
275
|
+
return recovered
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""First-class multi-turn conversation over the existing agent loop (R4).
|
|
2
|
+
|
|
3
|
+
A Conversation owns ordered Turns and drives the agent one turn at a time with a
|
|
4
|
+
small per-turn step budget, returning the answer synchronously — instead of the
|
|
5
|
+
HITL resume FSM (which re-enters a 100-step task loop per reply). Server task
|
|
6
|
+
sessions are unaffected; this is opt-in via Conversation.
|
|
7
|
+
"""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any, Iterable, List
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def extract_answer(history: Any) -> str:
|
|
14
|
+
"""The agent's reply for a turn = the last is_done result's extracted_content.
|
|
15
|
+
|
|
16
|
+
Accepts either a flat iterable of ActionResults or the AgentHistoryList that
|
|
17
|
+
Agent.run() actually returns. AgentHistoryList is a pydantic model: iterating
|
|
18
|
+
it yields (field, value) tuples, not results, so unwrap via action_results()
|
|
19
|
+
when available before scanning for the terminal is_done result.
|
|
20
|
+
"""
|
|
21
|
+
results: Iterable[Any]
|
|
22
|
+
if hasattr(history, "action_results") and callable(history.action_results):
|
|
23
|
+
results = history.action_results()
|
|
24
|
+
else:
|
|
25
|
+
results = history
|
|
26
|
+
answer = ""
|
|
27
|
+
for result in results:
|
|
28
|
+
if getattr(result, "is_done", False) and getattr(result, "extracted_content", None):
|
|
29
|
+
answer = result.extracted_content
|
|
30
|
+
return answer
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
DEFAULT_TURN_BUDGET = 20 # interactive turns; raise for big asks, autonomous tasks use run()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _reset_turn_state(agent: Any) -> None:
|
|
37
|
+
"""Clear transient failure state so a NEW user turn gets a fresh attempt.
|
|
38
|
+
|
|
39
|
+
Agent.run() resets n_steps but not consecutive_failures / _cancelled. Without
|
|
40
|
+
this, once a turn hits max consecutive failures (e.g. an LLM quota error), the
|
|
41
|
+
agent stays wedged and every later respond() returns 'failed | -1 steps' — the
|
|
42
|
+
REPL is dead until restart. A fresh user message should start clean.
|
|
43
|
+
"""
|
|
44
|
+
state = getattr(agent, "state", None)
|
|
45
|
+
if state is not None and hasattr(state, "consecutive_failures"):
|
|
46
|
+
state.consecutive_failures = 0
|
|
47
|
+
if getattr(agent, "_cancelled", False):
|
|
48
|
+
agent._cancelled = False
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass
|
|
52
|
+
class Turn:
|
|
53
|
+
user: str
|
|
54
|
+
assistant: str = ""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class Conversation:
|
|
59
|
+
agent: Any
|
|
60
|
+
turns: List[Turn] = field(default_factory=list)
|
|
61
|
+
|
|
62
|
+
async def respond(self, text: str, max_steps: int = DEFAULT_TURN_BUDGET) -> str:
|
|
63
|
+
"""Run exactly one conversational turn and return the agent's reply.
|
|
64
|
+
|
|
65
|
+
After the first turn we pass _continue_session=True so run() skips the
|
|
66
|
+
once-per-session preamble (session-start telemetry + H-MEM create/load),
|
|
67
|
+
which would otherwise re-fire on every REPL line.
|
|
68
|
+
"""
|
|
69
|
+
_reset_turn_state(self.agent) # recover from a prior failed turn
|
|
70
|
+
self.agent.set_turn_input(text) # append-as-turn (Task 7)
|
|
71
|
+
history = await self.agent.run(
|
|
72
|
+
max_steps=max_steps, _continue_session=bool(self.turns))
|
|
73
|
+
answer = extract_answer(history)
|
|
74
|
+
self.turns.append(Turn(user=text, assistant=answer))
|
|
75
|
+
return answer
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Meter auxiliary LLM calls (compaction, judge, reflection) through the single
|
|
2
|
+
usage/deduction path. These calls were unbilled — only the main step
|
|
3
|
+
(next_action_internal.py:388) recorded usage — so long-context compaction on the
|
|
4
|
+
flagship model was free. Per operator decision aux calls DEDUCT like main calls, but
|
|
5
|
+
this helper FAILS OPEN on any error (including InsufficientCreditsError): aux calls are
|
|
6
|
+
internal maintenance and must never hard-halt a session. The deduction still happens
|
|
7
|
+
whenever the balance covers it.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from typing import Any, Optional
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _llm_identity(llm: Any, response: Any = None) -> tuple[str, str]:
|
|
18
|
+
"""Derive (model, provider) from the llm object, falling back to detecting
|
|
19
|
+
the provider from the model name via ``detect_llm_provider`` (which takes a
|
|
20
|
+
RESPONSE + optional model name — the response param is unused by that
|
|
21
|
+
function but kept for signature compatibility; the model name is what it
|
|
22
|
+
actually looks up in the model registry).
|
|
23
|
+
"""
|
|
24
|
+
model = (getattr(llm, "model_name", None) or getattr(llm, "model", None)
|
|
25
|
+
or getattr(llm, "model_type", None) or "unknown")
|
|
26
|
+
provider = (getattr(llm, "llm_provider", None) or getattr(llm, "provider_name", None))
|
|
27
|
+
if not provider:
|
|
28
|
+
try:
|
|
29
|
+
from agents.task.utils import detect_llm_provider
|
|
30
|
+
provider = detect_llm_provider(response, model) or "unknown"
|
|
31
|
+
except Exception:
|
|
32
|
+
provider = "unknown"
|
|
33
|
+
return str(model), str(provider)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def meter_aux_llm(*, usage_tracker, user_id: Optional[str], session_id: str,
|
|
37
|
+
agent_id: str, llm: Any, response: Any, duration_seconds: float,
|
|
38
|
+
component: str, purpose: str) -> None:
|
|
39
|
+
"""Record usage for an auxiliary (non-main-step) LLM call.
|
|
40
|
+
|
|
41
|
+
Fail-open: no-ops silently when there's no tracker/user_id (e.g. anonymous
|
|
42
|
+
local sessions), and swallows ANY exception from token extraction or
|
|
43
|
+
``record_llm_usage`` itself (including InsufficientCreditsError) — an aux
|
|
44
|
+
call is internal maintenance and must never abort the caller.
|
|
45
|
+
"""
|
|
46
|
+
if not (usage_tracker and user_id):
|
|
47
|
+
return
|
|
48
|
+
try:
|
|
49
|
+
from agents.task.utils import extract_token_usage
|
|
50
|
+
model, provider = _llm_identity(llm, response)
|
|
51
|
+
usage = extract_token_usage(response, provider) or {}
|
|
52
|
+
await usage_tracker.record_llm_usage(
|
|
53
|
+
user_id=user_id, session_id=session_id, agent_id=agent_id,
|
|
54
|
+
model=model, provider=provider,
|
|
55
|
+
input_tokens=usage.get("prompt_tokens") or 0,
|
|
56
|
+
output_tokens=usage.get("completion_tokens") or 0,
|
|
57
|
+
cached_tokens=usage.get("cached_tokens") or 0,
|
|
58
|
+
duration_seconds=duration_seconds, component=component, purpose=purpose, success=True,
|
|
59
|
+
)
|
|
60
|
+
except Exception as e: # fail-open (incl. InsufficientCreditsError): never break the aux op
|
|
61
|
+
logger.warning(f"aux metering skipped ({component}/{purpose}): {type(e).__name__}: {e}")
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"""Post-turn background-review fork (W2-C, Reference-parity self-improvement loop).
|
|
2
|
+
|
|
3
|
+
After a run of productive turns, the agent forks a cheap, fire-and-forget sub-agent
|
|
4
|
+
that reviews what just happened and — when it finds a durable, reusable procedure —
|
|
5
|
+
authors a skill (via the W2 ``skill_manage`` tool, which quarantines it for review).
|
|
6
|
+
This is the "learning" loop: experience → distilled skill → available next session.
|
|
7
|
+
|
|
8
|
+
Design:
|
|
9
|
+
- **Non-blocking** — ``asyncio.create_task`` so the run loop returns immediately; a
|
|
10
|
+
review never delays the user-facing turn.
|
|
11
|
+
- **Cheap** — runs on the aux/judge model (``_provision_aux_llm("judge")``) and
|
|
12
|
+
inherits the cached system prefix → prefix-cache hit (Reference's ~26% saving).
|
|
13
|
+
- **Least-privilege** — spawned through ``SubAgentManager.run_subtask`` as a depth-1
|
|
14
|
+
LEAF (UP-05 child controller: no code-exec/cron, cannot re-delegate).
|
|
15
|
+
- **Bounded** — at most ``BG_REVIEW_MAX_STEPS`` steps; exempt for sub-agents (a
|
|
16
|
+
reviewer never forks its own reviewer).
|
|
17
|
+
- **Fail-open** — any error is swallowed; the loop must never break on a review.
|
|
18
|
+
|
|
19
|
+
Gated ``BACKGROUND_REVIEW_ENABLED`` (default OFF). Authored skills are quarantined
|
|
20
|
+
(``created_by="background_review"`` → always ``.pending/``), so even a compromised
|
|
21
|
+
review cannot self-activate a skill.
|
|
22
|
+
"""
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import asyncio
|
|
26
|
+
import logging
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
_REVIEW_PROMPT = (
|
|
31
|
+
"You are a background reviewer for an AI agent. The agent just finished a run of "
|
|
32
|
+
"work in this session. Review the recent conversation/work and decide whether a "
|
|
33
|
+
"DURABLE, REUSABLE procedure emerged that would help in future sessions.\n\n"
|
|
34
|
+
"If yes: call skill_manage(action='create', skill_id=<short-kebab-id>, "
|
|
35
|
+
"content=<a concise SKILL.md: a '# Title' heading then when-to-use + steps>). "
|
|
36
|
+
"Keep it general (no secrets, no session-specific data). Author at most ONE skill.\n"
|
|
37
|
+
"If nothing is worth saving: call done() with a one-line reason. Do not do any "
|
|
38
|
+
"other work, browse, or message the user."
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
# polyrob Phase E: the "learns over time" engine. The reviewer already runs as a
|
|
42
|
+
# LEAF sub-agent, so a self_context_manage write resolves to background provenance →
|
|
43
|
+
# ALWAYS .pending, NEVER auto-active and NEVER promotable by this forged turn. So we
|
|
44
|
+
# can safely invite it to ALSO propose a consolidated SELF-context refinement; the
|
|
45
|
+
# owner promotes it later. Only offered when SELF_CONTEXT_WRITABLE is on.
|
|
46
|
+
_SELF_CONTEXT_REVIEW_ADDENDUM = (
|
|
47
|
+
"\n\nSEPARATELY: if you learned something durable about how to work with THIS user "
|
|
48
|
+
"(a stable preference, convention, or fact worth remembering), you may also call "
|
|
49
|
+
"self_context_manage(action='update', content=<a consolidated self.md, ≤2200 "
|
|
50
|
+
"chars — MERGE with what's already there via action='read' first; do not sprawl>). "
|
|
51
|
+
"This proposes a refinement to your evolving SELF context; it is QUARANTINED for "
|
|
52
|
+
"owner review and applies next session. Keep it about working-style/preferences, "
|
|
53
|
+
"never your core identity or boundaries (those are operator-owned)."
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def build_review_prompt() -> str:
|
|
58
|
+
"""The reviewer task. Adds the SELF-context-refinement invitation only when the
|
|
59
|
+
SELF_CONTEXT_WRITABLE tool is actually available (else the legacy skills-only
|
|
60
|
+
prompt, byte-identical)."""
|
|
61
|
+
try:
|
|
62
|
+
from agents.task.constants import AutonomyConfig
|
|
63
|
+
if AutonomyConfig.self_context_writable():
|
|
64
|
+
return _REVIEW_PROMPT + _SELF_CONTEXT_REVIEW_ADDENDUM
|
|
65
|
+
except Exception:
|
|
66
|
+
pass
|
|
67
|
+
return _REVIEW_PROMPT
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class BackgroundReviewMixin:
|
|
71
|
+
"""Fire a post-turn self-improvement reviewer. Composed into Agent."""
|
|
72
|
+
|
|
73
|
+
def _bg_review_should_fire(self, turn_was_productive: bool) -> bool:
|
|
74
|
+
"""Pure decision: increment the productive-turn counter and report whether the
|
|
75
|
+
interval has elapsed. Resets the counter when it fires."""
|
|
76
|
+
from agents.task.constants import AutonomyConfig
|
|
77
|
+
if not AutonomyConfig.background_review_enabled():
|
|
78
|
+
return False
|
|
79
|
+
if not AutonomyConfig.skills_writable():
|
|
80
|
+
# SK-F8: the reviewer's whole output rail is skill_manage(create) —
|
|
81
|
+
# firing it while skills are read-only just burns an aux-model run
|
|
82
|
+
# that is guaranteed to fail at the tool layer.
|
|
83
|
+
return False
|
|
84
|
+
if getattr(self, "_is_sub_agent", False):
|
|
85
|
+
return False # a reviewer never forks a reviewer
|
|
86
|
+
if not turn_was_productive:
|
|
87
|
+
return False
|
|
88
|
+
interval = max(1, AutonomyConfig.bg_review_interval())
|
|
89
|
+
n = getattr(self, "_bg_review_productive_turns", 0) + 1
|
|
90
|
+
if n >= interval:
|
|
91
|
+
self._bg_review_productive_turns = 0
|
|
92
|
+
return True
|
|
93
|
+
self._bg_review_productive_turns = n
|
|
94
|
+
return False
|
|
95
|
+
|
|
96
|
+
def _maybe_spawn_background_review(self, *, turn_was_productive: bool) -> None:
|
|
97
|
+
"""Fire-and-forget the reviewer if the cadence says so. Never blocks/raises."""
|
|
98
|
+
try:
|
|
99
|
+
if not self._bg_review_should_fire(turn_was_productive):
|
|
100
|
+
return
|
|
101
|
+
task = asyncio.create_task(self._run_background_review())
|
|
102
|
+
# M5: keep a strong reference so CPython can't GC the task mid-run (a task no
|
|
103
|
+
# one references may be collected before it completes), and so it's cancelled
|
|
104
|
+
# at session teardown. Mirrors _stall_check_task; self-removes when done.
|
|
105
|
+
_tasks = getattr(self.orchestrator, "_execution_tasks", None)
|
|
106
|
+
if isinstance(_tasks, list):
|
|
107
|
+
_tasks.append(task)
|
|
108
|
+
task.add_done_callback(lambda t, _l=_tasks: (_l.remove(t) if t in _l else None))
|
|
109
|
+
logger.info("🪞 spawned background-review fork (session %s)",
|
|
110
|
+
getattr(self.orchestrator, "session_id", "?"))
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.debug("background-review spawn skipped: %s", e)
|
|
113
|
+
|
|
114
|
+
async def _run_background_review(self) -> None:
|
|
115
|
+
"""Run the reviewer sub-agent on the aux model. Fail-open."""
|
|
116
|
+
try:
|
|
117
|
+
from agents.task.constants import AutonomyConfig
|
|
118
|
+
manager = self.orchestrator.get_sub_agent_manager() if self.orchestrator else None
|
|
119
|
+
if manager is None:
|
|
120
|
+
return
|
|
121
|
+
# P2-8: reuse the agent's cached judge client instead of building a fresh
|
|
122
|
+
# isolated client (with its own httpx pool) on every fire — the old code
|
|
123
|
+
# leaked one pool per background review. Cached on self._judge_llm (the same
|
|
124
|
+
# slot _validate_output uses) and closed once at session cleanup (M2).
|
|
125
|
+
aux = getattr(self, "_judge_llm", None)
|
|
126
|
+
if aux is None:
|
|
127
|
+
try:
|
|
128
|
+
# P2-9: async provisioning — don't block the loop building the client.
|
|
129
|
+
aux = await self._provision_aux_llm_async("judge")
|
|
130
|
+
if aux is not None:
|
|
131
|
+
self._judge_llm = aux
|
|
132
|
+
except Exception:
|
|
133
|
+
aux = None
|
|
134
|
+
# P2-10: make the reviewer's model cost VISIBLE. The cheap-aux map has no
|
|
135
|
+
# openrouter/nvidia key, so on the default OpenRouter deploy `aux` is None and
|
|
136
|
+
# this PROACTIVE loop (fires every BG_REVIEW_INTERVAL turns without any user
|
|
137
|
+
# ask) runs up to bg_review_max_steps on the FLAGSHIP main model. Log at INFO
|
|
138
|
+
# which model it will use so the cost isn't silent (add an openrouter/nvidia
|
|
139
|
+
# cheap-map entry or set AUX_MODEL_JUDGE to route it cheap).
|
|
140
|
+
_review_llm = aux or self.llm
|
|
141
|
+
if aux is None:
|
|
142
|
+
logger.info(
|
|
143
|
+
"background review running on the MAIN model %r (no cheap aux "
|
|
144
|
+
"resolved) — set AUX_MODEL_JUDGE or add a provider cheap-map entry "
|
|
145
|
+
"to route it to a cheaper model",
|
|
146
|
+
getattr(self.llm, "model_name", getattr(self.llm, "model_type", "?")),
|
|
147
|
+
)
|
|
148
|
+
await manager.run_subtask(
|
|
149
|
+
task=build_review_prompt(),
|
|
150
|
+
parent_agent_id=self.agent_id,
|
|
151
|
+
profile_id="executor",
|
|
152
|
+
max_steps=AutonomyConfig.bg_review_max_steps(),
|
|
153
|
+
parent_llm=_review_llm,
|
|
154
|
+
skip_complexity_check=True,
|
|
155
|
+
)
|
|
156
|
+
except Exception as e:
|
|
157
|
+
logger.debug("background-review run failed (ignored): %s", e)
|