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
modules/pfp/renderer.py
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Headless still renderer — runs the EXACT ``avatar/mindprint.js`` engine in
|
|
2
|
+
Chromium (Playwright) and writes a deterministic PNG. Python only orchestrates;
|
|
3
|
+
every pixel comes from the committed JS engine.
|
|
4
|
+
|
|
5
|
+
Playwright is an OPTIONAL extra (``pip install '.[browser]' && playwright install
|
|
6
|
+
chromium``). When it (or Chromium) is absent, :func:`render_still` raises
|
|
7
|
+
:class:`PfpRenderUnavailable` so callers (``store.generate_pfp``) can fall back to the
|
|
8
|
+
committed reference PNG. The renderer is never on a runtime/interactive path.
|
|
9
|
+
|
|
10
|
+
Engine loading: the engine is a CLASSIC script (window globals, no ES-module export),
|
|
11
|
+
injected via ``set_content`` on ``about:blank`` — no ``file://`` / ESM / CSP concerns
|
|
12
|
+
and no local HTTP server.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import base64
|
|
17
|
+
from dataclasses import dataclass
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from typing import Any, Dict, Optional
|
|
20
|
+
|
|
21
|
+
from .config import render_seed, normalized_override
|
|
22
|
+
|
|
23
|
+
_AVATAR = Path(__file__).resolve().parents[2] / "avatar"
|
|
24
|
+
_ENGINE = _AVATAR / "mindprint.js"
|
|
25
|
+
|
|
26
|
+
# Renders a deterministic STILL and returns the PNG data-URL + derived meta.
|
|
27
|
+
_JS_RENDER = """(a) => {
|
|
28
|
+
const mp = new Mindprint(a.seed);
|
|
29
|
+
Object.assign(mp.override, a.override || {});
|
|
30
|
+
const cv = document.getElementById('c');
|
|
31
|
+
cv.width = cv.height = a.size;
|
|
32
|
+
mp.N = 0;
|
|
33
|
+
mp.render(cv.getContext('2d'), a.size, 1.0, 0, {still: true});
|
|
34
|
+
mp.N = 0;
|
|
35
|
+
return {dataURL: cv.toDataURL('image/png'), traits: mp.traitList(),
|
|
36
|
+
hex: mp.hex, voice: mp.voiceCfg()};
|
|
37
|
+
}"""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class PfpRenderUnavailable(RuntimeError):
|
|
41
|
+
"""Raised when Playwright/Chromium is not available to render a still."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class RenderResult:
|
|
46
|
+
path: Path
|
|
47
|
+
traits: Dict[str, Any]
|
|
48
|
+
seed_hex: str
|
|
49
|
+
voice: Dict[str, float]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def render_still(config: Dict[str, Any], out_png, *, size: Optional[int] = None) -> RenderResult:
|
|
53
|
+
"""Render ``config`` to ``out_png`` via headless Chromium. Raises
|
|
54
|
+
:class:`PfpRenderUnavailable` if Playwright/Chromium is missing."""
|
|
55
|
+
try:
|
|
56
|
+
from playwright.sync_api import sync_playwright, Error as PlaywrightError
|
|
57
|
+
except ImportError as e: # extra not installed
|
|
58
|
+
raise PfpRenderUnavailable("playwright not installed ('.[browser]')") from e
|
|
59
|
+
|
|
60
|
+
engine = _ENGINE.read_text(encoding="utf-8")
|
|
61
|
+
seed = render_seed(config)
|
|
62
|
+
override = normalized_override(config.get("override", {}) or {})
|
|
63
|
+
sz = int(size or config.get("size") or 768)
|
|
64
|
+
html = (
|
|
65
|
+
"<!doctype html><meta charset='utf-8'>"
|
|
66
|
+
"<canvas id='c'></canvas><script>" + engine + "</script>"
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
with sync_playwright() as pw:
|
|
71
|
+
browser = pw.chromium.launch(args=["--disable-gpu"])
|
|
72
|
+
try:
|
|
73
|
+
page = browser.new_page()
|
|
74
|
+
page.set_content(html, wait_until="load")
|
|
75
|
+
res = page.evaluate(_JS_RENDER, {"seed": seed, "override": override, "size": sz})
|
|
76
|
+
finally:
|
|
77
|
+
browser.close()
|
|
78
|
+
except PlaywrightError as e: # chromium binary missing / launch failure
|
|
79
|
+
raise PfpRenderUnavailable(f"chromium unavailable: {e}") from e
|
|
80
|
+
|
|
81
|
+
data = base64.b64decode(res["dataURL"].split(",", 1)[1])
|
|
82
|
+
out_png = Path(out_png)
|
|
83
|
+
out_png.parent.mkdir(parents=True, exist_ok=True)
|
|
84
|
+
out_png.write_bytes(data)
|
|
85
|
+
return RenderResult(path=out_png, traits=res["traits"], seed_hex=res["hex"], voice=res["voice"])
|
modules/pfp/store.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Persist the frozen avatar into the instance identity home.
|
|
2
|
+
|
|
3
|
+
``generate_pfp`` writes ``<home>/identity/{instance_id}/pfp/{pfp.png,pfp.json}``. It
|
|
4
|
+
is idempotent ("created once" — a no-op if the PNG exists unless ``force``), and it
|
|
5
|
+
falls back to a committed reference PNG when Chromium/Playwright is unavailable, so the
|
|
6
|
+
avatar always materializes even headless / in CI. Trait/voice/seed_hex meta is computed
|
|
7
|
+
by the pure-Python mesh (no browser), so the identity record is complete on either path.
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import shutil
|
|
13
|
+
from datetime import datetime, timezone
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Any, Dict, Optional
|
|
16
|
+
|
|
17
|
+
from core.instance import pfp_path, pfp_dir, load_pfp_meta, DEFAULT_INSTANCE_ID
|
|
18
|
+
from .config import load_frozen_config
|
|
19
|
+
from .mesh import Mesh
|
|
20
|
+
from .renderer import render_still, PfpRenderUnavailable
|
|
21
|
+
|
|
22
|
+
_AVATAR = Path(__file__).resolve().parents[2] / "avatar"
|
|
23
|
+
_DEFAULT_CONFIG = _AVATAR / "config" / "rob.json"
|
|
24
|
+
_DEFAULT_REFERENCE = _AVATAR / "renders" / "rob.png"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _now_iso() -> str:
|
|
28
|
+
return datetime.now(timezone.utc).replace(microsecond=0).isoformat()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def generate_pfp(
|
|
32
|
+
home_dir,
|
|
33
|
+
instance_id: str = DEFAULT_INSTANCE_ID,
|
|
34
|
+
*,
|
|
35
|
+
config: Optional[Dict[str, Any]] = None,
|
|
36
|
+
config_path=None,
|
|
37
|
+
force: bool = False,
|
|
38
|
+
reference_png=None,
|
|
39
|
+
size: Optional[int] = None,
|
|
40
|
+
) -> Dict[str, Any]:
|
|
41
|
+
"""Render + persist the instance avatar; return the ``pfp.json`` meta.
|
|
42
|
+
|
|
43
|
+
Idempotent unless ``force``. Raises :class:`PfpRenderUnavailable` only if BOTH the
|
|
44
|
+
headless render fails AND no reference PNG is available.
|
|
45
|
+
"""
|
|
46
|
+
cfg = config if config is not None else load_frozen_config(config_path or _DEFAULT_CONFIG)
|
|
47
|
+
|
|
48
|
+
target = pfp_path(home_dir, instance_id)
|
|
49
|
+
meta_path = pfp_dir(home_dir, instance_id) / "pfp.json"
|
|
50
|
+
|
|
51
|
+
if target.is_file() and not force:
|
|
52
|
+
existing = load_pfp_meta(home_dir, instance_id)
|
|
53
|
+
if existing is not None:
|
|
54
|
+
return existing # created once — no rewrite
|
|
55
|
+
|
|
56
|
+
mesh = Mesh(cfg) # traits/voice/seed_hex — pure Python, no browser
|
|
57
|
+
|
|
58
|
+
try:
|
|
59
|
+
render_still(cfg, target, size=size)
|
|
60
|
+
rendered_by = "playwright-chromium"
|
|
61
|
+
except PfpRenderUnavailable:
|
|
62
|
+
ref = Path(reference_png) if reference_png else _DEFAULT_REFERENCE
|
|
63
|
+
if ref.is_file():
|
|
64
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
65
|
+
shutil.copyfile(ref, target)
|
|
66
|
+
rendered_by = "committed-reference"
|
|
67
|
+
else:
|
|
68
|
+
raise
|
|
69
|
+
|
|
70
|
+
meta: Dict[str, Any] = {
|
|
71
|
+
**cfg,
|
|
72
|
+
"instance_id": instance_id,
|
|
73
|
+
"seed_hex": mesh.hex,
|
|
74
|
+
"traits": mesh.traits(),
|
|
75
|
+
"voice": mesh.voice(),
|
|
76
|
+
"config_ref": "avatar/config/rob.json",
|
|
77
|
+
"rendered_by": rendered_by,
|
|
78
|
+
"created_at": _now_iso(),
|
|
79
|
+
}
|
|
80
|
+
meta_path.parent.mkdir(parents=True, exist_ok=True)
|
|
81
|
+
meta_path.write_text(json.dumps(meta, indent=2), encoding="utf-8")
|
|
82
|
+
return meta
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Skill provenance + usage metrics (W2-D)."""
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
"""Skill provenance + usage metrics (W2-D).
|
|
2
|
+
|
|
3
|
+
Answers the question the safety brief requires of writable skills: *do authored
|
|
4
|
+
skills actually get reused?* Two tiny tenant-scoped tables in
|
|
5
|
+
``<data_dir>/skill_usage.db`` (shared WAL+jitter helpers):
|
|
6
|
+
|
|
7
|
+
- ``skill_provenance`` — who authored a skill and when (``user``/``agent``/
|
|
8
|
+
``background_review``);
|
|
9
|
+
- ``skill_usage`` — per-(skill,user) load_count + last_used_at, bumped each time the
|
|
10
|
+
agent pulls a skill body via ``load_skill``.
|
|
11
|
+
|
|
12
|
+
Feeds W5 (curator: archive unused authored skills) and W7 (`/insights`: authored-skill
|
|
13
|
+
reuse %). Pure storage; fail-open at the call sites (a metrics write must never break
|
|
14
|
+
a skill load).
|
|
15
|
+
"""
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import time
|
|
20
|
+
import threading
|
|
21
|
+
from typing import Callable, Dict, List, Optional
|
|
22
|
+
|
|
23
|
+
from core.identity import is_anonymous
|
|
24
|
+
from core.sqlite_util import execute_retry, wal_connect
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class SkillUsageStore:
|
|
28
|
+
def __init__(self, db_path: str, *, clock: Callable[[], float] = time.time):
|
|
29
|
+
self.db_path = db_path
|
|
30
|
+
self._now = clock
|
|
31
|
+
parent = os.path.dirname(db_path)
|
|
32
|
+
if parent:
|
|
33
|
+
os.makedirs(parent, exist_ok=True)
|
|
34
|
+
self._init_schema()
|
|
35
|
+
|
|
36
|
+
def _init_schema(self) -> None:
|
|
37
|
+
conn = wal_connect(self.db_path)
|
|
38
|
+
try:
|
|
39
|
+
conn.executescript(
|
|
40
|
+
"""
|
|
41
|
+
CREATE TABLE IF NOT EXISTS skill_provenance (
|
|
42
|
+
skill_id TEXT NOT NULL,
|
|
43
|
+
user_id TEXT NOT NULL,
|
|
44
|
+
created_by TEXT NOT NULL,
|
|
45
|
+
created_at REAL NOT NULL,
|
|
46
|
+
PRIMARY KEY (skill_id, user_id)
|
|
47
|
+
);
|
|
48
|
+
CREATE TABLE IF NOT EXISTS skill_usage (
|
|
49
|
+
skill_id TEXT NOT NULL,
|
|
50
|
+
user_id TEXT NOT NULL,
|
|
51
|
+
load_count INTEGER NOT NULL DEFAULT 0,
|
|
52
|
+
last_used_at REAL,
|
|
53
|
+
PRIMARY KEY (skill_id, user_id)
|
|
54
|
+
);
|
|
55
|
+
CREATE TABLE IF NOT EXISTS curator_state (
|
|
56
|
+
key TEXT PRIMARY KEY,
|
|
57
|
+
value TEXT
|
|
58
|
+
);
|
|
59
|
+
CREATE TABLE IF NOT EXISTS skill_install_audit (
|
|
60
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
61
|
+
name TEXT NOT NULL,
|
|
62
|
+
user_id TEXT NOT NULL,
|
|
63
|
+
source TEXT NOT NULL,
|
|
64
|
+
resolved_sha TEXT,
|
|
65
|
+
approver TEXT NOT NULL,
|
|
66
|
+
ts REAL NOT NULL
|
|
67
|
+
);
|
|
68
|
+
"""
|
|
69
|
+
)
|
|
70
|
+
conn.commit()
|
|
71
|
+
finally:
|
|
72
|
+
conn.close()
|
|
73
|
+
|
|
74
|
+
# --- provenance ----------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
def record_provenance(self, skill_id: str, user_id: str, created_by: str) -> None:
|
|
77
|
+
if is_anonymous(user_id):
|
|
78
|
+
return
|
|
79
|
+
execute_retry(
|
|
80
|
+
self.db_path,
|
|
81
|
+
"""INSERT INTO skill_provenance (skill_id,user_id,created_by,created_at)
|
|
82
|
+
VALUES (?,?,?,?)
|
|
83
|
+
ON CONFLICT(skill_id,user_id) DO UPDATE SET created_by=excluded.created_by""",
|
|
84
|
+
(skill_id, user_id, created_by, self._now()),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
def get_provenance(self, skill_id: str, user_id: str) -> Optional[Dict]:
|
|
88
|
+
row = execute_retry(
|
|
89
|
+
self.db_path,
|
|
90
|
+
"SELECT * FROM skill_provenance WHERE skill_id=? AND user_id=?",
|
|
91
|
+
(skill_id, user_id), fetch="one",
|
|
92
|
+
)
|
|
93
|
+
return dict(row) if row else None
|
|
94
|
+
|
|
95
|
+
# --- usage ---------------------------------------------------------------
|
|
96
|
+
|
|
97
|
+
def bump_load(self, skill_id: str, user_id: str) -> None:
|
|
98
|
+
if is_anonymous(user_id):
|
|
99
|
+
return
|
|
100
|
+
execute_retry(
|
|
101
|
+
self.db_path,
|
|
102
|
+
"""INSERT INTO skill_usage (skill_id,user_id,load_count,last_used_at)
|
|
103
|
+
VALUES (?,?,1,?)
|
|
104
|
+
ON CONFLICT(skill_id,user_id)
|
|
105
|
+
DO UPDATE SET load_count=load_count+1, last_used_at=excluded.last_used_at""",
|
|
106
|
+
(skill_id, user_id, self._now()),
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
def get_usage(self, skill_id: str, user_id: str) -> Dict:
|
|
110
|
+
row = execute_retry(
|
|
111
|
+
self.db_path,
|
|
112
|
+
"SELECT * FROM skill_usage WHERE skill_id=? AND user_id=?",
|
|
113
|
+
(skill_id, user_id), fetch="one",
|
|
114
|
+
)
|
|
115
|
+
return dict(row) if row else {"skill_id": skill_id, "user_id": user_id,
|
|
116
|
+
"load_count": 0, "last_used_at": None}
|
|
117
|
+
|
|
118
|
+
def list_authored(self, user_id: Optional[str] = None,
|
|
119
|
+
created_by: Optional[List[str]] = None) -> List[Dict]:
|
|
120
|
+
"""Join provenance+usage for authored skills (curator/insights feed)."""
|
|
121
|
+
sql = ("SELECT p.skill_id, p.user_id, p.created_by, p.created_at, "
|
|
122
|
+
"COALESCE(u.load_count,0) AS load_count, u.last_used_at "
|
|
123
|
+
"FROM skill_provenance p LEFT JOIN skill_usage u "
|
|
124
|
+
"ON p.skill_id=u.skill_id AND p.user_id=u.user_id")
|
|
125
|
+
clauses, params = [], []
|
|
126
|
+
if user_id is not None:
|
|
127
|
+
clauses.append("p.user_id=?"); params.append(user_id)
|
|
128
|
+
if created_by:
|
|
129
|
+
clauses.append("p.created_by IN (%s)" % ",".join("?" * len(created_by)))
|
|
130
|
+
params.extend(created_by)
|
|
131
|
+
if clauses:
|
|
132
|
+
sql += " WHERE " + " AND ".join(clauses)
|
|
133
|
+
rows = execute_retry(self.db_path, sql, tuple(params), fetch="all") or []
|
|
134
|
+
return [dict(r) for r in rows]
|
|
135
|
+
|
|
136
|
+
def authored_reuse_summary(self, user_id: Optional[str] = None) -> Dict:
|
|
137
|
+
"""W7 /insights: did self-authored skills get reused?
|
|
138
|
+
|
|
139
|
+
Returns counts + reuse rate over agent/background-authored skills — the
|
|
140
|
+
measurement the writable-skills safety brief requires. Tenant-scoped when
|
|
141
|
+
user_id is given.
|
|
142
|
+
"""
|
|
143
|
+
rows = self.list_authored(user_id=user_id, created_by=["agent", "background_review"])
|
|
144
|
+
total = len(rows)
|
|
145
|
+
reused = sum(1 for r in rows if (r.get("load_count") or 0) > 0)
|
|
146
|
+
by_author: Dict[str, int] = {}
|
|
147
|
+
for r in rows:
|
|
148
|
+
by_author[r["created_by"]] = by_author.get(r["created_by"], 0) + 1
|
|
149
|
+
return {
|
|
150
|
+
"authored_total": total,
|
|
151
|
+
"authored_reused": reused,
|
|
152
|
+
"reuse_rate": (reused / total) if total else 0.0,
|
|
153
|
+
"by_author": by_author,
|
|
154
|
+
"top": sorted(
|
|
155
|
+
({"skill_id": r["skill_id"], "loads": r.get("load_count") or 0} for r in rows),
|
|
156
|
+
key=lambda d: d["loads"], reverse=True,
|
|
157
|
+
)[:10],
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
# --- curator state -------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
def get_state(self, key: str) -> Optional[str]:
|
|
163
|
+
row = execute_retry(self.db_path, "SELECT value FROM curator_state WHERE key=?",
|
|
164
|
+
(key,), fetch="one")
|
|
165
|
+
return row["value"] if row else None
|
|
166
|
+
|
|
167
|
+
def set_state(self, key: str, value: str) -> None:
|
|
168
|
+
execute_retry(
|
|
169
|
+
self.db_path,
|
|
170
|
+
"INSERT INTO curator_state (key,value) VALUES (?,?) "
|
|
171
|
+
"ON CONFLICT(key) DO UPDATE SET value=excluded.value",
|
|
172
|
+
(key, value),
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# --- install audit ---------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
def record_install(self, name: str, *, user_id: str, source: str,
|
|
178
|
+
resolved_sha: Optional[str] = None, approver: str,
|
|
179
|
+
ts: Optional[float] = None) -> None:
|
|
180
|
+
"""Append-only audit trail for `polyrob skill install`/`approve`: what
|
|
181
|
+
skill, whose tenant, where it came from (source + resolved commit SHA
|
|
182
|
+
when known), who approved it, and when. Never updates/dedupes — a
|
|
183
|
+
re-install/re-approve of the same name is a NEW event worth keeping."""
|
|
184
|
+
ts = self._now() if ts is None else ts
|
|
185
|
+
execute_retry(
|
|
186
|
+
self.db_path,
|
|
187
|
+
"""INSERT INTO skill_install_audit
|
|
188
|
+
(name,user_id,source,resolved_sha,approver,ts)
|
|
189
|
+
VALUES (?,?,?,?,?,?)""",
|
|
190
|
+
(name, user_id, source, resolved_sha, approver, ts),
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def list_installs(self, user_id: Optional[str] = None) -> List[Dict]:
|
|
194
|
+
sql = "SELECT name,user_id,source,resolved_sha,approver,ts FROM skill_install_audit"
|
|
195
|
+
params: tuple = ()
|
|
196
|
+
if user_id is not None:
|
|
197
|
+
sql += " WHERE user_id=?"
|
|
198
|
+
params = (user_id,)
|
|
199
|
+
sql += " ORDER BY ts DESC"
|
|
200
|
+
rows = execute_retry(self.db_path, sql, params, fetch="all") or []
|
|
201
|
+
return [dict(r) for r in rows]
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# --- process-wide singleton (shares one db with the curator) -----------------
|
|
205
|
+
|
|
206
|
+
_STORE_LOCK = threading.Lock()
|
|
207
|
+
_STORE: Optional[SkillUsageStore] = None
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def _default_data_dir() -> str:
|
|
211
|
+
"""Stable ABSOLUTE data dir so the singleton resolves the same file regardless of
|
|
212
|
+
cwd. Otherwise the relative ``"data"`` default (used by the load_skill/provenance
|
|
213
|
+
call sites) could resolve to a different directory than the curator's
|
|
214
|
+
``config.data_dir`` (absolute), silently splitting reuse counts from the curator
|
|
215
|
+
and /insights. Anchored at the repo root (this file is modules/skills/skill_usage.py).
|
|
216
|
+
"""
|
|
217
|
+
env = os.getenv("POLYROB_DATA_DIR")
|
|
218
|
+
if env:
|
|
219
|
+
return os.path.abspath(env)
|
|
220
|
+
repo_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
221
|
+
return os.path.join(repo_root, "data")
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def get_skill_usage_store(data_dir: Optional[str] = None) -> SkillUsageStore:
|
|
225
|
+
global _STORE
|
|
226
|
+
with _STORE_LOCK:
|
|
227
|
+
if _STORE is None:
|
|
228
|
+
dd = os.path.abspath(data_dir) if data_dir else _default_data_dir()
|
|
229
|
+
_STORE = SkillUsageStore(os.path.join(dd, "skill_usage.db"))
|
|
230
|
+
elif data_dir:
|
|
231
|
+
want = os.path.join(os.path.abspath(data_dir), "skill_usage.db")
|
|
232
|
+
if want != _STORE.db_path:
|
|
233
|
+
# First caller's path wins (singleton). Warn loudly if a later caller
|
|
234
|
+
# (e.g. the curator) expected a different db — that would desync metrics.
|
|
235
|
+
import logging
|
|
236
|
+
logging.getLogger(__name__).warning(
|
|
237
|
+
"skill_usage store already bound to %s; ignoring requested %s "
|
|
238
|
+
"(metrics/curator may read a different file than intended)",
|
|
239
|
+
_STORE.db_path, want,
|
|
240
|
+
)
|
|
241
|
+
return _STORE
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def reset_skill_usage_store() -> None:
|
|
245
|
+
"""Test seam."""
|
|
246
|
+
global _STORE
|
|
247
|
+
with _STORE_LOCK:
|
|
248
|
+
_STORE = None
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Surface-agnostic speech-to-text (#9).
|
|
2
|
+
|
|
3
|
+
The engine lives here, NOT in any surface, so every chat surface (Telegram today,
|
|
4
|
+
WhatsApp/etc. tomorrow) downloads its own audio and hands raw bytes to one shared
|
|
5
|
+
``Transcriber``. Keep transport-specific extraction in the surface; keep model/runtime
|
|
6
|
+
concerns here.
|
|
7
|
+
|
|
8
|
+
``build_transcriber()`` is the factory: a real faster-whisper transcriber when the
|
|
9
|
+
extra is installed, else a ``NullTranscriber`` (returns "") so a misconfigured deploy
|
|
10
|
+
degrades to "no transcript" instead of crashing the inbound spine.
|
|
11
|
+
"""
|
|
12
|
+
from .base import Transcriber, NullTranscriber
|
|
13
|
+
|
|
14
|
+
__all__ = ["Transcriber", "NullTranscriber", "build_transcriber"]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def build_transcriber(model_size: str = "base") -> Transcriber:
|
|
18
|
+
"""Return a ready Transcriber. Falls back to NullTranscriber if faster-whisper isn't
|
|
19
|
+
importable (the model itself loads lazily on first transcribe)."""
|
|
20
|
+
try:
|
|
21
|
+
from .faster_whisper_transcriber import FasterWhisperTranscriber
|
|
22
|
+
return FasterWhisperTranscriber(model_size=model_size)
|
|
23
|
+
except Exception: # import-time failure (extra missing) -> degrade, don't crash
|
|
24
|
+
return NullTranscriber()
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Transcriber ABC + a no-op fallback. Surface-agnostic: bytes in, text out."""
|
|
2
|
+
from abc import ABC, abstractmethod
|
|
3
|
+
from typing import Optional
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Transcriber(ABC):
|
|
7
|
+
@abstractmethod
|
|
8
|
+
async def transcribe(
|
|
9
|
+
self, audio: bytes, *, mime: Optional[str] = None, language: Optional[str] = None
|
|
10
|
+
) -> str:
|
|
11
|
+
"""Transcribe raw audio bytes to text. Returns "" when nothing is recognized.
|
|
12
|
+
Implementations MUST be fail-open (never raise into the caller's inbound path)."""
|
|
13
|
+
...
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class NullTranscriber(Transcriber):
|
|
17
|
+
"""Used when transcription is disabled or the faster-whisper extra is unavailable."""
|
|
18
|
+
|
|
19
|
+
async def transcribe(self, audio: bytes, *, mime=None, language=None) -> str:
|
|
20
|
+
return ""
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""faster-whisper transcriber. The model loads lazily on first use and inference runs
|
|
2
|
+
in a worker thread (faster-whisper is sync + CPU-bound), so a long transcription never
|
|
3
|
+
blocks the event loop. Fail-open: any error returns "" (no transcript)."""
|
|
4
|
+
import asyncio
|
|
5
|
+
import logging
|
|
6
|
+
import os
|
|
7
|
+
import tempfile
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from .base import Transcriber
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class FasterWhisperTranscriber(Transcriber):
|
|
16
|
+
def __init__(self, model_size: str = "base", device: str = "cpu",
|
|
17
|
+
compute_type: str = "int8") -> None:
|
|
18
|
+
self._model_size = model_size
|
|
19
|
+
self._device = device
|
|
20
|
+
self._compute_type = compute_type
|
|
21
|
+
self._model = None # lazily constructed on first transcribe
|
|
22
|
+
|
|
23
|
+
def _ensure_model(self):
|
|
24
|
+
if self._model is None:
|
|
25
|
+
from faster_whisper import WhisperModel # lazy: heavy import + optional extra
|
|
26
|
+
self._model = WhisperModel(
|
|
27
|
+
self._model_size, device=self._device, compute_type=self._compute_type
|
|
28
|
+
)
|
|
29
|
+
return self._model
|
|
30
|
+
|
|
31
|
+
async def transcribe(self, audio: bytes, *, mime: Optional[str] = None,
|
|
32
|
+
language: Optional[str] = None) -> str:
|
|
33
|
+
if not audio:
|
|
34
|
+
return ""
|
|
35
|
+
try:
|
|
36
|
+
return await asyncio.to_thread(self._transcribe_sync, audio, language)
|
|
37
|
+
except Exception as e: # fail-open: a transcription failure must not drop the msg
|
|
38
|
+
logger.warning("faster-whisper transcription failed: %s", e)
|
|
39
|
+
return ""
|
|
40
|
+
|
|
41
|
+
def _transcribe_sync(self, audio: bytes, language: Optional[str]) -> str:
|
|
42
|
+
model = self._ensure_model()
|
|
43
|
+
# faster-whisper reads from a path; Telegram voice is OGG/Opus, which ffmpeg
|
|
44
|
+
# (pulled in by faster-whisper) decodes by content, so the suffix is cosmetic.
|
|
45
|
+
with tempfile.NamedTemporaryFile(suffix=".ogg", delete=False) as f:
|
|
46
|
+
f.write(audio)
|
|
47
|
+
path = f.name
|
|
48
|
+
try:
|
|
49
|
+
segments, _info = model.transcribe(path, language=language)
|
|
50
|
+
return "".join(seg.text for seg in segments).strip()
|
|
51
|
+
finally:
|
|
52
|
+
try:
|
|
53
|
+
os.unlink(path)
|
|
54
|
+
except OSError:
|
|
55
|
+
pass
|