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
webview/owner_auth.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"""Owner username/password login — Posture 1's net-new auth path (design doc §1).
|
|
2
|
+
|
|
3
|
+
Does NOT require standing up the full SIWE SaaS layer. Verifies against
|
|
4
|
+
POLYROB_OWNER_USERNAME + POLYROB_OWNER_PASSWORD_HASH (argon2, never plaintext)
|
|
5
|
+
and mints a session cookie on the SAME canonical request.state contract
|
|
6
|
+
Workstream C4 defines: user_id/tier/role/payment_method/authenticated. Owner
|
|
7
|
+
login sets role="owner", tier="admin", payment_method=None (no billing outside
|
|
8
|
+
Posture 2).
|
|
9
|
+
|
|
10
|
+
Reuses the existing wallet-JWT cookie infra byte-for-byte (same "auth_token"
|
|
11
|
+
cookie name/flags api/auth_endpoints.py:226-235 uses, same JWT_SECRET_KEY, same
|
|
12
|
+
HS256 algorithm) so webview/server.py's existing auth_middleware JWT-decode
|
|
13
|
+
branch (webview/server.py:598-641) needs ZERO changes to accept an owner
|
|
14
|
+
session — it already decodes "role"/"tier"/"user_id" generically. That decode
|
|
15
|
+
branch now writes request.state via api/auth_state.py::set_auth_state (the C4
|
|
16
|
+
SSOT) so an owner token converges on the exact same canonical contract this
|
|
17
|
+
module mints.
|
|
18
|
+
"""
|
|
19
|
+
import hmac
|
|
20
|
+
import os
|
|
21
|
+
from datetime import datetime, timedelta
|
|
22
|
+
|
|
23
|
+
import jwt as pyjwt
|
|
24
|
+
from argon2 import PasswordHasher
|
|
25
|
+
from fastapi import Response
|
|
26
|
+
|
|
27
|
+
from webview import webgate
|
|
28
|
+
|
|
29
|
+
_hasher = PasswordHasher()
|
|
30
|
+
|
|
31
|
+
# Precomputed ONCE at module load: a valid argon2 hash of an arbitrary
|
|
32
|
+
# constant. verify_owner_password() runs an argon2 verify against this on
|
|
33
|
+
# EVERY call where the username doesn't match, so a bad-username attempt
|
|
34
|
+
# costs the same wall-clock time as a bad-password attempt against a real
|
|
35
|
+
# user. Without this, a wrong username short-circuits before argon2 ever
|
|
36
|
+
# runs, and that near-instant-vs-tens-of-ms gap is a network-observable
|
|
37
|
+
# oracle that confirms the owner username (user enumeration).
|
|
38
|
+
_DUMMY_HASH = _hasher.hash("polyrob-owner-auth-constant-time-dummy")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def owner_credentials_configured() -> bool:
|
|
42
|
+
return bool(
|
|
43
|
+
os.environ.get("POLYROB_OWNER_USERNAME")
|
|
44
|
+
and os.environ.get("POLYROB_OWNER_PASSWORD_HASH")
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def verify_owner_password(username: str, password: str) -> bool:
|
|
49
|
+
"""Fail-closed: any misconfiguration or verify failure returns False, never raises.
|
|
50
|
+
|
|
51
|
+
Timing-safe by construction: every call — bad username, bad password,
|
|
52
|
+
unconfigured credentials, or a correct pair — performs exactly ONE
|
|
53
|
+
argon2 verify (against the real hash when the username matches, else a
|
|
54
|
+
precomputed dummy hash) before returning. There is no early fast-return,
|
|
55
|
+
so wall-clock time never leaks which field (username vs password) was
|
|
56
|
+
wrong, or whether owner auth is configured at all. The username
|
|
57
|
+
comparison itself uses `hmac.compare_digest` so it isn't a fast
|
|
58
|
+
string-compare oracle either.
|
|
59
|
+
"""
|
|
60
|
+
configured = owner_credentials_configured()
|
|
61
|
+
expected_username = os.environ.get("POLYROB_OWNER_USERNAME", "") if configured else ""
|
|
62
|
+
expected_hash = os.environ.get("POLYROB_OWNER_PASSWORD_HASH", "") if configured else ""
|
|
63
|
+
|
|
64
|
+
username_matches = configured and hmac.compare_digest(
|
|
65
|
+
username.encode("utf-8"), expected_username.encode("utf-8")
|
|
66
|
+
)
|
|
67
|
+
target_hash = expected_hash if username_matches else _DUMMY_HASH
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
verified = _hasher.verify(target_hash, password)
|
|
71
|
+
except Exception:
|
|
72
|
+
verified = False
|
|
73
|
+
|
|
74
|
+
return bool(verified and username_matches)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def issue_owner_session_cookie(response: Response) -> str:
|
|
78
|
+
"""Mint + set the owner session JWT. Returns the raw token."""
|
|
79
|
+
jwt_secret = os.environ.get("JWT_SECRET_KEY")
|
|
80
|
+
if not jwt_secret:
|
|
81
|
+
raise RuntimeError("JWT_SECRET_KEY not configured - cannot issue owner session")
|
|
82
|
+
|
|
83
|
+
expires_at = datetime.utcnow() + timedelta(days=7)
|
|
84
|
+
payload = {
|
|
85
|
+
"sub": webgate.local_owner_id(),
|
|
86
|
+
"user_id": webgate.local_owner_id(),
|
|
87
|
+
"tier": "admin",
|
|
88
|
+
"role": "owner",
|
|
89
|
+
"payment_method": None,
|
|
90
|
+
"iat": datetime.utcnow(),
|
|
91
|
+
"exp": expires_at,
|
|
92
|
+
}
|
|
93
|
+
token = pyjwt.encode(payload, jwt_secret, algorithm="HS256")
|
|
94
|
+
|
|
95
|
+
is_production = os.environ.get("ENVIRONMENT", "production") == "production"
|
|
96
|
+
response.set_cookie(
|
|
97
|
+
key="auth_token",
|
|
98
|
+
value=token,
|
|
99
|
+
max_age=7 * 24 * 60 * 60,
|
|
100
|
+
path="/",
|
|
101
|
+
secure=is_production,
|
|
102
|
+
httponly=True,
|
|
103
|
+
samesite="lax",
|
|
104
|
+
)
|
|
105
|
+
return token
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
__all__ = ["owner_credentials_configured", "verify_owner_password", "issue_owner_session_cookie"]
|
webview/pages.py
ADDED
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
"""Webgate v1 read-only pages — Memory · Autonomy · Identity · System.
|
|
2
|
+
|
|
3
|
+
These are the four single-user pages POLYROB lacked (doc 03 §4 P2). Each page is
|
|
4
|
+
one Jinja template + one JSON read endpoint here. Every endpoint **reuses the
|
|
5
|
+
existing service** — it never reimplements a second source of truth:
|
|
6
|
+
|
|
7
|
+
- Memory → the active ``MemoryProvider.search()`` (``modules/memory/*``).
|
|
8
|
+
- Autonomy → ``GoalBoard.list()`` (``agents/task/goals/board.py``) +
|
|
9
|
+
``CronService.list_jobs()`` (``cron/service.py``).
|
|
10
|
+
- Identity → ``core/instance.py`` SOUL/SELF (``load_self_context`` /
|
|
11
|
+
``load_self_doc``) — **read-only**, no web write path (editing stays
|
|
12
|
+
the owner-gated ``self_context_manage`` action).
|
|
13
|
+
- System → ``cli/commands/doctor.py::doctor_report`` (imported, not shelled out).
|
|
14
|
+
|
|
15
|
+
Web endpoints have no agent ``execution_context``, so they call the services
|
|
16
|
+
directly rather than the agent tool-action path — still the same SSOT, not a
|
|
17
|
+
rebuild. Everything is fail-open: a missing provider / disabled flag / read error
|
|
18
|
+
degrades to an empty result, never a 500.
|
|
19
|
+
|
|
20
|
+
Flags are read via the dedicated helpers / ``os.environ`` (never ``BotConfig.get``,
|
|
21
|
+
which is a ``getattr`` trap — AGENTS.md landmine). NO ``from __future__ import
|
|
22
|
+
annotations`` here (kept consistent with the registry-closure landmine elsewhere
|
|
23
|
+
and unnecessary in this module).
|
|
24
|
+
"""
|
|
25
|
+
import os
|
|
26
|
+
|
|
27
|
+
from pathlib import Path
|
|
28
|
+
|
|
29
|
+
from fastapi import APIRouter, HTTPException, Request
|
|
30
|
+
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse, Response
|
|
31
|
+
from fastapi.templating import Jinja2Templates
|
|
32
|
+
|
|
33
|
+
from webview import webgate
|
|
34
|
+
|
|
35
|
+
# Reused services / config — imported at module level so tests can monkeypatch
|
|
36
|
+
# them at the ``webview.pages`` seam (the proof of reuse).
|
|
37
|
+
from agents.task.constants import AutonomyConfig
|
|
38
|
+
from agents.task.goals.board import GoalBoard
|
|
39
|
+
from cli.commands.doctor import doctor_report, local_flag_on, resolve_memory_backend
|
|
40
|
+
from core.instance import (
|
|
41
|
+
load_pfp_meta,
|
|
42
|
+
load_self_context,
|
|
43
|
+
load_self_doc,
|
|
44
|
+
pfp_path,
|
|
45
|
+
resolve_instance_id,
|
|
46
|
+
)
|
|
47
|
+
from cron.jobs import CronJobStore
|
|
48
|
+
from cron.service import CronService
|
|
49
|
+
from core.version import get_version
|
|
50
|
+
from modules.credits.unified_ledger import build_ledger
|
|
51
|
+
|
|
52
|
+
router = APIRouter()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
# --- template resolution (same asset base as server.py) --------------------- #
|
|
56
|
+
|
|
57
|
+
def _templates() -> Jinja2Templates:
|
|
58
|
+
try:
|
|
59
|
+
from core.assets import webgate_asset_dir
|
|
60
|
+
templates_dir = webgate_asset_dir() / "templates"
|
|
61
|
+
except Exception: # fail-open to the repo checkout
|
|
62
|
+
from pathlib import Path
|
|
63
|
+
templates_dir = Path(__file__).resolve().parent / "templates"
|
|
64
|
+
return Jinja2Templates(directory=str(templates_dir))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
_TEMPLATES = _templates()
|
|
68
|
+
_TEMPLATES.env.globals["console_display_name"] = webgate.console_display_name
|
|
69
|
+
_TEMPLATES.env.globals["branding"] = webgate.branding_config
|
|
70
|
+
_TEMPLATES.env.globals["get_version"] = get_version
|
|
71
|
+
# Posture default for the layout's tenant-nav block (P0-3) — same global as
|
|
72
|
+
# server.py's env; the explicit `is_multitenant` context var still wins.
|
|
73
|
+
_TEMPLATES.env.globals["is_multitenant_posture"] = webgate.is_multitenant
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# --- shared helpers --------------------------------------------------------- #
|
|
77
|
+
|
|
78
|
+
def _data_dir() -> str:
|
|
79
|
+
"""Data home the running services use (``goals.db``/``cron.db``/``memory.db``).
|
|
80
|
+
|
|
81
|
+
Honors ``POLYROB_DATA_DIR`` (the same env the agents/autonomy runtime read);
|
|
82
|
+
defaults to ``data`` (the tool/runtime default).
|
|
83
|
+
"""
|
|
84
|
+
return os.environ.get("POLYROB_DATA_DIR", "data")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _cron_enabled() -> bool:
|
|
88
|
+
"""Whether the cron subsystem is on (lazy import to keep this module light)."""
|
|
89
|
+
try:
|
|
90
|
+
from tools.cronjob_tools import cron_enabled
|
|
91
|
+
return cron_enabled()
|
|
92
|
+
except Exception:
|
|
93
|
+
return False
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _effective_user_id(request: Request) -> str:
|
|
97
|
+
"""Tenant identity for the webgate-v1 pages (memory/goals/cron/identity).
|
|
98
|
+
|
|
99
|
+
Single-tenant postures (local/own_ops): ``webgate.local_owner_id()`` — there is
|
|
100
|
+
exactly one owner and no separate caller-identity concept.
|
|
101
|
+
Multitenant: the AUTHENTICATED caller's ``request.state.user_id`` (fixes
|
|
102
|
+
assessment gap 5 — this used to be unconditionally ``local_owner_id()``,
|
|
103
|
+
leaking the instance owner's memory/goals/cron/identity data to every
|
|
104
|
+
authenticated tenant).
|
|
105
|
+
|
|
106
|
+
Fails CLOSED, never open: if multitenant and ``request.state.user_id`` is
|
|
107
|
+
missing/None/empty, this raises ``HTTPException(403)`` rather than falling
|
|
108
|
+
back to ``local_owner_id()`` (the instance owner's data). ``auth_middleware``
|
|
109
|
+
already 401s unauthenticated requests in multitenant mode before this is
|
|
110
|
+
ever called, so the 403 here is defense-in-depth, not the expected path —
|
|
111
|
+
but it must never resolve to the owner's identity.
|
|
112
|
+
"""
|
|
113
|
+
if webgate.is_multitenant():
|
|
114
|
+
uid = getattr(request.state, "user_id", None)
|
|
115
|
+
if uid:
|
|
116
|
+
return uid
|
|
117
|
+
raise HTTPException(status_code=403, detail="Authenticated tenant identity required")
|
|
118
|
+
return webgate.local_owner_id()
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _memory_provider():
|
|
122
|
+
"""The active external MemoryProvider, or None — reuse the registry/factory.
|
|
123
|
+
|
|
124
|
+
Prefers a provider already registered by an agent in this process; otherwise
|
|
125
|
+
constructs the configured backend read-only via the same factory the agent
|
|
126
|
+
uses. Fail-open to None (Memory page then shows an empty state).
|
|
127
|
+
"""
|
|
128
|
+
try:
|
|
129
|
+
from modules.memory.registry import get_memory_registry
|
|
130
|
+
active = get_memory_registry().active()
|
|
131
|
+
if active is not None and getattr(active, "is_external", False):
|
|
132
|
+
return active
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
try:
|
|
136
|
+
from modules.memory.backend_factory import maybe_register_memory_backend
|
|
137
|
+
return maybe_register_memory_backend(data_dir=_data_dir())
|
|
138
|
+
except Exception:
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def _split_snippets(raw: str) -> list:
|
|
143
|
+
"""Turn the provider's newline-joined ``- {content}`` string into a JSON list."""
|
|
144
|
+
if not raw:
|
|
145
|
+
return []
|
|
146
|
+
out = []
|
|
147
|
+
for line in raw.splitlines():
|
|
148
|
+
line = line.strip()
|
|
149
|
+
if not line:
|
|
150
|
+
continue
|
|
151
|
+
if line.startswith("- "):
|
|
152
|
+
line = line[2:]
|
|
153
|
+
out.append(line)
|
|
154
|
+
return out
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _goal_dict(g) -> dict:
|
|
158
|
+
return {
|
|
159
|
+
"id": g.id,
|
|
160
|
+
"title": g.title,
|
|
161
|
+
"body": g.body,
|
|
162
|
+
"status": g.status,
|
|
163
|
+
"priority": g.priority,
|
|
164
|
+
"created_at": g.created_at,
|
|
165
|
+
"completed_at": g.completed_at,
|
|
166
|
+
"result": g.result,
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _cron_dict(j) -> dict:
|
|
171
|
+
return {
|
|
172
|
+
"id": j.id,
|
|
173
|
+
"task": j.task,
|
|
174
|
+
"schedule_spec": j.schedule_spec,
|
|
175
|
+
"next_run_at": j.next_run_at.isoformat() if j.next_run_at else None,
|
|
176
|
+
"one_shot": j.one_shot,
|
|
177
|
+
"enabled": j.enabled,
|
|
178
|
+
"status": j.status,
|
|
179
|
+
"last_run_at": j.last_run_at.isoformat() if getattr(j, "last_run_at", None) else None,
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def _version() -> str:
|
|
184
|
+
# get_version() is the SSOT (source pyproject wins over stale installed
|
|
185
|
+
# metadata). Reading importlib.metadata directly here re-introduced the stale-
|
|
186
|
+
# install bug (a venv pinned at an old 1.0.0 would show 1.0.0). An explicit
|
|
187
|
+
# WEBVIEW_VERSION override still wins for deployments that want to pin it.
|
|
188
|
+
return os.environ.get("WEBVIEW_VERSION") or get_version()
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _provider_model():
|
|
192
|
+
try:
|
|
193
|
+
from cli.config_store import resolve_provider_model
|
|
194
|
+
from modules.llm.profiles import PROFILES, providers_with_keys
|
|
195
|
+
present = set(providers_with_keys(dict(os.environ)))
|
|
196
|
+
keys = {p.env_key for p in PROFILES.values() if p.name in present}
|
|
197
|
+
return resolve_provider_model(None, None, available_keys=keys)
|
|
198
|
+
except Exception:
|
|
199
|
+
return (None, None)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _page_context(request: Request) -> dict:
|
|
203
|
+
return {
|
|
204
|
+
"request": request,
|
|
205
|
+
"is_multitenant": webgate.is_multitenant(),
|
|
206
|
+
"version": _version(),
|
|
207
|
+
"ws_url": os.environ.get("WEBVIEW_WS_URL", ""),
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# --- JSON read endpoints ---------------------------------------------------- #
|
|
212
|
+
|
|
213
|
+
@router.get("/api/webgate/memory")
|
|
214
|
+
async def api_memory(request: Request, q: str = "", limit: int = 10):
|
|
215
|
+
"""Browse + search recall over the active MemoryProvider (tenant-scoped).
|
|
216
|
+
|
|
217
|
+
``q`` set → discover; ``q`` empty → browse-recent. Fail-open to no items.
|
|
218
|
+
Returns ``{items, count, mode, limit}`` so the page can caption results
|
|
219
|
+
honestly ("showing the N most recent" vs "N matches"). Per-hit
|
|
220
|
+
timestamp/score provenance is NOT available from the provider's search
|
|
221
|
+
surface (the ``memories`` FTS5 store has no timestamp column — the
|
|
222
|
+
episodic-memory plan tracks that); the envelope carries what exists.
|
|
223
|
+
"""
|
|
224
|
+
mode = "search" if (q or "").strip() else "browse"
|
|
225
|
+
provider = _memory_provider()
|
|
226
|
+
if provider is None:
|
|
227
|
+
return JSONResponse({"items": [], "count": 0, "mode": mode, "limit": limit})
|
|
228
|
+
user_id = _effective_user_id(request) # raises 403 outside this try — must not be fail-open-swallowed
|
|
229
|
+
try:
|
|
230
|
+
raw = await provider.search(q or "", user_id=user_id, limit=limit)
|
|
231
|
+
except Exception:
|
|
232
|
+
return JSONResponse({"items": [], "count": 0, "mode": mode, "limit": limit})
|
|
233
|
+
items = _split_snippets(raw)
|
|
234
|
+
return JSONResponse({"items": items, "count": len(items), "mode": mode, "limit": limit})
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@router.get("/api/webgate/goals")
|
|
238
|
+
async def api_goals(request: Request):
|
|
239
|
+
"""Durable goal board for the effective tenant — reuse ``GoalBoard.list()``."""
|
|
240
|
+
if not AutonomyConfig.goals_enabled():
|
|
241
|
+
return JSONResponse({"enabled": False, "goals": []})
|
|
242
|
+
user_id = _effective_user_id(request) # raises 403 outside this try — must not be fail-open-swallowed
|
|
243
|
+
try:
|
|
244
|
+
board = GoalBoard(os.path.join(_data_dir(), "goals.db"))
|
|
245
|
+
goals = board.list(user_id=user_id)
|
|
246
|
+
return JSONResponse({"enabled": True, "goals": [_goal_dict(g) for g in goals]})
|
|
247
|
+
except Exception:
|
|
248
|
+
return JSONResponse({"enabled": True, "goals": []})
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@router.get("/api/webgate/cron")
|
|
252
|
+
async def api_cron(request: Request):
|
|
253
|
+
"""Cron job board for the effective tenant — reuse ``CronService.list_jobs()``."""
|
|
254
|
+
if not _cron_enabled():
|
|
255
|
+
return JSONResponse({"enabled": False, "jobs": []})
|
|
256
|
+
user_id = _effective_user_id(request) # raises 403 outside this try — must not be fail-open-swallowed
|
|
257
|
+
try:
|
|
258
|
+
service = CronService(CronJobStore(os.path.join(_data_dir(), "cron.db")))
|
|
259
|
+
jobs = service.list_jobs(user_id=user_id)
|
|
260
|
+
return JSONResponse({"enabled": True, "jobs": [_cron_dict(j) for j in jobs]})
|
|
261
|
+
except Exception:
|
|
262
|
+
return JSONResponse({"enabled": True, "jobs": []})
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@router.get("/api/webgate/identity")
|
|
266
|
+
async def api_identity(request: Request):
|
|
267
|
+
"""SOUL/SELF identity — reuse ``core.instance`` (READ-ONLY, no write path).
|
|
268
|
+
|
|
269
|
+
SOUL is deliberately NOT re-scoped (instance-wide by design,
|
|
270
|
+
``core/self_context_writer.py:25``); only the SELF-tier read
|
|
271
|
+
(``load_self_doc``) is per-tenant.
|
|
272
|
+
"""
|
|
273
|
+
home = _data_dir()
|
|
274
|
+
owner = _effective_user_id(request)
|
|
275
|
+
instance_id = resolve_instance_id()
|
|
276
|
+
try:
|
|
277
|
+
soul = load_self_context(home) or None
|
|
278
|
+
except Exception:
|
|
279
|
+
soul = None
|
|
280
|
+
try:
|
|
281
|
+
self_doc = load_self_doc(home, owner, instance_id) or None
|
|
282
|
+
except Exception:
|
|
283
|
+
self_doc = None
|
|
284
|
+
return JSONResponse({
|
|
285
|
+
"soul": soul,
|
|
286
|
+
"self": self_doc,
|
|
287
|
+
"instance_id": instance_id,
|
|
288
|
+
"owner": owner,
|
|
289
|
+
})
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
# --- avatar (pfp) — the instance's face rendered LIVE in the console ---------- #
|
|
293
|
+
# The engine is served same-origin so the console runs the EXACT avatar/mindprint.js
|
|
294
|
+
# on a canvas (animated), driven by the instance's frozen /pfp.json. All fail-open:
|
|
295
|
+
# no avatar yet -> 404 -> the identity page just shows a neutral fallback.
|
|
296
|
+
_AVATAR_DIR = Path(__file__).resolve().parents[1] / "avatar"
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _pfp_data_dir() -> str:
|
|
300
|
+
"""Data home to read the avatar from.
|
|
301
|
+
|
|
302
|
+
The avatar is WRITTEN by the CLI (`pfp generate`), whose data home defaults to
|
|
303
|
+
``cwd/.polyrob`` when ``POLYROB_DATA_DIR`` is unset (``core.bootstrap.
|
|
304
|
+
_resolve_cli_data_home``). Mirror that so generate↔serve agree in local dev; in
|
|
305
|
+
prod (``POLYROB_DATA_DIR`` set) this is identical to :func:`_data_dir`.
|
|
306
|
+
"""
|
|
307
|
+
env = os.environ.get("POLYROB_DATA_DIR")
|
|
308
|
+
if env:
|
|
309
|
+
return env
|
|
310
|
+
local = Path.cwd() / ".polyrob"
|
|
311
|
+
return str(local) if local.exists() else _data_dir()
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
@router.get("/pfp.json")
|
|
315
|
+
async def api_pfp_json():
|
|
316
|
+
"""The instance avatar identity blob (traits + engine-agnostic voice), or 404."""
|
|
317
|
+
meta = load_pfp_meta(_pfp_data_dir(), resolve_instance_id())
|
|
318
|
+
if not meta:
|
|
319
|
+
return JSONResponse({"detail": "no avatar"}, status_code=404)
|
|
320
|
+
return JSONResponse(meta)
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
@router.get("/pfp.png")
|
|
324
|
+
async def pfp_png():
|
|
325
|
+
"""The instance avatar still PNG (progressive fallback / OG image), or 404."""
|
|
326
|
+
p = pfp_path(_pfp_data_dir(), resolve_instance_id())
|
|
327
|
+
if not p.is_file():
|
|
328
|
+
return Response(status_code=404)
|
|
329
|
+
return FileResponse(str(p), media_type="image/png")
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
@router.get("/avatar/mindprint.js")
|
|
333
|
+
async def avatar_engine():
|
|
334
|
+
"""Serve the EXACT engine same-origin (classic script; sets window.Mindprint)."""
|
|
335
|
+
p = _AVATAR_DIR / "mindprint.js"
|
|
336
|
+
if not p.is_file():
|
|
337
|
+
return Response(status_code=404)
|
|
338
|
+
return FileResponse(str(p), media_type="application/javascript")
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@router.get("/avatar/avatar-live.js")
|
|
342
|
+
async def avatar_live():
|
|
343
|
+
"""Serve the read-only live embed (fetch /pfp.json -> animate the canvas)."""
|
|
344
|
+
p = _AVATAR_DIR / "webview" / "avatar-live.js"
|
|
345
|
+
if not p.is_file():
|
|
346
|
+
return Response(status_code=404)
|
|
347
|
+
return FileResponse(str(p), media_type="application/javascript")
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def _empty_ledger(user_id: str, days: int) -> dict:
|
|
351
|
+
"""The all-zero ledger shape — returned when the ledger read fails so the
|
|
352
|
+
Finance page shows zeros rather than a 500 (every ledger leg fail-opens too)."""
|
|
353
|
+
return {"user_id": user_id, "window_days": days, "llm_api_cost_usd": 0.0,
|
|
354
|
+
"credits_spent": 0.0, "llm_calls": 0, "wallet_spend_usd": 0.0,
|
|
355
|
+
"wallet_payments": 0, "earned_usd": 0.0, "settled_payments": 0,
|
|
356
|
+
"pending_invoices_usd": 0.0, "pending_invoices": 0,
|
|
357
|
+
"total_spend_usd": 0.0, "net_usd": 0.0}
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _ledger_caps() -> dict:
|
|
361
|
+
"""Display-only policy caps (NOT part of the ledger read model) — the operator
|
|
362
|
+
context for the earned/spent/pending numbers. Resolved from env, fail-open."""
|
|
363
|
+
def _f(name, default):
|
|
364
|
+
try:
|
|
365
|
+
v = os.environ.get(name)
|
|
366
|
+
return float(v) if v not in (None, "") else default
|
|
367
|
+
except (TypeError, ValueError):
|
|
368
|
+
return default
|
|
369
|
+
return {
|
|
370
|
+
"wallet_daily_cap_usd": _f("WALLET_DAILY_CAP_USD", None),
|
|
371
|
+
"invoice_max_usd": _f("X402_INVOICE_MAX_USD", 50.0),
|
|
372
|
+
"invoice_daily_max": _f("X402_INVOICE_DAILY_MAX", 10.0),
|
|
373
|
+
"autonomy_budget_usd": _f("AUTONOMY_BUDGET_USD", 10.0),
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
@router.get("/api/webgate/ledger")
|
|
378
|
+
async def api_ledger(request: Request, days: int = 7):
|
|
379
|
+
"""Unified financial ledger for the effective tenant — reuse ``build_ledger``.
|
|
380
|
+
|
|
381
|
+
earned / spent / pending / net over a trailing window. Read-only, tenant-
|
|
382
|
+
scoped (``_effective_user_id`` raises 403 outside the try — must not be
|
|
383
|
+
fail-open-swallowed). All-zero-tolerant: a ledger error degrades to zeros."""
|
|
384
|
+
days = max(1, min(int(days), 365))
|
|
385
|
+
user_id = _effective_user_id(request) # 403 in multitenant if no tenant identity
|
|
386
|
+
try:
|
|
387
|
+
ledger = await build_ledger(user_id, days=days)
|
|
388
|
+
except Exception:
|
|
389
|
+
ledger = _empty_ledger(user_id, days)
|
|
390
|
+
ledger["caps"] = _ledger_caps()
|
|
391
|
+
return JSONResponse(ledger)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
@router.get("/finance", response_class=HTMLResponse)
|
|
395
|
+
async def finance_page(request: Request):
|
|
396
|
+
return _TEMPLATES.TemplateResponse("finance.html", _page_context(request))
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
@router.get("/api/webgate/doctor")
|
|
400
|
+
async def api_doctor():
|
|
401
|
+
"""System health — reuse ``doctor_report`` (same checks as ``polyrob doctor``).
|
|
402
|
+
|
|
403
|
+
The webview is a SERVER process — nothing does the CLI's POLYROB_LOCAL
|
|
404
|
+
setdefault here — so both the checks and the header's ``memory_backend``
|
|
405
|
+
resolve with ``absent_means_on=False`` (matching what
|
|
406
|
+
``modules.memory.backend_factory`` actually does at runtime). One shared
|
|
407
|
+
resolution, so the page can never contradict itself again (P0-4).
|
|
408
|
+
"""
|
|
409
|
+
env = dict(os.environ)
|
|
410
|
+
try:
|
|
411
|
+
checks = doctor_report(env, local_absent_means_on=False)
|
|
412
|
+
except Exception:
|
|
413
|
+
checks = []
|
|
414
|
+
provider, model = _provider_model()
|
|
415
|
+
rob_local = local_flag_on(env, absent_means_on=False)
|
|
416
|
+
return JSONResponse({
|
|
417
|
+
"checks": checks,
|
|
418
|
+
"instance_id": resolve_instance_id(),
|
|
419
|
+
"version": _version(),
|
|
420
|
+
"provider": provider,
|
|
421
|
+
"model": model,
|
|
422
|
+
"memory_backend": resolve_memory_backend(env, rob_local),
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
# --- page routes (render the template; data fetched client-side via the API) - #
|
|
427
|
+
|
|
428
|
+
@router.get("/memory", response_class=HTMLResponse)
|
|
429
|
+
async def memory_page(request: Request):
|
|
430
|
+
return _TEMPLATES.TemplateResponse("memory.html", _page_context(request))
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
@router.get("/autonomy", response_class=HTMLResponse)
|
|
434
|
+
async def autonomy_page(request: Request):
|
|
435
|
+
return _TEMPLATES.TemplateResponse("autonomy.html", _page_context(request))
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
@router.get("/identity", response_class=HTMLResponse)
|
|
439
|
+
async def identity_page(request: Request):
|
|
440
|
+
return _TEMPLATES.TemplateResponse("identity.html", _page_context(request))
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
@router.get("/system", response_class=HTMLResponse)
|
|
444
|
+
async def system_page(request: Request):
|
|
445
|
+
return _TEMPLATES.TemplateResponse("system.html", _page_context(request))
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
__all__ = ["router"]
|