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
api/jwt_middleware.py
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""JWT authentication middleware."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import jwt
|
|
5
|
+
from typing import Callable
|
|
6
|
+
|
|
7
|
+
from fastapi import Request
|
|
8
|
+
from fastapi.responses import JSONResponse
|
|
9
|
+
from starlette.middleware.base import BaseHTTPMiddleware
|
|
10
|
+
from starlette.responses import Response
|
|
11
|
+
|
|
12
|
+
from api.auth_constants import is_admin
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class JWTAuthMiddleware(BaseHTTPMiddleware):
|
|
18
|
+
"""Decode JWT and populate request.state."""
|
|
19
|
+
|
|
20
|
+
PUBLIC_PATHS = {
|
|
21
|
+
"/", "/health", "/docs", "/openapi.json", "/redoc",
|
|
22
|
+
"/api/auth/nonce", "/api/auth/verify",
|
|
23
|
+
"/api/payments/pricing", "/api/x402/pricing"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
def __init__(self, app, jwt_secret: str):
|
|
27
|
+
super().__init__(app)
|
|
28
|
+
self.jwt_secret = jwt_secret
|
|
29
|
+
|
|
30
|
+
async def dispatch(self, request: Request, call_next: Callable) -> Response:
|
|
31
|
+
"""Process request with JWT authentication."""
|
|
32
|
+
|
|
33
|
+
# Skip public endpoints
|
|
34
|
+
if (request.url.path in self.PUBLIC_PATHS or
|
|
35
|
+
request.url.path.startswith("/static")):
|
|
36
|
+
return await call_next(request)
|
|
37
|
+
|
|
38
|
+
# Get JWT
|
|
39
|
+
auth_header = request.headers.get("Authorization", "")
|
|
40
|
+
if not auth_header.startswith("Bearer "):
|
|
41
|
+
return await call_next(request) # x402 flow
|
|
42
|
+
|
|
43
|
+
token = auth_header[7:]
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
# Decode JWT
|
|
47
|
+
payload = jwt.decode(token, self.jwt_secret, algorithms=["HS256"])
|
|
48
|
+
|
|
49
|
+
# Populate request.state via the canonical C4 contract.
|
|
50
|
+
from api.auth_state import set_auth_state
|
|
51
|
+
set_auth_state(
|
|
52
|
+
request.state,
|
|
53
|
+
user_id=payload.get("user_id"),
|
|
54
|
+
tier=payload.get("tier", "free"),
|
|
55
|
+
role=payload.get("role", "user"),
|
|
56
|
+
payment_method=None,
|
|
57
|
+
authenticated=True,
|
|
58
|
+
)
|
|
59
|
+
# NOTE: "sub" contains wallet_address (JWT standard)
|
|
60
|
+
request.state.wallet_address = payload.get("sub")
|
|
61
|
+
request.state.chain = payload.get("chain", "ethereum")
|
|
62
|
+
|
|
63
|
+
# Admin flag (simplified: only 'admin' role or admin wallet)
|
|
64
|
+
request.state.is_admin = is_admin(
|
|
65
|
+
role=request.state.role,
|
|
66
|
+
wallet_address=request.state.wallet_address
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
wallet_display = payload.get('sub', '')[:10] if payload.get('sub') else 'unknown'
|
|
70
|
+
logger.debug(f"JWT valid for {wallet_display}...")
|
|
71
|
+
|
|
72
|
+
except jwt.ExpiredSignatureError:
|
|
73
|
+
return JSONResponse(
|
|
74
|
+
status_code=401,
|
|
75
|
+
content={"error": "Token expired", "code": "TOKEN_EXPIRED"}
|
|
76
|
+
)
|
|
77
|
+
except jwt.InvalidTokenError:
|
|
78
|
+
return JSONResponse(
|
|
79
|
+
status_code=401,
|
|
80
|
+
content={"error": "Invalid token", "code": "INVALID_TOKEN"}
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
return await call_next(request)
|
api/kb/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# api/kb package
|
api/kb/endpoints.py
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
"""KB (knowledge-base) HTTP API — gated, multi-tenant.
|
|
2
|
+
|
|
3
|
+
Mount in ``api/app.py`` behind ``KB_API_ENABLED`` (default OFF), mirroring the
|
|
4
|
+
OpenAI-compat block:
|
|
5
|
+
|
|
6
|
+
from api.kb.endpoints import router as kb_router, kb_api_enabled
|
|
7
|
+
if kb_api_enabled():
|
|
8
|
+
app.include_router(kb_router)
|
|
9
|
+
|
|
10
|
+
Auth
|
|
11
|
+
----
|
|
12
|
+
``user_id`` is ALWAYS resolved from the authenticated request state via the shared
|
|
13
|
+
``get_user_id`` dependency (same seam the task HTTP API and other routers use).
|
|
14
|
+
A caller-supplied ``user_id`` in the body is never trusted.
|
|
15
|
+
|
|
16
|
+
Provider registration
|
|
17
|
+
---------------------
|
|
18
|
+
The SQLite memory backend (``MEMORY_BACKEND=sqlite``, default) is registered lazily
|
|
19
|
+
on the first ingest/search request via ``_ensure_backend``. On the full server path
|
|
20
|
+
``build_server_bot`` does NOT call ``maybe_register_memory_backend`` — that call lives
|
|
21
|
+
in ``construction.py`` (per-session agent build). Calling it here on first request is
|
|
22
|
+
idempotent: the factory's active-check short-circuits if a provider is already live
|
|
23
|
+
(a sibling session already registered it).
|
|
24
|
+
|
|
25
|
+
Path confinement (path-body variant)
|
|
26
|
+
-------------------------------------
|
|
27
|
+
``POST /api/kb/ingest`` with a path in the body: the path must be **relative** and
|
|
28
|
+
must not escape the authenticated tenant's session workspace. Absolute paths and
|
|
29
|
+
``..``-escapes are rejected with HTTP 400 *before* the engine is called (defence in
|
|
30
|
+
depth — the engine also confines, but we fail-fast here).
|
|
31
|
+
|
|
32
|
+
File-upload variant
|
|
33
|
+
-------------------
|
|
34
|
+
``POST /api/kb/ingest/upload`` accepts a multipart file upload. The bytes are
|
|
35
|
+
written to a unique temp file UNDER the authenticated tenant's session workspace
|
|
36
|
+
(``<workspace>/.kb_uploads/<unique>``), so the shared ``kb_ingest`` engine's
|
|
37
|
+
confinement check passes naturally. The upload is ingested through the SAME engine
|
|
38
|
+
as the path-body variant (no duplicated chunking/hashing), with ``source_name`` set
|
|
39
|
+
to the stripped original filename so a re-upload of the same file dedups by its
|
|
40
|
+
logical name rather than the volatile temp path. The temp file is removed in a
|
|
41
|
+
``finally`` block even on error.
|
|
42
|
+
"""
|
|
43
|
+
from __future__ import annotations
|
|
44
|
+
|
|
45
|
+
import logging
|
|
46
|
+
import os
|
|
47
|
+
import tempfile
|
|
48
|
+
from pathlib import Path
|
|
49
|
+
from typing import Optional
|
|
50
|
+
|
|
51
|
+
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
|
52
|
+
from fastapi.responses import JSONResponse
|
|
53
|
+
from pydantic import BaseModel, Field
|
|
54
|
+
|
|
55
|
+
from api.dependencies import get_user_id
|
|
56
|
+
from core.path_safety import is_within_root
|
|
57
|
+
|
|
58
|
+
logger = logging.getLogger(__name__)
|
|
59
|
+
|
|
60
|
+
router = APIRouter(prefix="/api/kb", tags=["knowledge-base"])
|
|
61
|
+
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
# Gate helper
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def kb_api_enabled() -> bool:
|
|
68
|
+
"""Whether the KB HTTP API is mounted (default OFF).
|
|
69
|
+
|
|
70
|
+
Uses the project's shared ``_bool_env`` parser (falsey-set semantics:
|
|
71
|
+
none/off/false/0/no/'' = off), so the flag behaves consistently with the
|
|
72
|
+
rest of POLYROB's env flags.
|
|
73
|
+
"""
|
|
74
|
+
from agents.task.constants import _bool_env
|
|
75
|
+
return _bool_env("KB_API_ENABLED", False)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ---------------------------------------------------------------------------
|
|
79
|
+
# Lazy backend registration (idempotent)
|
|
80
|
+
# ---------------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
def _ensure_backend() -> None:
|
|
83
|
+
"""Register the memory backend if it isn't already active.
|
|
84
|
+
|
|
85
|
+
Called on the first ingest/search request; idempotent — the factory
|
|
86
|
+
short-circuits if a provider is already registered.
|
|
87
|
+
"""
|
|
88
|
+
try:
|
|
89
|
+
from modules.memory.backend_factory import maybe_register_memory_backend
|
|
90
|
+
from core.config import BotConfig
|
|
91
|
+
try:
|
|
92
|
+
cfg = BotConfig()
|
|
93
|
+
data_dir = getattr(cfg, "data_dir", "data") or "data"
|
|
94
|
+
except Exception:
|
|
95
|
+
data_dir = "data"
|
|
96
|
+
maybe_register_memory_backend(data_dir=data_dir)
|
|
97
|
+
except Exception as e:
|
|
98
|
+
logger.debug("KB API: memory backend registration skipped: %s", e)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
# ---------------------------------------------------------------------------
|
|
102
|
+
# Request models
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
|
|
105
|
+
class KbIngestBody(BaseModel):
|
|
106
|
+
"""Body for ``POST /api/kb/ingest`` when providing a workspace-relative path."""
|
|
107
|
+
path: str = Field(..., description="Path relative to the tenant's session workspace.")
|
|
108
|
+
session_id: str = Field(..., description="Session ID whose workspace root to use.")
|
|
109
|
+
collection: str = Field("default", description="KB collection name.")
|
|
110
|
+
recursive: bool = Field(True, description="Recurse into subdirectories.")
|
|
111
|
+
globs: Optional[list[str]] = Field(None, description="Optional glob patterns.")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class KbSearchBody(BaseModel):
|
|
115
|
+
"""Body for ``POST /api/kb/search``."""
|
|
116
|
+
query: str = Field(..., description="Search query.")
|
|
117
|
+
collection: str = Field("default", description="KB collection to search.")
|
|
118
|
+
limit: int = Field(8, ge=1, le=50, description="Maximum number of results.")
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# ---------------------------------------------------------------------------
|
|
122
|
+
# Path-safety helper (endpoint-level early rejection)
|
|
123
|
+
# ---------------------------------------------------------------------------
|
|
124
|
+
|
|
125
|
+
def _resolve_and_guard(
|
|
126
|
+
path: str,
|
|
127
|
+
session_id: str,
|
|
128
|
+
user_id: str,
|
|
129
|
+
) -> Path:
|
|
130
|
+
"""Resolve *path* against the tenant workspace and return the resolved Path.
|
|
131
|
+
|
|
132
|
+
Raises ``HTTPException(400)`` for:
|
|
133
|
+
- Absolute paths.
|
|
134
|
+
- Paths that escape the workspace root after resolution.
|
|
135
|
+
"""
|
|
136
|
+
if os.path.isabs(path):
|
|
137
|
+
raise HTTPException(
|
|
138
|
+
status_code=400,
|
|
139
|
+
detail="Absolute paths are not allowed. Provide a path relative to your session workspace.",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
from agents.task.path import pm
|
|
143
|
+
try:
|
|
144
|
+
workspace_root = pm().get_workspace_dir(session_id, user_id)
|
|
145
|
+
except Exception as exc:
|
|
146
|
+
raise HTTPException(
|
|
147
|
+
status_code=400,
|
|
148
|
+
detail=f"Could not resolve workspace for session '{session_id}': {exc}",
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
resolved = (workspace_root / path).resolve()
|
|
152
|
+
|
|
153
|
+
if not is_within_root(str(resolved), str(workspace_root.resolve())):
|
|
154
|
+
raise HTTPException(
|
|
155
|
+
status_code=400,
|
|
156
|
+
detail="Path escapes the session workspace. Only paths within the workspace are allowed.",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
return resolved
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# ---------------------------------------------------------------------------
|
|
163
|
+
# POST /api/kb/ingest (path body variant)
|
|
164
|
+
# ---------------------------------------------------------------------------
|
|
165
|
+
|
|
166
|
+
@router.post("/ingest")
|
|
167
|
+
async def kb_ingest_path(
|
|
168
|
+
body: KbIngestBody,
|
|
169
|
+
user_id: str = Depends(get_user_id),
|
|
170
|
+
) -> JSONResponse:
|
|
171
|
+
"""Ingest a file or directory from the tenant's session workspace into the KB.
|
|
172
|
+
|
|
173
|
+
The ``path`` in the body must be **relative** to the authenticated tenant's
|
|
174
|
+
session workspace. Absolute paths and ``..``-escaping paths are rejected.
|
|
175
|
+
``user_id`` is derived from the auth dependency — the body never supplies it.
|
|
176
|
+
"""
|
|
177
|
+
_ensure_backend()
|
|
178
|
+
|
|
179
|
+
# Early path safety — reject absolute / escaping paths before calling the engine.
|
|
180
|
+
_resolve_and_guard(body.path, body.session_id, user_id)
|
|
181
|
+
|
|
182
|
+
try:
|
|
183
|
+
from tools.knowledge_ingest import kb_ingest
|
|
184
|
+
result = await kb_ingest(
|
|
185
|
+
body.path,
|
|
186
|
+
collection=body.collection,
|
|
187
|
+
recursive=body.recursive,
|
|
188
|
+
globs=body.globs,
|
|
189
|
+
user_id=user_id,
|
|
190
|
+
session_id=body.session_id,
|
|
191
|
+
)
|
|
192
|
+
except Exception as exc:
|
|
193
|
+
logger.warning("KB ingest failed for user=%s: %s", user_id, exc)
|
|
194
|
+
raise HTTPException(status_code=500, detail=f"KB ingest error: {exc}")
|
|
195
|
+
|
|
196
|
+
if "error" in result:
|
|
197
|
+
raise HTTPException(status_code=400, detail=result["error"])
|
|
198
|
+
|
|
199
|
+
return JSONResponse(content=result)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
# ---------------------------------------------------------------------------
|
|
203
|
+
# POST /api/kb/ingest/upload (file-upload variant)
|
|
204
|
+
# ---------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
@router.post("/ingest/upload")
|
|
207
|
+
async def kb_ingest_upload(
|
|
208
|
+
file: UploadFile = File(...),
|
|
209
|
+
collection: str = "default",
|
|
210
|
+
session_id: str = "_kb_upload_",
|
|
211
|
+
user_id: str = Depends(get_user_id),
|
|
212
|
+
) -> JSONResponse:
|
|
213
|
+
"""Ingest an uploaded file into the tenant's KB.
|
|
214
|
+
|
|
215
|
+
The bytes are written to a unique temp file UNDER the authenticated tenant's
|
|
216
|
+
session workspace, then ingested through the SAME ``kb_ingest`` engine as the
|
|
217
|
+
path-body variant — so size guards, MIME detection, dedup and confinement all
|
|
218
|
+
apply for free. ``source_name`` is the stripped original filename, so a
|
|
219
|
+
re-upload of the same file dedups by logical name (not the volatile temp path).
|
|
220
|
+
``user_id`` comes from the auth dependency, NEVER from the request body.
|
|
221
|
+
"""
|
|
222
|
+
_ensure_backend()
|
|
223
|
+
|
|
224
|
+
# Resolve the tenant's confined workspace root and write the upload UNDER it,
|
|
225
|
+
# so the engine's is_within_root confinement check passes naturally.
|
|
226
|
+
from agents.task.path import pm
|
|
227
|
+
try:
|
|
228
|
+
workspace_root = pm().get_workspace_dir(session_id, user_id)
|
|
229
|
+
except Exception as exc:
|
|
230
|
+
raise HTTPException(
|
|
231
|
+
status_code=400,
|
|
232
|
+
detail=f"Could not resolve workspace for session '{session_id}': {exc}",
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
upload_dir = Path(workspace_root) / ".kb_uploads"
|
|
236
|
+
upload_dir.mkdir(parents=True, exist_ok=True)
|
|
237
|
+
|
|
238
|
+
# Strip path components from the client filename (no traversal); keep it as the
|
|
239
|
+
# STABLE source identity so a re-upload dedups by its original name.
|
|
240
|
+
source_name = Path(file.filename or "upload.bin").name
|
|
241
|
+
suffix = Path(source_name).suffix or ".bin"
|
|
242
|
+
|
|
243
|
+
tmp_file: Optional[Path] = None
|
|
244
|
+
try:
|
|
245
|
+
fd, tmp_path_str = tempfile.mkstemp(suffix=suffix, dir=str(upload_dir))
|
|
246
|
+
tmp_file = Path(tmp_path_str)
|
|
247
|
+
try:
|
|
248
|
+
with os.fdopen(fd, "wb") as fh:
|
|
249
|
+
content = await file.read()
|
|
250
|
+
fh.write(content)
|
|
251
|
+
except Exception:
|
|
252
|
+
os.close(fd)
|
|
253
|
+
raise
|
|
254
|
+
|
|
255
|
+
# Engine path is relative to the workspace root (it resolves
|
|
256
|
+
# allowed_root / path); pass the temp file's path relative to the root.
|
|
257
|
+
rel_path = str(tmp_file.relative_to(Path(workspace_root)))
|
|
258
|
+
|
|
259
|
+
from tools.knowledge_ingest import kb_ingest
|
|
260
|
+
result = await kb_ingest(
|
|
261
|
+
rel_path,
|
|
262
|
+
collection=collection,
|
|
263
|
+
recursive=False,
|
|
264
|
+
user_id=user_id,
|
|
265
|
+
session_id=session_id,
|
|
266
|
+
source_name=source_name,
|
|
267
|
+
)
|
|
268
|
+
except HTTPException:
|
|
269
|
+
raise
|
|
270
|
+
except Exception as exc:
|
|
271
|
+
logger.warning("KB upload ingest failed for user=%s: %s", user_id, exc)
|
|
272
|
+
raise HTTPException(status_code=500, detail=f"KB ingest error: {exc}")
|
|
273
|
+
finally:
|
|
274
|
+
if tmp_file is not None:
|
|
275
|
+
try:
|
|
276
|
+
tmp_file.unlink(missing_ok=True)
|
|
277
|
+
except OSError:
|
|
278
|
+
pass
|
|
279
|
+
|
|
280
|
+
if "error" in result:
|
|
281
|
+
raise HTTPException(status_code=400, detail=result["error"])
|
|
282
|
+
|
|
283
|
+
return JSONResponse(content=result)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
# ---------------------------------------------------------------------------
|
|
287
|
+
# POST /api/kb/search
|
|
288
|
+
# ---------------------------------------------------------------------------
|
|
289
|
+
|
|
290
|
+
@router.post("/search")
|
|
291
|
+
async def kb_search_endpoint(
|
|
292
|
+
body: KbSearchBody,
|
|
293
|
+
user_id: str = Depends(get_user_id),
|
|
294
|
+
) -> JSONResponse:
|
|
295
|
+
"""Search the tenant's KB.
|
|
296
|
+
|
|
297
|
+
Results are scoped to the authenticated ``user_id``; a caller cannot search
|
|
298
|
+
another tenant's data.
|
|
299
|
+
"""
|
|
300
|
+
_ensure_backend()
|
|
301
|
+
|
|
302
|
+
try:
|
|
303
|
+
from modules.memory.registry import kb_search as _kb_search
|
|
304
|
+
result = await _kb_search(
|
|
305
|
+
body.query,
|
|
306
|
+
user_id=user_id,
|
|
307
|
+
collection=body.collection,
|
|
308
|
+
limit=body.limit,
|
|
309
|
+
)
|
|
310
|
+
except Exception as exc:
|
|
311
|
+
logger.warning("KB search failed for user=%s: %s", user_id, exc)
|
|
312
|
+
raise HTTPException(status_code=500, detail=f"KB search error: {exc}")
|
|
313
|
+
|
|
314
|
+
return JSONResponse(content={"results": result or ""})
|
api/mcp_models.py
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pydantic models for MCP server management API.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Optional, List, Dict, Any
|
|
6
|
+
from pydantic import BaseModel, Field, validator
|
|
7
|
+
import re
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AddServerRequest(BaseModel):
|
|
11
|
+
"""Request to add a new MCP server."""
|
|
12
|
+
|
|
13
|
+
server_name: str = Field(
|
|
14
|
+
...,
|
|
15
|
+
min_length=3,
|
|
16
|
+
max_length=50,
|
|
17
|
+
description="Unique server name (lowercase, alphanumeric, hyphens only)"
|
|
18
|
+
)
|
|
19
|
+
server_url: str = Field(
|
|
20
|
+
...,
|
|
21
|
+
description="Server endpoint URL (must be HTTPS)"
|
|
22
|
+
)
|
|
23
|
+
server_type: str = Field(
|
|
24
|
+
default="sse",
|
|
25
|
+
description="Server type: 'sse' or 'http' (STDIO not allowed)"
|
|
26
|
+
)
|
|
27
|
+
auth_method: str = Field(
|
|
28
|
+
default="api_key",
|
|
29
|
+
description="Authentication method: 'api_key', 'bearer', or 'none'"
|
|
30
|
+
)
|
|
31
|
+
api_key: Optional[str] = Field(
|
|
32
|
+
default=None,
|
|
33
|
+
description="API key for authentication (will be encrypted)"
|
|
34
|
+
)
|
|
35
|
+
headers: Optional[Dict[str, str]] = Field(
|
|
36
|
+
default=None,
|
|
37
|
+
description="Custom headers (will be encrypted)"
|
|
38
|
+
)
|
|
39
|
+
display_name: Optional[str] = Field(
|
|
40
|
+
default=None,
|
|
41
|
+
max_length=100,
|
|
42
|
+
description="Friendly display name"
|
|
43
|
+
)
|
|
44
|
+
enabled: bool = Field(
|
|
45
|
+
default=True,
|
|
46
|
+
description="Whether server is enabled"
|
|
47
|
+
)
|
|
48
|
+
timeout: int = Field(
|
|
49
|
+
default=30,
|
|
50
|
+
ge=5,
|
|
51
|
+
le=300,
|
|
52
|
+
description="Connection timeout in seconds"
|
|
53
|
+
)
|
|
54
|
+
retry_attempts: int = Field(
|
|
55
|
+
default=3,
|
|
56
|
+
ge=0,
|
|
57
|
+
le=10,
|
|
58
|
+
description="Number of retry attempts"
|
|
59
|
+
)
|
|
60
|
+
retry_delay: int = Field(
|
|
61
|
+
default=5,
|
|
62
|
+
ge=1,
|
|
63
|
+
le=60,
|
|
64
|
+
description="Delay between retry attempts in seconds"
|
|
65
|
+
)
|
|
66
|
+
auto_reconnect: bool = Field(
|
|
67
|
+
default=True,
|
|
68
|
+
description="Whether to automatically reconnect on failure"
|
|
69
|
+
)
|
|
70
|
+
max_concurrent_requests: int = Field(
|
|
71
|
+
default=5,
|
|
72
|
+
ge=1,
|
|
73
|
+
le=50,
|
|
74
|
+
description="Maximum concurrent requests to this server"
|
|
75
|
+
)
|
|
76
|
+
verify_connection: bool = Field(
|
|
77
|
+
default=False,
|
|
78
|
+
description="If true, verify MCP protocol before saving (tests connection and discovers tools)"
|
|
79
|
+
)
|
|
80
|
+
message_endpoint: Optional[str] = Field(
|
|
81
|
+
default=None,
|
|
82
|
+
description="For SSE servers: explicit POST endpoint for sending messages (if different from main URL)"
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
@validator('server_name')
|
|
86
|
+
def validate_server_name(cls, v):
|
|
87
|
+
"""Validate server name format."""
|
|
88
|
+
if not re.match(r'^[a-z0-9][a-z0-9-]*[a-z0-9]$', v) and len(v) > 2:
|
|
89
|
+
raise ValueError('Server name must be lowercase alphanumeric with hyphens')
|
|
90
|
+
if len(v) <= 2 and not re.match(r'^[a-z0-9]+$', v):
|
|
91
|
+
raise ValueError('Server name must be lowercase alphanumeric')
|
|
92
|
+
return v
|
|
93
|
+
|
|
94
|
+
@validator('server_type')
|
|
95
|
+
def validate_server_type(cls, v):
|
|
96
|
+
"""Validate server type."""
|
|
97
|
+
if v not in ('sse', 'http', 'streamable_http'):
|
|
98
|
+
raise ValueError('Server type must be "sse", "http", or "streamable_http" (STDIO not allowed)')
|
|
99
|
+
return v
|
|
100
|
+
|
|
101
|
+
@validator('auth_method')
|
|
102
|
+
def validate_auth_method(cls, v):
|
|
103
|
+
"""Validate auth method."""
|
|
104
|
+
if v not in ('api_key', 'bearer', 'none'):
|
|
105
|
+
raise ValueError('Auth method must be "api_key", "bearer", or "none"')
|
|
106
|
+
return v
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
class UpdateServerRequest(BaseModel):
|
|
110
|
+
"""Request to update an MCP server."""
|
|
111
|
+
|
|
112
|
+
server_url: Optional[str] = Field(
|
|
113
|
+
default=None,
|
|
114
|
+
description="Server endpoint URL (must be HTTPS)"
|
|
115
|
+
)
|
|
116
|
+
auth_method: Optional[str] = Field(
|
|
117
|
+
default=None,
|
|
118
|
+
description="Authentication method"
|
|
119
|
+
)
|
|
120
|
+
api_key: Optional[str] = Field(
|
|
121
|
+
default=None,
|
|
122
|
+
description="API key for authentication"
|
|
123
|
+
)
|
|
124
|
+
headers: Optional[Dict[str, str]] = Field(
|
|
125
|
+
default=None,
|
|
126
|
+
description="Custom headers"
|
|
127
|
+
)
|
|
128
|
+
display_name: Optional[str] = Field(
|
|
129
|
+
default=None,
|
|
130
|
+
max_length=100,
|
|
131
|
+
description="Friendly display name"
|
|
132
|
+
)
|
|
133
|
+
enabled: Optional[bool] = Field(
|
|
134
|
+
default=None,
|
|
135
|
+
description="Whether server is enabled"
|
|
136
|
+
)
|
|
137
|
+
timeout: Optional[int] = Field(
|
|
138
|
+
default=None,
|
|
139
|
+
ge=5,
|
|
140
|
+
le=300,
|
|
141
|
+
description="Connection timeout"
|
|
142
|
+
)
|
|
143
|
+
retry_attempts: Optional[int] = Field(
|
|
144
|
+
default=None,
|
|
145
|
+
ge=0,
|
|
146
|
+
le=10,
|
|
147
|
+
description="Retry attempts"
|
|
148
|
+
)
|
|
149
|
+
retry_delay: Optional[int] = Field(
|
|
150
|
+
default=None,
|
|
151
|
+
ge=1,
|
|
152
|
+
le=60,
|
|
153
|
+
description="Delay between retry attempts in seconds"
|
|
154
|
+
)
|
|
155
|
+
auto_reconnect: Optional[bool] = Field(
|
|
156
|
+
default=None,
|
|
157
|
+
description="Whether to automatically reconnect on failure"
|
|
158
|
+
)
|
|
159
|
+
max_concurrent_requests: Optional[int] = Field(
|
|
160
|
+
default=None,
|
|
161
|
+
ge=1,
|
|
162
|
+
le=50,
|
|
163
|
+
description="Maximum concurrent requests to this server"
|
|
164
|
+
)
|
|
165
|
+
message_endpoint: Optional[str] = Field(
|
|
166
|
+
default=None,
|
|
167
|
+
description="For SSE servers: explicit POST endpoint for sending messages"
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class ServerResponse(BaseModel):
|
|
172
|
+
"""Response with server details."""
|
|
173
|
+
|
|
174
|
+
server_name: str
|
|
175
|
+
display_name: Optional[str]
|
|
176
|
+
server_url: str
|
|
177
|
+
server_type: str
|
|
178
|
+
auth_method: str
|
|
179
|
+
enabled: bool
|
|
180
|
+
timeout: int
|
|
181
|
+
retry_attempts: int
|
|
182
|
+
retry_delay: int
|
|
183
|
+
auto_reconnect: bool
|
|
184
|
+
max_concurrent_requests: int
|
|
185
|
+
message_endpoint: Optional[str]
|
|
186
|
+
auth_status: str
|
|
187
|
+
connection_count: int
|
|
188
|
+
tools_discovered: int
|
|
189
|
+
last_connected_at: Optional[str]
|
|
190
|
+
last_error: Optional[str]
|
|
191
|
+
created_at: str
|
|
192
|
+
updated_at: str
|
|
193
|
+
|
|
194
|
+
# Tool ID for use in session creation
|
|
195
|
+
tool_id: str
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class ServerListResponse(BaseModel):
|
|
199
|
+
"""Response with list of servers."""
|
|
200
|
+
|
|
201
|
+
servers: List[ServerResponse]
|
|
202
|
+
count: int
|
|
203
|
+
max_servers: int
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class TestConnectionResponse(BaseModel):
|
|
207
|
+
"""Response from connection test."""
|
|
208
|
+
|
|
209
|
+
success: bool
|
|
210
|
+
latency_ms: Optional[float]
|
|
211
|
+
error: Optional[str]
|
|
212
|
+
tools_discovered: Optional[int]
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class UserSettingsRequest(BaseModel):
|
|
216
|
+
"""Request to update user MCP settings."""
|
|
217
|
+
|
|
218
|
+
mcp_enabled: Optional[bool] = Field(
|
|
219
|
+
default=None,
|
|
220
|
+
description="Enable/disable MCP for user"
|
|
221
|
+
)
|
|
222
|
+
include_global_servers: Optional[bool] = Field(
|
|
223
|
+
default=None,
|
|
224
|
+
description="Include global platform servers"
|
|
225
|
+
)
|
|
226
|
+
max_servers: Optional[int] = Field(
|
|
227
|
+
default=None,
|
|
228
|
+
ge=1,
|
|
229
|
+
le=50,
|
|
230
|
+
description="Maximum number of custom servers"
|
|
231
|
+
)
|
|
232
|
+
default_timeout: Optional[int] = Field(
|
|
233
|
+
default=None,
|
|
234
|
+
ge=5,
|
|
235
|
+
le=300,
|
|
236
|
+
description="Default timeout for new servers"
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class UserSettingsResponse(BaseModel):
|
|
241
|
+
"""Response with user MCP settings."""
|
|
242
|
+
|
|
243
|
+
mcp_enabled: bool
|
|
244
|
+
include_global_servers: bool
|
|
245
|
+
max_servers: int
|
|
246
|
+
default_timeout: int
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class AvailableServersResponse(BaseModel):
|
|
250
|
+
"""Response with all available MCP servers for session creation."""
|
|
251
|
+
|
|
252
|
+
global_servers: List[Dict[str, Any]]
|
|
253
|
+
user_servers: List[Dict[str, Any]]
|
|
254
|
+
include_global: bool
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
class AuditLogEntry(BaseModel):
|
|
258
|
+
"""Single audit log entry."""
|
|
259
|
+
|
|
260
|
+
id: int
|
|
261
|
+
action: str
|
|
262
|
+
server_name: Optional[str]
|
|
263
|
+
details: Optional[Dict[str, Any]]
|
|
264
|
+
ip_address: Optional[str]
|
|
265
|
+
timestamp: str
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class AuditLogResponse(BaseModel):
|
|
269
|
+
"""Response with audit log entries."""
|
|
270
|
+
|
|
271
|
+
entries: List[AuditLogEntry]
|
|
272
|
+
count: int
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
class ErrorResponse(BaseModel):
|
|
276
|
+
"""Standard error response."""
|
|
277
|
+
|
|
278
|
+
error: str
|
|
279
|
+
detail: Optional[str] = None
|