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/runtime_paths.py
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Location: core/runtime_paths.py
|
|
2
|
+
|
|
3
|
+
One resolver for all path roots — the single seam that separates installed
|
|
4
|
+
**code** (read-only), **config** (secrets), and the **runtime workspace** the
|
|
5
|
+
agent can write to.
|
|
6
|
+
|
|
7
|
+
The whole point (doc 01 "runtime isolation"): on the SERVER/headless path,
|
|
8
|
+
``realpath(workspace_root)`` must NOT live under ``realpath(code_root)`` (the
|
|
9
|
+
install/code tree that also holds ``config/.env.production`` secrets), so even a
|
|
10
|
+
confinement miss in a file tool cannot reach the agent's own source or the master
|
|
11
|
+
secrets. The CLI *local* mode is the single documented exception — it keeps
|
|
12
|
+
Claude-Code-style CWD-as-workspace (consented, single-user).
|
|
13
|
+
|
|
14
|
+
This module is intentionally dependency-light and lives in ``core/`` (NOT on the
|
|
15
|
+
action-registration import path), so it carries no ``from __future__ import
|
|
16
|
+
annotations`` landmine. ``POLYROB_DATA_DIR`` is read via ``os.getenv`` only —
|
|
17
|
+
never ``BotConfig.get`` (which is a getattr that silently ignores the env).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import os
|
|
21
|
+
from dataclasses import dataclass
|
|
22
|
+
from pathlib import Path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# code_root == the install/code root: the parent of this ``core/`` package.
|
|
26
|
+
# Same value as ``core/config.py``'s ``base_dir`` (dirname(dirname(__file__))).
|
|
27
|
+
_CODE_ROOT = Path(__file__).resolve().parent.parent
|
|
28
|
+
|
|
29
|
+
# FHS default for a systemd service's mutable state. Used on the server path when
|
|
30
|
+
# no explicit POLYROB_DATA_DIR is given and the location is writable/creatable.
|
|
31
|
+
_SERVER_DATA_HOME = Path("/var/lib/polyrob")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass(frozen=True)
|
|
35
|
+
class RuntimePaths:
|
|
36
|
+
"""The four resolved path roots. Frozen — resolve once, read everywhere."""
|
|
37
|
+
|
|
38
|
+
code_root: Path
|
|
39
|
+
config_dir: Path
|
|
40
|
+
data_home: Path
|
|
41
|
+
workspace_root: Path
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _server_default_data_home() -> Path:
|
|
45
|
+
"""Server data-home when POLYROB_DATA_DIR is unset.
|
|
46
|
+
|
|
47
|
+
Prefer FHS ``/var/lib/polyrob`` when its parent is writable (or it already
|
|
48
|
+
exists), else fall back to ``~/.polyrob`` so a non-root / locked-down service
|
|
49
|
+
user still gets a writable home OUTSIDE the code tree.
|
|
50
|
+
"""
|
|
51
|
+
try:
|
|
52
|
+
if _SERVER_DATA_HOME.exists():
|
|
53
|
+
return _SERVER_DATA_HOME
|
|
54
|
+
parent = _SERVER_DATA_HOME.parent
|
|
55
|
+
if parent.exists() and os.access(str(parent), os.W_OK):
|
|
56
|
+
return _SERVER_DATA_HOME
|
|
57
|
+
except Exception:
|
|
58
|
+
pass
|
|
59
|
+
return Path.home() / ".polyrob"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def resolve_runtime_paths(*, local: bool) -> RuntimePaths:
|
|
63
|
+
"""Resolve (code_root, config_dir, data_home, workspace_root).
|
|
64
|
+
|
|
65
|
+
Precedence (LOCKED — doc 01 T1):
|
|
66
|
+
- code_root = the install/code root (parent of ``core/``).
|
|
67
|
+
- data_home = ``POLYROB_DATA_DIR`` if set; elif local → ``cwd/.polyrob``;
|
|
68
|
+
else server → ``/var/lib/polyrob`` (if writable) else
|
|
69
|
+
``~/.polyrob``.
|
|
70
|
+
- config_dir = ``code_root/config`` (server) or ``data_home`` (local).
|
|
71
|
+
- workspace_root= ``cwd`` (local, project-root mode) or ``data_home/task``.
|
|
72
|
+
"""
|
|
73
|
+
code_root = _CODE_ROOT
|
|
74
|
+
|
|
75
|
+
env_data_dir = os.getenv("POLYROB_DATA_DIR")
|
|
76
|
+
if env_data_dir:
|
|
77
|
+
data_home = Path(env_data_dir).resolve()
|
|
78
|
+
elif local:
|
|
79
|
+
# Local home is the project-scoped ``.polyrob`` dir (doc 02 rename).
|
|
80
|
+
data_home = (Path.cwd() / ".polyrob").resolve()
|
|
81
|
+
else:
|
|
82
|
+
data_home = _server_default_data_home().resolve()
|
|
83
|
+
|
|
84
|
+
if local:
|
|
85
|
+
config_dir = data_home
|
|
86
|
+
workspace_root = Path.cwd().resolve()
|
|
87
|
+
else:
|
|
88
|
+
config_dir = code_root / "config"
|
|
89
|
+
workspace_root = data_home / "task"
|
|
90
|
+
|
|
91
|
+
return RuntimePaths(
|
|
92
|
+
code_root=code_root,
|
|
93
|
+
config_dir=config_dir,
|
|
94
|
+
data_home=data_home,
|
|
95
|
+
workspace_root=workspace_root,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# The PathManager's own legacy default (agents/task/path.py reads DATA_ROOT,
|
|
100
|
+
# NOT POLYROB_DATA_DIR — the landmine at the heart of RC-1, 2026-07-07).
|
|
101
|
+
_LEGACY_SESSIONS_DEFAULT = "./data/task"
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def resolve_session_data_root() -> Path:
|
|
105
|
+
"""Resolve the session ARTIFACT tree root (PathManager ``data_root``).
|
|
106
|
+
|
|
107
|
+
RC-1 (2026-07-07 webview full-control handoff): the agent process resolves
|
|
108
|
+
its session tree via ``build_cli_container`` (``POLYROB_DATA_DIR`` →
|
|
109
|
+
``{data_home}/sessions``) and installs it as the global ``pm()``; the
|
|
110
|
+
webview process never ran that bootstrap, so its ``pm()`` fell back to the
|
|
111
|
+
PathManager default (env ``DATA_ROOT`` → ``./data/task``) — a DIFFERENT
|
|
112
|
+
tree. Every non-agent process that reads session artifacts must resolve
|
|
113
|
+
the root through THIS function so the trees cannot diverge again.
|
|
114
|
+
|
|
115
|
+
Resolution order:
|
|
116
|
+
1. Explicit ``DATA_ROOT`` — the PathManager's own env — always wins (an
|
|
117
|
+
operator who set it meant it; existing DATA_ROOT test rigs unchanged).
|
|
118
|
+
2. ``POLYROB_DATA_DIR`` set (headless/server data home) →
|
|
119
|
+
``{POLYROB_DATA_DIR}/sessions`` — exactly what ``build_cli_container``
|
|
120
|
+
produces for the agent process (parity pinned by test).
|
|
121
|
+
3. Neither set → the legacy ``./data/task``, byte-identical to a bare
|
|
122
|
+
``PathManager()``.
|
|
123
|
+
|
|
124
|
+
The local-dev CLI branches (``POLYROB_DATA_DIR`` unset → ``cwd/.polyrob/
|
|
125
|
+
sessions``) are deliberately NOT mirrored: this function runs in OTHER
|
|
126
|
+
processes whose cwd is not the CLI's, so guessing would be wrong more
|
|
127
|
+
often than the byte-identical legacy default.
|
|
128
|
+
"""
|
|
129
|
+
explicit = os.getenv("DATA_ROOT")
|
|
130
|
+
if explicit and explicit.strip():
|
|
131
|
+
return Path(explicit).resolve()
|
|
132
|
+
data_home = os.getenv("POLYROB_DATA_DIR")
|
|
133
|
+
if data_home and data_home.strip():
|
|
134
|
+
return Path(data_home).resolve() / "sessions"
|
|
135
|
+
return Path(_LEGACY_SESSIONS_DEFAULT).resolve()
|
core/seams.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Core ⇄ platform seam contracts.
|
|
2
|
+
|
|
3
|
+
This module is the single home for the dependency-inversion seams between polyrob-core
|
|
4
|
+
(the agent framework) and polyrob-platform (auth/billing/metering). Core declares the
|
|
5
|
+
Protocols + permissive defaults; platform injects real implementations.
|
|
6
|
+
|
|
7
|
+
This commit seeds ONLY the usage carrier type. The five Protocols (UsageRecorder,
|
|
8
|
+
SessionAdmissionPolicy, PaymentVerifier, plus the auth/admission defaults) land in the
|
|
9
|
+
seam-inversion plan. Keep this module free of any platform import (modules.credits/
|
|
10
|
+
payments/x402, api.* platform modules).
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from dataclasses import dataclass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class LLMUsage:
|
|
18
|
+
"""One LLM call's billable usage, carried across the UsageRecorder seam.
|
|
19
|
+
|
|
20
|
+
Field names mirror the kwargs the agent loop already passes to the platform usage
|
|
21
|
+
tracker (agents/task/agent/core/next_action_internal.py). The platform recorder maps
|
|
22
|
+
this onto modules.credits.usage_tracker.UsageRecord; core never imports that type.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
user_id: str
|
|
26
|
+
session_id: str
|
|
27
|
+
agent_id: str
|
|
28
|
+
model: str
|
|
29
|
+
provider: str
|
|
30
|
+
input_tokens: int
|
|
31
|
+
output_tokens: int
|
|
32
|
+
cached_tokens: int
|
|
33
|
+
duration_seconds: float
|
|
34
|
+
component: str
|
|
35
|
+
purpose: str
|
core/secret_scan.py
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""SEC-1 backstop: does a directory look like a secrets/code tree?
|
|
2
|
+
|
|
3
|
+
Used to WARN (or optionally refuse) when the agent's persistent workspace is the
|
|
4
|
+
cwd default AND that cwd contains secrets/VCS — the launch-in-a-repo footgun
|
|
5
|
+
(memory rob-glm-livetest-paramodel-bug-2026-06-20). This is a STEER, not a
|
|
6
|
+
read-denial guard; real per-read denial belongs at the filesystem/coding
|
|
7
|
+
chokepoint in a separate hardening PR (analysis §8.3).
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
from typing import List
|
|
11
|
+
|
|
12
|
+
_EXACT = {".env", ".env.production", ".env.development", ".git",
|
|
13
|
+
"id_rsa", "credentials.json"}
|
|
14
|
+
_SUFFIX = (".pem",)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def looks_like_secrets_tree(path: str) -> List[str]:
|
|
18
|
+
found: List[str] = []
|
|
19
|
+
try:
|
|
20
|
+
for name in os.listdir(path):
|
|
21
|
+
if name in _EXACT or name.endswith(_SUFFIX):
|
|
22
|
+
found.append(name)
|
|
23
|
+
except OSError:
|
|
24
|
+
return []
|
|
25
|
+
return sorted(found)
|
core/secret_scrub.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""core.secret_scrub — conservative secret-shape redaction for PERSISTED content.
|
|
2
|
+
|
|
3
|
+
POLYROB's message-history sensitive-data filter (``messages/filters.py``) was
|
|
4
|
+
allowlist-only: it redacted just the credentials explicitly registered in
|
|
5
|
+
``sensitive_data``. Any UNregistered secret that lands in a tool result (a
|
|
6
|
+
``cat .env``, an MCP response echoing an auth header, a printed private key) was
|
|
7
|
+
written verbatim into ``message_history.json`` and the compaction checkpoints on
|
|
8
|
+
disk, in the clear.
|
|
9
|
+
|
|
10
|
+
This module adds a pattern backstop. Unlike the CLI's display-only scrubber
|
|
11
|
+
(``cli/ui/secrets.py``), this one runs over content that is FED BACK TO THE MODEL
|
|
12
|
+
and kept as working state, so it deliberately OMITS the aggressive
|
|
13
|
+
hex/base64 catch-alls: redacting every 32-char hex or 40-char base64 run would
|
|
14
|
+
corrupt legitimate working data (a git SHA the agent is comparing, a base64 blob
|
|
15
|
+
it is processing). Only HIGH-CONFIDENCE credential shapes are redacted here.
|
|
16
|
+
|
|
17
|
+
Pure (no I/O, no state). Defense-in-depth, NOT a security boundary — the real fix
|
|
18
|
+
for secret EXPOSURE is workspace path-confinement at the tool layer.
|
|
19
|
+
"""
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import re
|
|
23
|
+
from typing import Optional
|
|
24
|
+
|
|
25
|
+
#: Replacement marker. Matches the ``<secret>…</secret>`` convention the history
|
|
26
|
+
#: filter already uses for registered sensitive values, so the model sees one shape.
|
|
27
|
+
REDACTED = "<secret>redacted</secret>"
|
|
28
|
+
|
|
29
|
+
#: PEM private-key blocks (multi-line) — redact the whole block.
|
|
30
|
+
_PEM_RE = re.compile(
|
|
31
|
+
r"-----BEGIN [A-Z ]*PRIVATE KEY-----.*?-----END [A-Z ]*PRIVATE KEY-----",
|
|
32
|
+
re.DOTALL,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
#: ``Bearer <token>`` — run before the kv rule (kv value would stop at the space
|
|
36
|
+
#: and leave the token). 8+ token chars.
|
|
37
|
+
_BEARER_RE = re.compile(r"(?i)\bBearer\s+[A-Za-z0-9._\-]{8,}")
|
|
38
|
+
|
|
39
|
+
#: ``key = value`` / ``key: value`` where the key NAME signals a credential.
|
|
40
|
+
#: Key is preserved (line stays readable); only the value (>=6 chars) is redacted.
|
|
41
|
+
_KV_RE = re.compile(
|
|
42
|
+
r"(?i)\b(api[_-]?key|apikey|secret|client_secret|password|passwd|"
|
|
43
|
+
r"access[_-]?token|auth[_-]?token|token|authorization|bearer)"
|
|
44
|
+
r"(\s*[=:]\s*)"
|
|
45
|
+
r"(['\"]?)([^\s'\"]{6,})\3"
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
#: Provider-style opaque keys: ``sk-``/``pk-``/``rk-`` (OpenAI/Anthropic/Stripe),
|
|
49
|
+
#: ``rob_`` (POLYROB API keys), and AWS ``AKIA…`` access-key ids.
|
|
50
|
+
_PROVIDER_KEY_RE = re.compile(r"\b(?:sk|pk|rk)-[A-Za-z0-9_-]{16,}")
|
|
51
|
+
_ROB_KEY_RE = re.compile(r"\brob_[A-Za-z0-9]{16,}")
|
|
52
|
+
_AWS_RE = re.compile(r"\bAKIA[0-9A-Z]{16}\b")
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def scrub_secret_shapes(text: Optional[str]) -> str:
|
|
56
|
+
"""Redact high-confidence credential shapes from *text*.
|
|
57
|
+
|
|
58
|
+
Returns "" for None. No-op for content with no secret-shaped substrings, so it
|
|
59
|
+
is safe to apply to every persisted message's string content and tool args.
|
|
60
|
+
Conservative by design — see the module docstring on why hex/base64 blobs are
|
|
61
|
+
intentionally left intact.
|
|
62
|
+
"""
|
|
63
|
+
if not text:
|
|
64
|
+
return ""
|
|
65
|
+
out = _PEM_RE.sub(REDACTED, text)
|
|
66
|
+
out = _BEARER_RE.sub(REDACTED, out)
|
|
67
|
+
out = _KV_RE.sub(lambda m: f"{m.group(1)}{m.group(2)}{REDACTED}", out)
|
|
68
|
+
out = _PROVIDER_KEY_RE.sub(REDACTED, out)
|
|
69
|
+
out = _ROB_KEY_RE.sub(REDACTED, out)
|
|
70
|
+
out = _AWS_RE.sub(REDACTED, out)
|
|
71
|
+
return out
|
core/secrets.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""The single ENV-VAR-NAME predicate for DISPLAY REDACTION.
|
|
2
|
+
|
|
3
|
+
Reused by cli config show, core.config_service, and the webview config router.
|
|
4
|
+
|
|
5
|
+
NOTE: this is distinct from core/bootstrap._is_secret_key, which is a *backfill
|
|
6
|
+
selector* (a narrow allowlist of names we import from config/.env.production) —
|
|
7
|
+
a different question; do not conflate them.
|
|
8
|
+
|
|
9
|
+
Value-shape scrubbing of transcript text is yet another concern and lives in
|
|
10
|
+
cli/ui/secrets.py.
|
|
11
|
+
"""
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
_SECRET_HINTS = (
|
|
15
|
+
"KEY", "TOKEN", "SECRET", "PASSWORD", "PASSPHRASE", "PASS", "JWT",
|
|
16
|
+
"CREDENTIAL", "PRIVATE", "SIGNATURE", "SEED", "MNEMONIC",
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def is_secret_key(name: str) -> bool:
|
|
21
|
+
"""Return True if name appears to be a secret (for display redaction).
|
|
22
|
+
|
|
23
|
+
Checks if any of the secret substring hints appear case-insensitively in the name.
|
|
24
|
+
Used to decide whether to redact a config value in display output.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
name: The environment variable name to check.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
True if the name matches a secret hint, False otherwise.
|
|
31
|
+
"""
|
|
32
|
+
if not name:
|
|
33
|
+
return False
|
|
34
|
+
upper = name.upper()
|
|
35
|
+
return any(hint in upper for hint in _SECRET_HINTS)
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Security logging filter to prevent secrets from being logged.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import re
|
|
7
|
+
from typing import Set, Pattern
|
|
8
|
+
|
|
9
|
+
class SecretScrubbingFilter(logging.Filter):
|
|
10
|
+
"""Filter that scrubs sensitive information from log records."""
|
|
11
|
+
|
|
12
|
+
# Common secret patterns to match
|
|
13
|
+
SECRET_PATTERNS = [
|
|
14
|
+
# API keys - various formats
|
|
15
|
+
re.compile(r'(?i)api[_-]?key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9+/=]{20,})["\']?'),
|
|
16
|
+
re.compile(r'(?i)key["\']?\s*[:=]\s*["\']?([a-zA-Z0-9+/=]{20,})["\']?'),
|
|
17
|
+
re.compile(r'(?i)token["\']?\s*[:=]\s*["\']?([a-zA-Z0-9+/=_-]{20,})["\']?'),
|
|
18
|
+
re.compile(r'(?i)secret["\']?\s*[:=]\s*["\']?([a-zA-Z0-9+/=_-]{20,})["\']?'),
|
|
19
|
+
|
|
20
|
+
# Passwords
|
|
21
|
+
re.compile(r'(?i)password["\']?\s*[:=]\s*["\']?([^\s"\']{6,})["\']?'),
|
|
22
|
+
re.compile(r'(?i)passwd["\']?\s*[:=]\s*["\']?([^\s"\']{6,})["\']?'),
|
|
23
|
+
|
|
24
|
+
# Bearer tokens
|
|
25
|
+
re.compile(r'(?i)bearer\s+([a-zA-Z0-9+/=_-]{20,})'),
|
|
26
|
+
|
|
27
|
+
# Specific service patterns
|
|
28
|
+
re.compile(r'sk-[a-zA-Z0-9]{48}'), # OpenAI API keys
|
|
29
|
+
re.compile(r'claude-[a-zA-Z0-9_-]{20,}'), # Anthropic keys
|
|
30
|
+
re.compile(r'pc-[a-zA-Z0-9]{32}'), # Pinecone keys
|
|
31
|
+
|
|
32
|
+
# Generic base64-looking strings that might be keys
|
|
33
|
+
re.compile(r'([a-zA-Z0-9+/]{32,}={0,2})'),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
# Fields that should always be scrubbed if they contain potential secrets
|
|
37
|
+
SENSITIVE_FIELDS = {
|
|
38
|
+
'api_key', 'apikey', 'api-key',
|
|
39
|
+
'secret_key', 'secretkey', 'secret-key', 'secret',
|
|
40
|
+
'password', 'passwd', 'pass',
|
|
41
|
+
'token', 'access_token', 'refresh_token',
|
|
42
|
+
'authorization', 'auth',
|
|
43
|
+
'key', 'private_key', 'public_key',
|
|
44
|
+
'client_secret', 'client_id'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
def __init__(self, mask_length: int = 4, mask_char: str = '*'):
|
|
48
|
+
"""
|
|
49
|
+
Initialize the filter.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
mask_length: Number of characters to show at start/end
|
|
53
|
+
mask_char: Character to use for masking
|
|
54
|
+
"""
|
|
55
|
+
super().__init__()
|
|
56
|
+
self.mask_length = mask_length
|
|
57
|
+
self.mask_char = mask_char
|
|
58
|
+
|
|
59
|
+
def mask_secret(self, secret: str) -> str:
|
|
60
|
+
"""Mask a secret string, showing only first/last few characters."""
|
|
61
|
+
if len(secret) <= self.mask_length * 2:
|
|
62
|
+
return self.mask_char * len(secret)
|
|
63
|
+
|
|
64
|
+
start = secret[:self.mask_length]
|
|
65
|
+
end = secret[-self.mask_length:]
|
|
66
|
+
middle_length = len(secret) - (self.mask_length * 2)
|
|
67
|
+
return f"{start}{self.mask_char * min(middle_length, 10)}{end}"
|
|
68
|
+
|
|
69
|
+
def scrub_message(self, message: str) -> str:
|
|
70
|
+
"""Scrub secrets from a message string."""
|
|
71
|
+
scrubbed = message
|
|
72
|
+
|
|
73
|
+
# Apply all secret patterns
|
|
74
|
+
for pattern in self.SECRET_PATTERNS:
|
|
75
|
+
def replace_secret(match):
|
|
76
|
+
full_match = match.group(0)
|
|
77
|
+
if len(match.groups()) > 0:
|
|
78
|
+
secret = match.group(1)
|
|
79
|
+
masked_secret = self.mask_secret(secret)
|
|
80
|
+
return full_match.replace(secret, masked_secret)
|
|
81
|
+
return self.mask_secret(full_match)
|
|
82
|
+
|
|
83
|
+
scrubbed = pattern.sub(replace_secret, scrubbed)
|
|
84
|
+
|
|
85
|
+
return scrubbed
|
|
86
|
+
|
|
87
|
+
def filter(self, record: logging.LogRecord) -> bool:
|
|
88
|
+
"""
|
|
89
|
+
Filter method called on each log record.
|
|
90
|
+
|
|
91
|
+
Returns:
|
|
92
|
+
True to allow the record through, False to drop it
|
|
93
|
+
"""
|
|
94
|
+
# Scrub the main message
|
|
95
|
+
if hasattr(record, 'msg') and record.msg:
|
|
96
|
+
if isinstance(record.msg, str):
|
|
97
|
+
record.msg = self.scrub_message(record.msg)
|
|
98
|
+
|
|
99
|
+
# Scrub formatted message
|
|
100
|
+
if hasattr(record, 'getMessage'):
|
|
101
|
+
try:
|
|
102
|
+
original_get_message = record.getMessage
|
|
103
|
+
|
|
104
|
+
def scrubbed_get_message():
|
|
105
|
+
msg = original_get_message()
|
|
106
|
+
return self.scrub_message(msg) if isinstance(msg, str) else msg
|
|
107
|
+
|
|
108
|
+
record.getMessage = scrubbed_get_message
|
|
109
|
+
except Exception:
|
|
110
|
+
pass
|
|
111
|
+
|
|
112
|
+
# Scrub any dictionary-like extra data
|
|
113
|
+
if hasattr(record, '__dict__'):
|
|
114
|
+
for key, value in record.__dict__.items():
|
|
115
|
+
if isinstance(value, str) and (
|
|
116
|
+
key.lower() in self.SENSITIVE_FIELDS or
|
|
117
|
+
any(field in key.lower() for field in self.SENSITIVE_FIELDS)
|
|
118
|
+
):
|
|
119
|
+
record.__dict__[key] = self.mask_secret(value)
|
|
120
|
+
|
|
121
|
+
return True
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def install_secret_scrubbing_filter(logger_name: str = None):
|
|
125
|
+
"""
|
|
126
|
+
Install the secret scrubbing filter on a logger.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
logger_name: Name of logger to install on, None for root logger
|
|
130
|
+
"""
|
|
131
|
+
logger = logging.getLogger(logger_name)
|
|
132
|
+
|
|
133
|
+
# Check if filter is already installed
|
|
134
|
+
for filter_obj in logger.filters:
|
|
135
|
+
if isinstance(filter_obj, SecretScrubbingFilter):
|
|
136
|
+
return
|
|
137
|
+
|
|
138
|
+
filter_obj = SecretScrubbingFilter()
|
|
139
|
+
logger.addFilter(filter_obj)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def install_global_secret_scrubbing():
|
|
143
|
+
"""Install secret scrubbing on the root logger to catch all logs."""
|
|
144
|
+
install_secret_scrubbing_filter()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
# Auto-install on import
|
|
148
|
+
install_global_secret_scrubbing()
|