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
core/tool_catalog.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Product-facing tool catalog built from the descriptor source of truth."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import importlib.util
|
|
6
|
+
import os
|
|
7
|
+
from dataclasses import asdict, dataclass, field
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Any, Dict, List, Optional
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _load_descriptors_module():
|
|
13
|
+
"""Load tools/descriptors.py without importing tools/__init__.py."""
|
|
14
|
+
path = Path(__file__).resolve().parents[1] / "tools" / "descriptors.py"
|
|
15
|
+
spec = importlib.util.spec_from_file_location("_polyrob_tool_descriptors", path)
|
|
16
|
+
if spec is None or spec.loader is None:
|
|
17
|
+
raise RuntimeError(f"could not load tool descriptors from {path}")
|
|
18
|
+
module = importlib.util.module_from_spec(spec)
|
|
19
|
+
spec.loader.exec_module(module)
|
|
20
|
+
return module
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_DESCRIPTORS = _load_descriptors_module()
|
|
24
|
+
TOOL_DESCRIPTORS = _DESCRIPTORS.TOOL_DESCRIPTORS
|
|
25
|
+
ToolCategory = _DESCRIPTORS.ToolCategory
|
|
26
|
+
ToolDescriptor = _DESCRIPTORS.ToolDescriptor
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
_CATEGORY_LABELS = {
|
|
30
|
+
ToolCategory.CORE: "core",
|
|
31
|
+
ToolCategory.BROWSER: "automation",
|
|
32
|
+
ToolCategory.COMMUNICATION: "integration",
|
|
33
|
+
ToolCategory.SEARCH: "automation",
|
|
34
|
+
ToolCategory.VERIFICATION: "integration",
|
|
35
|
+
ToolCategory.INTEGRATION: "integration",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
_PERMISSIONS = {
|
|
39
|
+
"filesystem": ["fs.read", "fs.write"],
|
|
40
|
+
"task": ["memory.read", "memory.write"],
|
|
41
|
+
"browser_manager": ["browser.control", "network.read"],
|
|
42
|
+
"perplexity": ["network.read"],
|
|
43
|
+
"twitter": ["network.read", "network.write", "social.post"],
|
|
44
|
+
"email": ["network.write", "email.send"],
|
|
45
|
+
"collabland": ["network.read"],
|
|
46
|
+
"alchemy": ["network.read"],
|
|
47
|
+
"mcp": ["mcp.call", "network.read"],
|
|
48
|
+
"anysite": ["network.read", "process.spawn"],
|
|
49
|
+
"polymarket": ["network.read", "trade.execute"],
|
|
50
|
+
"hyperliquid": ["network.read", "trade.execute"],
|
|
51
|
+
# Read-only split tools: market data only, no wallet/signing.
|
|
52
|
+
"polymarket_data": ["network.read"],
|
|
53
|
+
"hyperliquid_data": ["network.read"],
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_HIGH_RISK_TOOLS = {"twitter", "email", "polymarket", "hyperliquid"}
|
|
57
|
+
_MEDIUM_RISK_TOOLS = {"mcp", "anysite", "browser_manager", "perplexity"}
|
|
58
|
+
_ENABLE_FLAGS = {
|
|
59
|
+
"twitter": "TWITTER_ENABLED",
|
|
60
|
+
}
|
|
61
|
+
_DB_CREDENTIAL_TOOLS = {
|
|
62
|
+
"polymarket": "requires database-stored trading credentials and explicit operator enablement",
|
|
63
|
+
"hyperliquid": "requires database-stored trading credentials and explicit operator enablement",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class ToolCatalogEntry:
|
|
69
|
+
"""Stable product metadata for one tool."""
|
|
70
|
+
|
|
71
|
+
id: str
|
|
72
|
+
display_name: str
|
|
73
|
+
category: str
|
|
74
|
+
model_description: str
|
|
75
|
+
human_description: str
|
|
76
|
+
permissions: List[str] = field(default_factory=list)
|
|
77
|
+
scope: str = "session"
|
|
78
|
+
enabled: bool = True
|
|
79
|
+
disabled_reason: Optional[str] = None
|
|
80
|
+
required_config: List[str] = field(default_factory=list)
|
|
81
|
+
required_services: List[str] = field(default_factory=list)
|
|
82
|
+
optional_services: List[str] = field(default_factory=list)
|
|
83
|
+
rate_limited: bool = False
|
|
84
|
+
rate_limits: Dict[str, Any] = field(default_factory=dict)
|
|
85
|
+
cost_risk: str = "low"
|
|
86
|
+
security_risk: str = "low"
|
|
87
|
+
audit_events: List[str] = field(default_factory=list)
|
|
88
|
+
|
|
89
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
90
|
+
return asdict(self)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def build_tool_catalog(env: Optional[Dict[str, str]] = None) -> List[ToolCatalogEntry]:
|
|
94
|
+
"""Return catalog entries sorted by category and id."""
|
|
95
|
+
env = os.environ if env is None else env
|
|
96
|
+
entries = [_entry_from_descriptor(name, desc, env) for name, desc in TOOL_DESCRIPTORS.items()]
|
|
97
|
+
return sorted(entries, key=lambda e: (e.category, e.id))
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def find_tool(tool_id: str, env: Optional[Dict[str, str]] = None) -> Optional[ToolCatalogEntry]:
|
|
101
|
+
needle = tool_id.lower().strip()
|
|
102
|
+
for entry in build_tool_catalog(env=env):
|
|
103
|
+
if entry.id == needle:
|
|
104
|
+
return entry
|
|
105
|
+
return None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def permission_catalog() -> Dict[str, List[str]]:
|
|
109
|
+
"""Return permission class -> tool ids."""
|
|
110
|
+
result: Dict[str, List[str]] = {}
|
|
111
|
+
for entry in build_tool_catalog():
|
|
112
|
+
for permission in entry.permissions:
|
|
113
|
+
result.setdefault(permission, []).append(entry.id)
|
|
114
|
+
return {key: sorted(value) for key, value in sorted(result.items())}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _entry_from_descriptor(
|
|
118
|
+
name: str,
|
|
119
|
+
desc: ToolDescriptor,
|
|
120
|
+
env: Dict[str, str],
|
|
121
|
+
) -> ToolCatalogEntry:
|
|
122
|
+
required_config = list(desc.required_config)
|
|
123
|
+
missing = [key for key in required_config if not _config_present(key, env)]
|
|
124
|
+
gated_reason = _gated_reason(name, env)
|
|
125
|
+
enabled = not missing
|
|
126
|
+
disabled_reason = None
|
|
127
|
+
if missing:
|
|
128
|
+
disabled_reason = "missing config: " + ", ".join(missing)
|
|
129
|
+
if gated_reason:
|
|
130
|
+
enabled = False
|
|
131
|
+
disabled_reason = gated_reason
|
|
132
|
+
|
|
133
|
+
return ToolCatalogEntry(
|
|
134
|
+
id=name,
|
|
135
|
+
display_name=name.replace("_", " ").title(),
|
|
136
|
+
category=_CATEGORY_LABELS.get(desc.category, desc.category.value),
|
|
137
|
+
model_description=_model_description(desc.description),
|
|
138
|
+
human_description=desc.description,
|
|
139
|
+
permissions=list(_PERMISSIONS.get(name, [])),
|
|
140
|
+
enabled=enabled,
|
|
141
|
+
disabled_reason=disabled_reason,
|
|
142
|
+
required_config=required_config,
|
|
143
|
+
required_services=list(desc.required_services),
|
|
144
|
+
optional_services=list(desc.optional_services),
|
|
145
|
+
rate_limited=desc.rate_limited,
|
|
146
|
+
rate_limits=dict(desc.rate_limit_settings),
|
|
147
|
+
cost_risk=_risk_for(name),
|
|
148
|
+
security_risk=_risk_for(name),
|
|
149
|
+
audit_events=_audit_events(name),
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _config_present(key: str, env: Dict[str, str]) -> bool:
|
|
154
|
+
variants = {
|
|
155
|
+
key,
|
|
156
|
+
key.upper(),
|
|
157
|
+
key.lower(),
|
|
158
|
+
key.replace("_api_key", "_API_KEY").upper(),
|
|
159
|
+
}
|
|
160
|
+
return any(bool(env.get(variant)) for variant in variants)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def _model_description(description: str) -> str:
|
|
164
|
+
first_sentence = description.split(".", 1)[0].strip()
|
|
165
|
+
return first_sentence or description.strip()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _risk_for(name: str) -> str:
|
|
169
|
+
if name in _HIGH_RISK_TOOLS:
|
|
170
|
+
return "high"
|
|
171
|
+
if name in _MEDIUM_RISK_TOOLS:
|
|
172
|
+
return "medium"
|
|
173
|
+
return "low"
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _gated_reason(name: str, env: Dict[str, str]) -> Optional[str]:
|
|
177
|
+
flag = _ENABLE_FLAGS.get(name)
|
|
178
|
+
if flag and not _truthy(env.get(flag)):
|
|
179
|
+
return f"gated by {flag}=false"
|
|
180
|
+
return _DB_CREDENTIAL_TOOLS.get(name)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _truthy(value: Optional[str]) -> bool:
|
|
184
|
+
return str(value or "").strip().lower() in {"1", "true", "yes", "on"}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _audit_events(name: str) -> List[str]:
|
|
188
|
+
permissions = _PERMISSIONS.get(name, [])
|
|
189
|
+
events = ["tool.call"]
|
|
190
|
+
if any(p.endswith(".write") or p in {"social.post", "email.send", "trade.execute"} for p in permissions):
|
|
191
|
+
events.append("external.write")
|
|
192
|
+
return events
|
core/version.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Single source of truth for the project version at runtime.
|
|
2
|
+
|
|
3
|
+
Build-time SSOT is pyproject.toml. Resolution order at runtime:
|
|
4
|
+
|
|
5
|
+
1. The **source** ``pyproject.toml`` next to this checkout — authoritative when you
|
|
6
|
+
run ``polyrob`` from a source tree. This is FIRST on purpose: a stale editable
|
|
7
|
+
or wheel install (e.g. a venv still carrying ``polyrob 1.0.0`` while the source
|
|
8
|
+
is ``0.4.2``) must NOT shadow the version of the code you're actually running.
|
|
9
|
+
2. Installed package metadata — for a real ``pip install`` where there's no source
|
|
10
|
+
``pyproject.toml`` adjacent to this module (site-packages).
|
|
11
|
+
3. The literal fallback below (tests pin it to pyproject.toml so it can't drift).
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
from importlib.metadata import PackageNotFoundError, version as _pkg_version
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
# Dev-checkout fallback. MUST equal pyproject.toml [project].version.
|
|
20
|
+
_FALLBACK_VERSION = "0.5.0"
|
|
21
|
+
|
|
22
|
+
# Project names this module is willing to claim from an adjacent pyproject.toml, so
|
|
23
|
+
# a parent/monorepo pyproject can never mislabel the version.
|
|
24
|
+
_OWN_PROJECT_NAMES = {"polyrob", "polyrob-core", "rob"}
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _load_toml(text: str) -> Optional[dict]:
|
|
28
|
+
try:
|
|
29
|
+
import tomllib # py3.11+
|
|
30
|
+
except ModuleNotFoundError: # pragma: no cover - older interpreters
|
|
31
|
+
try:
|
|
32
|
+
import tomli as tomllib # type: ignore
|
|
33
|
+
except ModuleNotFoundError:
|
|
34
|
+
return None
|
|
35
|
+
try:
|
|
36
|
+
return tomllib.loads(text)
|
|
37
|
+
except Exception:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _source_pyproject_version() -> Optional[str]:
|
|
42
|
+
"""Version from the checkout's own ``pyproject.toml``, or None.
|
|
43
|
+
|
|
44
|
+
``core/version.py`` → repo root is ``parents[1]``. Only trusted when the file's
|
|
45
|
+
``[project].name`` is one of ours, so a wrong/parent pyproject is ignored. Fully
|
|
46
|
+
fail-open: any read/parse problem returns None and we fall through to metadata.
|
|
47
|
+
"""
|
|
48
|
+
try:
|
|
49
|
+
pyproject = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
|
50
|
+
if not pyproject.is_file():
|
|
51
|
+
return None
|
|
52
|
+
data = _load_toml(pyproject.read_text(encoding="utf-8"))
|
|
53
|
+
if not data:
|
|
54
|
+
return None
|
|
55
|
+
project = data.get("project", {}) or {}
|
|
56
|
+
name = str(project.get("name", "")).strip().lower()
|
|
57
|
+
ver = project.get("version")
|
|
58
|
+
if ver and name in _OWN_PROJECT_NAMES:
|
|
59
|
+
return str(ver)
|
|
60
|
+
except Exception:
|
|
61
|
+
return None
|
|
62
|
+
return None
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def get_version() -> str:
|
|
66
|
+
src = _source_pyproject_version()
|
|
67
|
+
if src:
|
|
68
|
+
return src
|
|
69
|
+
try:
|
|
70
|
+
return _pkg_version("polyrob")
|
|
71
|
+
except PackageNotFoundError:
|
|
72
|
+
return _FALLBACK_VERSION
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
__version__ = get_version()
|
core/wallet/__init__.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""AgentWallet: the agent's single, operator-funded personal wallet (core-tier).
|
|
2
|
+
|
|
3
|
+
Hub-and-spoke topology: one master seed → a treasury key + domain-separated
|
|
4
|
+
per-venue keys. Hyperliquid trades via its own delegated key (the venue's
|
|
5
|
+
built-in withdrawal firewall); x402 pays from its own small float.
|
|
6
|
+
"""
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
from typing import Callable, List, Optional
|
|
11
|
+
|
|
12
|
+
from core.wallet.config import WalletConfig
|
|
13
|
+
from core.wallet.policy import PolicyGate
|
|
14
|
+
from core.wallet.signer import LocalEoaSigner
|
|
15
|
+
|
|
16
|
+
VENUES = frozenset({"treasury", "x402", "polymarket", "hyperliquid"})
|
|
17
|
+
|
|
18
|
+
_PBKDF2_ITERS = 100_000
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AgentWallet:
|
|
22
|
+
def __init__(self, config: WalletConfig, audit_sink: Optional[List[dict]] = None,
|
|
23
|
+
on_record: Optional[Callable[[dict], None]] = None):
|
|
24
|
+
self._config = config
|
|
25
|
+
if config.enabled:
|
|
26
|
+
if not config.master_seed or len(config.master_seed) < 32:
|
|
27
|
+
raise ValueError("AGENT_WALLET_MASTER_SEED must be set and >=32 chars when enabled")
|
|
28
|
+
self._seed = config.master_seed or ""
|
|
29
|
+
self._signers: dict[str, LocalEoaSigner] = {}
|
|
30
|
+
self._policy = PolicyGate(
|
|
31
|
+
max_per_tx_usd=config.max_per_tx_usd,
|
|
32
|
+
audit_sink=audit_sink,
|
|
33
|
+
daily_cap_usd=getattr(config, "daily_cap_usd", None),
|
|
34
|
+
per_venue_daily_cap_usd=getattr(config, "per_venue_daily_cap_usd", None),
|
|
35
|
+
on_record=on_record,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
def _derive_key(self, venue: str) -> bytes:
|
|
39
|
+
# Domain-separated label keeps venue keys independent and recoverable.
|
|
40
|
+
label = f"agent-wallet:{venue}".encode("utf-8")
|
|
41
|
+
return hashlib.pbkdf2_hmac("sha256", self._seed.encode("utf-8"), label, _PBKDF2_ITERS, dklen=32)
|
|
42
|
+
|
|
43
|
+
def signer_for(self, venue: str) -> LocalEoaSigner:
|
|
44
|
+
if venue not in VENUES:
|
|
45
|
+
raise ValueError(f"unknown venue '{venue}' (expected one of {sorted(VENUES)})")
|
|
46
|
+
if venue not in self._signers:
|
|
47
|
+
self._signers[venue] = LocalEoaSigner(self._derive_key(venue))
|
|
48
|
+
return self._signers[venue]
|
|
49
|
+
|
|
50
|
+
def account_for(self, venue: str):
|
|
51
|
+
return self.signer_for(venue).account
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def address(self) -> str:
|
|
55
|
+
return self.signer_for("treasury").address
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def policy(self) -> PolicyGate:
|
|
59
|
+
return self._policy
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Durable, append-only audit sink for the wallet PolicyGate (G3).
|
|
2
|
+
|
|
3
|
+
An in-memory ``list`` of audit entries is lost on restart, so the harness can't sum
|
|
4
|
+
lifetime/rolling spend across a service restart — a mainnet prerequisite. This sink
|
|
5
|
+
is a ``list`` subclass that mirrors every appended entry to an append-only JSONL file
|
|
6
|
+
and reloads prior entries on construction, so a fresh ``PolicyGate(audit_sink=...)``
|
|
7
|
+
sees the full history. Default behavior is unchanged: PolicyGate uses a plain list
|
|
8
|
+
unless a sink is injected (only the factory does, when the wallet is enabled).
|
|
9
|
+
|
|
10
|
+
Fail-open: a file I/O error never blocks an action (the in-memory copy is always
|
|
11
|
+
authoritative for the live process); persistence is best-effort.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import logging
|
|
17
|
+
import os
|
|
18
|
+
from typing import List
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class JsonlAuditSink(list):
|
|
24
|
+
"""A list of audit dicts mirrored to an append-only JSONL file."""
|
|
25
|
+
|
|
26
|
+
def __init__(self, path: str):
|
|
27
|
+
super().__init__()
|
|
28
|
+
self._path = path
|
|
29
|
+
parent = os.path.dirname(path)
|
|
30
|
+
if parent:
|
|
31
|
+
os.makedirs(parent, exist_ok=True)
|
|
32
|
+
self._load()
|
|
33
|
+
|
|
34
|
+
def _load(self) -> None:
|
|
35
|
+
if not os.path.exists(self._path):
|
|
36
|
+
return
|
|
37
|
+
try:
|
|
38
|
+
with open(self._path, "r", encoding="utf-8") as fh:
|
|
39
|
+
for line in fh:
|
|
40
|
+
line = line.strip()
|
|
41
|
+
if not line:
|
|
42
|
+
continue
|
|
43
|
+
try:
|
|
44
|
+
list.append(self, json.loads(line)) # base append: no re-write
|
|
45
|
+
except json.JSONDecodeError:
|
|
46
|
+
continue # skip a corrupt line, keep the rest
|
|
47
|
+
except OSError as e:
|
|
48
|
+
logger.warning("wallet audit sink load failed (%s): %s", self._path, e)
|
|
49
|
+
|
|
50
|
+
def append(self, entry: dict) -> None: # type: ignore[override]
|
|
51
|
+
list.append(self, entry)
|
|
52
|
+
try:
|
|
53
|
+
with open(self._path, "a", encoding="utf-8") as fh:
|
|
54
|
+
fh.write(json.dumps(entry) + "\n")
|
|
55
|
+
except OSError as e:
|
|
56
|
+
logger.warning("wallet audit sink write failed (%s): %s", self._path, e)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def default_audit_sink(data_dir: str = "data") -> List[dict]:
|
|
60
|
+
"""The factory's default persistent sink at ``<data_dir>/wallet/audit.jsonl``."""
|
|
61
|
+
return JsonlAuditSink(os.path.join(data_dir, "wallet", "audit.jsonl"))
|
core/wallet/config.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Agent-wallet configuration (env-driven, default-safe)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from typing import Dict, Mapping, Optional
|
|
7
|
+
|
|
8
|
+
TESTNET_FACILITATOR_URL = "https://x402.org/facilitator"
|
|
9
|
+
|
|
10
|
+
_TRUE = {"1", "true", "yes", "on"}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def _b(env: Mapping[str, str], key: str, default: bool) -> bool:
|
|
14
|
+
raw = env.get(key)
|
|
15
|
+
return default if raw is None else raw.strip().lower() in _TRUE
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class WalletConfig:
|
|
20
|
+
enabled: bool
|
|
21
|
+
backend: str
|
|
22
|
+
master_seed: Optional[str]
|
|
23
|
+
network: str
|
|
24
|
+
max_per_tx_usd: float
|
|
25
|
+
x402_client_enabled: bool
|
|
26
|
+
x402_facilitator_url: str
|
|
27
|
+
daily_cap_usd: Optional[float] = None
|
|
28
|
+
per_venue_daily_cap_usd: Dict[str, float] = field(default_factory=dict)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _opt_float(env: Mapping[str, str], key: str) -> Optional[float]:
|
|
32
|
+
raw = env.get(key)
|
|
33
|
+
if raw is None or not str(raw).strip():
|
|
34
|
+
return None
|
|
35
|
+
try:
|
|
36
|
+
return float(raw)
|
|
37
|
+
except ValueError:
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _load_per_venue_caps(env: Mapping[str, str]) -> Dict[str, float]:
|
|
42
|
+
"""Parse WALLET_VENUE_DAILY_CAP_<VENUE>_USD env vars into {venue: cap}."""
|
|
43
|
+
prefix, suffix = "WALLET_VENUE_DAILY_CAP_", "_USD"
|
|
44
|
+
caps: Dict[str, float] = {}
|
|
45
|
+
for key in env:
|
|
46
|
+
if key.startswith(prefix) and key.endswith(suffix):
|
|
47
|
+
venue = key[len(prefix):-len(suffix)].lower()
|
|
48
|
+
val = _opt_float(env, key)
|
|
49
|
+
if venue and val is not None:
|
|
50
|
+
caps[venue] = val
|
|
51
|
+
return caps
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_wallet_config(env: Optional[Mapping[str, str]] = None) -> WalletConfig:
|
|
55
|
+
env = os.environ if env is None else env
|
|
56
|
+
network = env.get("AGENT_WALLET_NETWORK", "testnet").strip().lower()
|
|
57
|
+
return WalletConfig(
|
|
58
|
+
enabled=_b(env, "AGENT_WALLET_ENABLED", False),
|
|
59
|
+
backend=env.get("AGENT_WALLET_BACKEND", "local_eoa").strip().lower(),
|
|
60
|
+
master_seed=env.get("AGENT_WALLET_MASTER_SEED"),
|
|
61
|
+
network=network if network in ("testnet", "mainnet") else "testnet",
|
|
62
|
+
# Safety default: a catastrophic per-tx ceiling, NOT a budget. Was
|
|
63
|
+
# $1,000,000 (a typo could drain funds); raise it explicitly if needed.
|
|
64
|
+
max_per_tx_usd=float(env.get("AGENT_WALLET_MAX_PER_TX_USD", "1000")),
|
|
65
|
+
x402_client_enabled=_b(env, "X402_CLIENT_ENABLED", False),
|
|
66
|
+
x402_facilitator_url=env.get("X402_CLIENT_FACILITATOR_URL", TESTNET_FACILITATOR_URL),
|
|
67
|
+
# Rolling 24h spend cap; unset = disabled = legacy behavior (per-tx ceiling only).
|
|
68
|
+
daily_cap_usd=_opt_float(env, "WALLET_DAILY_CAP_USD"),
|
|
69
|
+
per_venue_daily_cap_usd=_load_per_venue_caps(env),
|
|
70
|
+
)
|
core/wallet/factory.py
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Process-level AgentWallet singleton (the agent's wallet is single/operator-owned)."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from core.wallet.agent_wallet import AgentWallet
|
|
7
|
+
from core.wallet.config import load_wallet_config
|
|
8
|
+
from core.wallet.policy import PolicyGate
|
|
9
|
+
|
|
10
|
+
_cached: Optional[AgentWallet] = None
|
|
11
|
+
_resolved = False
|
|
12
|
+
_standalone_policy: Optional[PolicyGate] = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _emit_spend_to_event_log(entry: dict) -> None:
|
|
16
|
+
"""PolicyGate.on_record hook: mirror each recorded spend into the durable
|
|
17
|
+
telemetry event log so money movement survives a restart and is queryable
|
|
18
|
+
cross-session (telemetry audit 2026-07-04). LAZY import keeps the core/wallet
|
|
19
|
+
tier free of a top-level agents-tier dependency; fail-open throughout."""
|
|
20
|
+
try:
|
|
21
|
+
from agents.task.telemetry.event_log import get_event_log, event_log_enabled
|
|
22
|
+
if not event_log_enabled():
|
|
23
|
+
return
|
|
24
|
+
get_event_log().record(
|
|
25
|
+
"wallet_spend",
|
|
26
|
+
source="wallet",
|
|
27
|
+
venue=entry.get("venue"),
|
|
28
|
+
action=entry.get("action"),
|
|
29
|
+
amount_usd=entry.get("amount_usd"),
|
|
30
|
+
counterparty=entry.get("counterparty"),
|
|
31
|
+
result_ref=entry.get("result_ref"),
|
|
32
|
+
ts=entry.get("ts"),
|
|
33
|
+
)
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def get_agent_wallet() -> Optional[AgentWallet]:
|
|
39
|
+
global _cached, _resolved
|
|
40
|
+
if _resolved:
|
|
41
|
+
return _cached
|
|
42
|
+
cfg = load_wallet_config()
|
|
43
|
+
if cfg.enabled:
|
|
44
|
+
# Persist the audit trail so rolling-spend/lifetime tracking survives a
|
|
45
|
+
# restart (mainnet prerequisite). Fail-open: fall back to in-memory if the
|
|
46
|
+
# durable sink can't be created.
|
|
47
|
+
sink = None
|
|
48
|
+
try:
|
|
49
|
+
from core.wallet.audit_sink import default_audit_sink
|
|
50
|
+
sink = default_audit_sink()
|
|
51
|
+
except Exception:
|
|
52
|
+
sink = None
|
|
53
|
+
_cached = AgentWallet(cfg, audit_sink=sink, on_record=_emit_spend_to_event_log)
|
|
54
|
+
else:
|
|
55
|
+
_cached = None
|
|
56
|
+
_resolved = True
|
|
57
|
+
return _cached
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_policy_gate() -> PolicyGate:
|
|
61
|
+
"""The PolicyGate that guards value-moving actions (trades + payments).
|
|
62
|
+
|
|
63
|
+
Returns the agent wallet's gate when the wallet is enabled; otherwise a
|
|
64
|
+
standalone gate built from wallet config so the catastrophic per-tx ceiling
|
|
65
|
+
and daily/venue caps STILL apply to DB-credential trading (which has no
|
|
66
|
+
agent wallet). Cached so rolling-spend/replay state persists within a process.
|
|
67
|
+
"""
|
|
68
|
+
wallet = get_agent_wallet()
|
|
69
|
+
if wallet is not None:
|
|
70
|
+
return wallet.policy
|
|
71
|
+
global _standalone_policy
|
|
72
|
+
if _standalone_policy is None:
|
|
73
|
+
cfg = load_wallet_config()
|
|
74
|
+
_standalone_policy = PolicyGate(
|
|
75
|
+
max_per_tx_usd=cfg.max_per_tx_usd,
|
|
76
|
+
daily_cap_usd=cfg.daily_cap_usd,
|
|
77
|
+
per_venue_daily_cap_usd=cfg.per_venue_daily_cap_usd,
|
|
78
|
+
on_record=_emit_spend_to_event_log,
|
|
79
|
+
)
|
|
80
|
+
return _standalone_policy
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def reset_agent_wallet_cache() -> None:
|
|
84
|
+
global _cached, _resolved, _standalone_policy
|
|
85
|
+
_cached = None
|
|
86
|
+
_resolved = False
|
|
87
|
+
_standalone_policy = None
|
core/wallet/policy.py
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""PolicyGate: cheap, backend-independent hygiene for value-moving actions.
|
|
2
|
+
|
|
3
|
+
NOT a budget engine (the operator asked for 'no limits now'); it enforces ONE
|
|
4
|
+
catastrophic per-tx ceiling, idempotency de-dup, and an append-only audit trail.
|
|
5
|
+
A future budget engine can extend `check`."""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Callable, List, Optional
|
|
11
|
+
|
|
12
|
+
_DAY_SECONDS = 86_400
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass(frozen=True)
|
|
16
|
+
class PolicyDecision:
|
|
17
|
+
allowed: bool
|
|
18
|
+
reason: Optional[str]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PolicyGate:
|
|
22
|
+
def __init__(self, max_per_tx_usd: float, audit_sink: Optional[List[dict]] = None,
|
|
23
|
+
daily_cap_usd: Optional[float] = None,
|
|
24
|
+
per_venue_daily_cap_usd: Optional[dict] = None,
|
|
25
|
+
clock: Callable[[], float] = time.time,
|
|
26
|
+
on_record: Optional[Callable[[dict], None]] = None):
|
|
27
|
+
self._ceiling = float(max_per_tx_usd)
|
|
28
|
+
# Telemetry hook (audit 2026-07-04): fired with each recorded spend entry so
|
|
29
|
+
# a durable sink can capture money movement. Fail-open — never break record().
|
|
30
|
+
self._on_record = on_record
|
|
31
|
+
self._audit: List[dict] = audit_sink if audit_sink is not None else []
|
|
32
|
+
self._daily_cap: Optional[float] = (
|
|
33
|
+
None if daily_cap_usd is None else float(daily_cap_usd)
|
|
34
|
+
)
|
|
35
|
+
# Per-venue rolling-24h caps so one venue can't drain the global budget.
|
|
36
|
+
self._per_venue_cap: dict = {
|
|
37
|
+
str(k).lower(): float(v)
|
|
38
|
+
for k, v in (per_venue_daily_cap_usd or {}).items()
|
|
39
|
+
}
|
|
40
|
+
self._now = clock
|
|
41
|
+
# Rebuild the replay-guard set from a pre-loaded (persistent) sink so
|
|
42
|
+
# idempotency survives a restart. No-op for the default empty list.
|
|
43
|
+
self._seen_idempotency: set[str] = {
|
|
44
|
+
e["idempotency_key"] for e in self._audit
|
|
45
|
+
if e.get("idempotency_key")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
def _rolling_24h_spend(self, venue: Optional[str] = None) -> float:
|
|
49
|
+
window_start = self._now() - _DAY_SECONDS
|
|
50
|
+
return sum(
|
|
51
|
+
float(e.get("amount_usd") or 0.0)
|
|
52
|
+
for e in self._audit
|
|
53
|
+
if float(e.get("ts") or 0.0) >= window_start
|
|
54
|
+
and (venue is None or e.get("venue") == venue)
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def check(self, *, venue: str, amount_usd: float, idempotency_key: Optional[str]) -> PolicyDecision:
|
|
58
|
+
if amount_usd > self._ceiling:
|
|
59
|
+
return PolicyDecision(False, f"amount ${amount_usd:.2f} exceeds catastrophic ceiling ${self._ceiling:.2f}")
|
|
60
|
+
if idempotency_key and idempotency_key in self._seen_idempotency:
|
|
61
|
+
return PolicyDecision(False, f"idempotency key '{idempotency_key}' already used (replay blocked)")
|
|
62
|
+
if self._daily_cap is not None:
|
|
63
|
+
spent = self._rolling_24h_spend()
|
|
64
|
+
if spent + amount_usd > self._daily_cap:
|
|
65
|
+
return PolicyDecision(
|
|
66
|
+
False,
|
|
67
|
+
f"daily spend cap ${self._daily_cap:.2f} would be exceeded "
|
|
68
|
+
f"(trailing-24h ${spent:.2f} + ${amount_usd:.2f})",
|
|
69
|
+
)
|
|
70
|
+
venue_cap = self._per_venue_cap.get(str(venue).lower())
|
|
71
|
+
if venue_cap is not None:
|
|
72
|
+
venue_spent = self._rolling_24h_spend(venue=venue)
|
|
73
|
+
if venue_spent + amount_usd > venue_cap:
|
|
74
|
+
return PolicyDecision(
|
|
75
|
+
False,
|
|
76
|
+
f"venue '{venue}' daily cap ${venue_cap:.2f} would be exceeded "
|
|
77
|
+
f"(trailing-24h ${venue_spent:.2f} + ${amount_usd:.2f})",
|
|
78
|
+
)
|
|
79
|
+
return PolicyDecision(True, None)
|
|
80
|
+
|
|
81
|
+
def record(self, *, venue: str, action: str, amount_usd: float,
|
|
82
|
+
counterparty: Optional[str], idempotency_key: Optional[str],
|
|
83
|
+
result_ref: Optional[str]) -> None:
|
|
84
|
+
if idempotency_key:
|
|
85
|
+
self._seen_idempotency.add(idempotency_key)
|
|
86
|
+
entry = {
|
|
87
|
+
"ts": self._now(),
|
|
88
|
+
"venue": venue,
|
|
89
|
+
"action": action,
|
|
90
|
+
"amount_usd": amount_usd,
|
|
91
|
+
"counterparty": counterparty,
|
|
92
|
+
"idempotency_key": idempotency_key,
|
|
93
|
+
"result_ref": result_ref,
|
|
94
|
+
}
|
|
95
|
+
self._audit.append(entry)
|
|
96
|
+
if self._on_record is not None:
|
|
97
|
+
try:
|
|
98
|
+
self._on_record(entry)
|
|
99
|
+
except Exception:
|
|
100
|
+
pass # fail-open: telemetry must never break a recorded spend
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def audit_log(self) -> List[dict]:
|
|
104
|
+
return list(self._audit)
|