@vellumai/assistant 0.6.1 → 0.6.3
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.
- package/bun.lock +40 -40
- package/bunfig.toml +3 -0
- package/docker-entrypoint.sh +12 -2
- package/docs/architecture/memory.md +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +184 -69
- package/package.json +41 -41
- package/scripts/generate-openapi.ts +1 -2
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/browser-fill-credential.test.ts +229 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/catalog-files.test.ts +862 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/checker.test.ts +104 -170
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +125 -48
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/context-overflow-approval.test.ts +21 -6
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +1 -1
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-queue.test.ts +45 -2
- package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
- package/src/__tests__/conversation-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -4
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +2 -2
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/gemini-provider.test.ts +2 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +707 -371
- package/src/__tests__/headless-browser-navigate.test.ts +389 -47
- package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
- package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
- package/src/__tests__/host-bash-proxy.test.ts +150 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
- package/src/__tests__/host-cu-proxy.test.ts +171 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/log-export-workspace.test.ts +190 -0
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +61 -2
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +101 -1
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/oauth-apps-routes.test.ts +17 -12
- package/src/__tests__/oauth-cli.test.ts +707 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +50 -14
- package/src/__tests__/oauth-store.test.ts +1386 -182
- package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
- package/src/__tests__/onboarding-template-contract.test.ts +74 -55
- package/src/__tests__/openai-provider.test.ts +2 -2
- package/src/__tests__/outlook-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/require-fresh-approval.test.ts +40 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
- package/src/__tests__/slack-channel-config.test.ts +12 -15
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +72 -1
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +11 -5
- package/src/__tests__/test-preload.ts +14 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +62 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -35
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -0
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +55 -0
- package/src/cli/__tests__/run-assistant-command.ts +34 -7
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +18 -13
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +16 -2
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +200 -36
- package/src/cli/commands/oauth/shared.ts +5 -5
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -0
- package/src/cli/program.ts +10 -3
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- package/src/config/bundled-skills/contacts/SKILL.md +3 -0
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +12 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -0
- package/src/config/env-registry.ts +14 -0
- package/src/config/env.ts +21 -0
- package/src/config/feature-flag-registry.json +46 -7
- package/src/config/loader.ts +56 -1
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +46 -5
- package/src/config/schemas/host-browser.ts +66 -0
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +16 -0
- package/src/config/types.ts +0 -1
- package/src/context/post-turn-tool-result-truncation.ts +176 -0
- package/src/context/window-manager.ts +19 -1
- package/src/credential-execution/approval-bridge.ts +49 -16
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +6 -2
- package/src/daemon/context-overflow-approval.ts +5 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +74 -19
- package/src/daemon/conversation-attachments.ts +40 -1
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +66 -3
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +159 -20
- package/src/daemon/conversation-surfaces.ts +78 -12
- package/src/daemon/conversation-tool-setup.ts +74 -11
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +227 -11
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -139
- package/src/daemon/handlers/shared.ts +65 -0
- package/src/daemon/handlers/skills.ts +232 -37
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +191 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +86 -12
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +59 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -6
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/settings.ts +12 -0
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +112 -35
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +14 -0
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +38 -10
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +65 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +177 -18
- package/src/memory/graph/capability-seed.ts +3 -5
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/retriever.ts +40 -22
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/llm-usage-store.ts +45 -4
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +1 -0
- package/src/memory/schema/oauth.ts +18 -13
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +8 -8
- package/src/oauth/byo-connection.ts +7 -7
- package/src/oauth/connect-orchestrator.ts +23 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +16 -16
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +214 -100
- package/src/oauth/platform-connection.test.ts +5 -5
- package/src/oauth/platform-connection.ts +4 -4
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +127 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -96
- package/src/prompts/templates/SOUL.md +11 -11
- package/src/providers/anthropic/client.ts +1 -0
- package/src/providers/types.ts +1 -1
- package/src/runtime/AGENTS.md +23 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
- package/src/runtime/assistant-event-hub.ts +24 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +6 -7
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/chrome-extension-registry.ts +332 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +425 -70
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
- package/src/runtime/migrations/migration-transport.ts +6 -0
- package/src/runtime/migrations/migration-wizard.ts +22 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
- package/src/runtime/migrations/vbundle-importer.ts +55 -5
- package/src/runtime/pending-interactions.ts +29 -13
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/browser-cdp-routes.ts +229 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
- package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +308 -28
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/host-browser-routes.ts +279 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-routes.ts +259 -16
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +60 -25
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +87 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +9 -97
- package/src/runtime/routes/skills-routes.ts +52 -2
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/usage-routes.ts +8 -7
- package/src/runtime/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- package/src/schedule/scheduler.ts +7 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +42 -25
- package/src/security/secure-keys.ts +118 -25
- package/src/security/token-manager.ts +23 -10
- package/src/skills/catalog-files.ts +492 -0
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/subagent/manager.ts +131 -26
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +645 -340
- package/src/tools/browser/browser-manager.ts +36 -12
- package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
- package/src/tools/browser/cdp-client/errors.ts +34 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
- package/src/tools/browser/cdp-client/factory.ts +204 -0
- package/src/tools/browser/cdp-client/index.ts +14 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +52 -0
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/permission-checker.ts +77 -100
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -1
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +24 -21
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -3
- package/src/util/platform.ts +14 -19
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/029-seed-pkb.ts +1 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/top-level-renderer.ts +19 -1
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -4,13 +4,17 @@
|
|
|
4
4
|
* GET /v1/conversation-starters — list conversation starters (chips)
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import { and, desc, eq, inArray,
|
|
7
|
+
import { and, desc, eq, inArray, sql } from "drizzle-orm";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import { getDb } from "../../memory/db.js";
|
|
11
11
|
import { enqueueMemoryJob } from "../../memory/jobs-store.js";
|
|
12
12
|
import { rawGet } from "../../memory/raw-query.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
conversationStarters,
|
|
15
|
+
memoryCheckpoints,
|
|
16
|
+
memoryJobs,
|
|
17
|
+
} from "../../memory/schema.js";
|
|
14
18
|
import type { RouteDefinition } from "../http-router.js";
|
|
15
19
|
|
|
16
20
|
// ---------------------------------------------------------------------------
|
|
@@ -26,6 +30,39 @@ interface StarterItem {
|
|
|
26
30
|
batch: number;
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
const CK_ITEM_COUNT = "conversation_starters:item_count_at_last_gen";
|
|
34
|
+
const CK_LAST_GEN_AT = "conversation_starters:last_gen_at";
|
|
35
|
+
export const CONVERSATION_STARTERS_STALE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
36
|
+
|
|
37
|
+
function checkpointKey(base: string, scopeId: string): string {
|
|
38
|
+
return `${base}:${scopeId}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function parseCheckpointInt(value: string | undefined): number | null {
|
|
42
|
+
if (!value) return null;
|
|
43
|
+
const parsed = Number.parseInt(value, 10);
|
|
44
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasActiveConversationStarterJob(
|
|
48
|
+
db: ReturnType<typeof getDb>,
|
|
49
|
+
scopeId: string,
|
|
50
|
+
): boolean {
|
|
51
|
+
return (
|
|
52
|
+
db
|
|
53
|
+
.select({ id: memoryJobs.id })
|
|
54
|
+
.from(memoryJobs)
|
|
55
|
+
.where(
|
|
56
|
+
and(
|
|
57
|
+
eq(memoryJobs.type, "generate_conversation_starters"),
|
|
58
|
+
inArray(memoryJobs.status, ["pending", "running"]),
|
|
59
|
+
sql`json_extract(${memoryJobs.payload}, '$.scopeId') = ${scopeId}`,
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
.get() != null
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
29
66
|
/**
|
|
30
67
|
* Re-order starters so adjacent items have distinct categories wherever
|
|
31
68
|
* possible. Within each category, preserve the original (batch-desc) order.
|
|
@@ -150,14 +187,47 @@ function handleListConversationStarters(url: URL): Response {
|
|
|
150
187
|
|
|
151
188
|
const total = allItems.length;
|
|
152
189
|
|
|
153
|
-
// If starters exist,
|
|
190
|
+
// If starters exist, return them immediately. If the batch is stale or
|
|
191
|
+
// the generation checkpoint is ahead of the current active memory count,
|
|
192
|
+
// kick off a background refresh but keep the existing chips visible.
|
|
154
193
|
if (total > 0) {
|
|
194
|
+
const totalActive =
|
|
195
|
+
rawGet<{ c: number }>(
|
|
196
|
+
`SELECT COUNT(*) AS c FROM memory_graph_nodes WHERE fidelity != 'gone' AND scope_id = ?`,
|
|
197
|
+
scopeId,
|
|
198
|
+
)?.c ?? 0;
|
|
199
|
+
const lastCount = parseCheckpointInt(
|
|
200
|
+
db
|
|
201
|
+
.select({ value: memoryCheckpoints.value })
|
|
202
|
+
.from(memoryCheckpoints)
|
|
203
|
+
.where(eq(memoryCheckpoints.key, checkpointKey(CK_ITEM_COUNT, scopeId)))
|
|
204
|
+
.get()?.value,
|
|
205
|
+
);
|
|
206
|
+
const lastGenAt = parseCheckpointInt(
|
|
207
|
+
db
|
|
208
|
+
.select({ value: memoryCheckpoints.value })
|
|
209
|
+
.from(memoryCheckpoints)
|
|
210
|
+
.where(eq(memoryCheckpoints.key, checkpointKey(CK_LAST_GEN_AT, scopeId)))
|
|
211
|
+
.get()?.value,
|
|
212
|
+
);
|
|
213
|
+
const staleByAge =
|
|
214
|
+
lastGenAt == null ||
|
|
215
|
+
Date.now() - lastGenAt >= CONVERSATION_STARTERS_STALE_TTL_MS;
|
|
216
|
+
const checkpointAhead = lastCount != null && totalActive < lastCount;
|
|
217
|
+
let hasActiveJob = hasActiveConversationStarterJob(db, scopeId);
|
|
218
|
+
const shouldRefresh = staleByAge || checkpointAhead;
|
|
219
|
+
|
|
220
|
+
if (shouldRefresh && !hasActiveJob) {
|
|
221
|
+
enqueueMemoryJob("generate_conversation_starters", { scopeId });
|
|
222
|
+
hasActiveJob = true;
|
|
223
|
+
}
|
|
224
|
+
|
|
155
225
|
const ordered = orderStrongestFirst(allItems);
|
|
156
226
|
const page = ordered.slice(offsetParam, offsetParam + limitParam);
|
|
157
227
|
return Response.json({
|
|
158
228
|
starters: page,
|
|
159
229
|
total,
|
|
160
|
-
status: "ready",
|
|
230
|
+
status: hasActiveJob ? "refreshing" : "ready",
|
|
161
231
|
});
|
|
162
232
|
}
|
|
163
233
|
|
|
@@ -172,17 +242,7 @@ function handleListConversationStarters(url: URL): Response {
|
|
|
172
242
|
}
|
|
173
243
|
|
|
174
244
|
// Memory items exist but no starters yet — ensure a generation job is queued.
|
|
175
|
-
const existing = db
|
|
176
|
-
.select({ id: memoryJobs.id })
|
|
177
|
-
.from(memoryJobs)
|
|
178
|
-
.where(
|
|
179
|
-
and(
|
|
180
|
-
eq(memoryJobs.type, "generate_conversation_starters"),
|
|
181
|
-
inArray(memoryJobs.status, ["pending", "running"]),
|
|
182
|
-
like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
|
|
183
|
-
),
|
|
184
|
-
)
|
|
185
|
-
.get();
|
|
245
|
+
const existing = hasActiveConversationStarterJob(db, scopeId);
|
|
186
246
|
|
|
187
247
|
if (!existing) {
|
|
188
248
|
enqueueMemoryJob("generate_conversation_starters", { scopeId });
|
|
@@ -227,7 +287,9 @@ export function conversationStarterRouteDefinitions(): RouteDefinition[] {
|
|
|
227
287
|
.array(z.unknown())
|
|
228
288
|
.describe("Ordered list of starter chips"),
|
|
229
289
|
total: z.number().int().describe("Total number of available starters"),
|
|
230
|
-
status: z
|
|
290
|
+
status: z
|
|
291
|
+
.enum(["ready", "refreshing", "empty", "generating"])
|
|
292
|
+
.describe("One of: ready, refreshing, empty, generating"),
|
|
231
293
|
}),
|
|
232
294
|
},
|
|
233
295
|
];
|
|
@@ -63,8 +63,22 @@ export function groupRouteDefinitions(): RouteDefinition[] {
|
|
|
63
63
|
if (!body.name || typeof body.name !== "string") {
|
|
64
64
|
return httpError("BAD_REQUEST", "Missing or invalid name", 400);
|
|
65
65
|
}
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
try {
|
|
67
|
+
const group = createGroup(body.name);
|
|
68
|
+
return Response.json(serializeGroup(group), { status: 201 });
|
|
69
|
+
} catch (err) {
|
|
70
|
+
if (
|
|
71
|
+
err instanceof Error &&
|
|
72
|
+
err.message.includes("sort_position must be >= 4")
|
|
73
|
+
) {
|
|
74
|
+
return httpError(
|
|
75
|
+
"BAD_REQUEST",
|
|
76
|
+
"Too many custom groups — sort_position ceiling reached",
|
|
77
|
+
400,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
throw err;
|
|
81
|
+
}
|
|
68
82
|
},
|
|
69
83
|
},
|
|
70
84
|
{
|
|
@@ -105,16 +119,16 @@ export function groupRouteDefinitions(): RouteDefinition[] {
|
|
|
105
119
|
403,
|
|
106
120
|
);
|
|
107
121
|
}
|
|
108
|
-
// Custom group sort_position must be >= 3
|
|
122
|
+
// Custom group sort_position must be >= 4 (0–3 reserved for system groups)
|
|
109
123
|
if (
|
|
110
124
|
body.sortPosition !== undefined &&
|
|
111
125
|
(typeof body.sortPosition !== "number" ||
|
|
112
126
|
!isFinite(body.sortPosition) ||
|
|
113
|
-
body.sortPosition <
|
|
127
|
+
body.sortPosition < 4)
|
|
114
128
|
) {
|
|
115
129
|
return httpError(
|
|
116
130
|
"BAD_REQUEST",
|
|
117
|
-
"Custom group sort_position must be >=
|
|
131
|
+
"Custom group sort_position must be >= 4",
|
|
118
132
|
400,
|
|
119
133
|
);
|
|
120
134
|
}
|
|
@@ -176,7 +190,7 @@ export function groupRouteDefinitions(): RouteDefinition[] {
|
|
|
176
190
|
if (!Array.isArray(body.updates)) {
|
|
177
191
|
return httpError("BAD_REQUEST", "Missing updates array", 400);
|
|
178
192
|
}
|
|
179
|
-
// Validate: no system group reordering,
|
|
193
|
+
// Validate: no system group reordering, sort_position >= 4 for custom groups
|
|
180
194
|
for (const update of body.updates) {
|
|
181
195
|
const group = getGroup(update.groupId);
|
|
182
196
|
if (!group) continue;
|
|
@@ -190,11 +204,11 @@ export function groupRouteDefinitions(): RouteDefinition[] {
|
|
|
190
204
|
if (
|
|
191
205
|
typeof update.sortPosition !== "number" ||
|
|
192
206
|
!isFinite(update.sortPosition) ||
|
|
193
|
-
update.sortPosition <
|
|
207
|
+
update.sortPosition < 4
|
|
194
208
|
) {
|
|
195
209
|
return httpError(
|
|
196
210
|
"BAD_REQUEST",
|
|
197
|
-
`Custom group sort_position must be >=
|
|
211
|
+
`Custom group sort_position must be >= 4 (got ${update.sortPosition} for ${update.groupId})`,
|
|
198
212
|
400,
|
|
199
213
|
);
|
|
200
214
|
}
|
|
@@ -19,13 +19,14 @@ import {
|
|
|
19
19
|
type CanonicalGuardianRequest,
|
|
20
20
|
listPendingRequestsByConversationScope,
|
|
21
21
|
} from "../../memory/canonical-guardian-store.js";
|
|
22
|
+
import { isPermissionControlsV2Enabled } from "../../permissions/v2-consent-policy.js";
|
|
22
23
|
import { requireBoundGuardian } from "../auth/require-bound-guardian.js";
|
|
23
24
|
import type { AuthContext } from "../auth/types.js";
|
|
24
25
|
import { processGuardianDecision } from "../guardian-action-service.js";
|
|
25
26
|
import type { GuardianDecisionPrompt } from "../guardian-decision-types.js";
|
|
26
27
|
import {
|
|
27
28
|
buildDecisionActions,
|
|
28
|
-
|
|
29
|
+
buildOneTimeDecisionActions,
|
|
29
30
|
} from "../guardian-decision-types.js";
|
|
30
31
|
import { httpError } from "../http-errors.js";
|
|
31
32
|
import type { RouteDefinition } from "../http-router.js";
|
|
@@ -98,6 +99,18 @@ export async function handleGuardianActionDecision(
|
|
|
98
99
|
return httpError("BAD_REQUEST", "action is required", 400);
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
if (
|
|
103
|
+
isPermissionControlsV2Enabled() &&
|
|
104
|
+
action !== "approve_once" &&
|
|
105
|
+
action !== "reject"
|
|
106
|
+
) {
|
|
107
|
+
return httpError(
|
|
108
|
+
"FORBIDDEN",
|
|
109
|
+
"permission-controls-v2 only accepts approve_once or reject for guardian actions",
|
|
110
|
+
403,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
101
114
|
// Resolve the actor's guardian principal ID. For JWT-verified actors this
|
|
102
115
|
// comes from the token claims. For dev bypass (HTTP auth disabled) the
|
|
103
116
|
// synthetic "dev-bypass" principal won't match the real guardian binding,
|
|
@@ -193,15 +206,17 @@ export function listGuardianDecisionPrompts(params: {
|
|
|
193
206
|
* Map a canonical guardian request to the client-facing prompt format.
|
|
194
207
|
*
|
|
195
208
|
* Generates kind-specific questionText and action sets:
|
|
196
|
-
* - `tool_approval`: temporal modes (approve_once, approve_10m, approve_conversation) + reject
|
|
209
|
+
* - `tool_approval`: temporal modes (approve_once, approve_10m, approve_conversation) + reject in legacy mode,
|
|
210
|
+
* approve_once + reject only under v2
|
|
197
211
|
* - `pending_question`: approve_once + reject only
|
|
198
212
|
* - `access_request`: approve_once + reject only, with text fallback instructions
|
|
199
213
|
* (request code + "open invite flow")
|
|
200
214
|
* - `tool_grant_request`: approve_once + reject only
|
|
201
215
|
*
|
|
202
|
-
*
|
|
203
|
-
*
|
|
204
|
-
*
|
|
216
|
+
* Under permission-controls-v2 we collapse all deterministic guardian action
|
|
217
|
+
* prompts to approve_once + reject only. Outside v2, only `tool_approval`
|
|
218
|
+
* receives temporal modes because time-scoped grants are meaningful only for
|
|
219
|
+
* tool execution.
|
|
205
220
|
*/
|
|
206
221
|
function mapCanonicalRequestToPrompt(
|
|
207
222
|
req: CanonicalGuardianRequest,
|
|
@@ -209,15 +224,11 @@ function mapCanonicalRequestToPrompt(
|
|
|
209
224
|
): GuardianDecisionPrompt {
|
|
210
225
|
const questionText = buildKindAwareQuestionText(req);
|
|
211
226
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
req.kind === "tool_approval"
|
|
227
|
+
const actions = isPermissionControlsV2Enabled()
|
|
228
|
+
? buildOneTimeDecisionActions()
|
|
229
|
+
: req.kind === "tool_approval"
|
|
216
230
|
? buildDecisionActions({ forGuardianOnBehalf: true })
|
|
217
|
-
:
|
|
218
|
-
GUARDIAN_DECISION_ACTIONS.approve_once,
|
|
219
|
-
GUARDIAN_DECISION_ACTIONS.reject,
|
|
220
|
-
];
|
|
231
|
+
: buildOneTimeDecisionActions();
|
|
221
232
|
|
|
222
233
|
const expiresAt = req.expiresAt
|
|
223
234
|
? new Date(req.expiresAt).getTime()
|
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handler for host browser result submissions.
|
|
3
|
+
*
|
|
4
|
+
* Resolves pending host browser proxy requests by requestId when the desktop
|
|
5
|
+
* client returns CDP results via HTTP.
|
|
6
|
+
*/
|
|
7
|
+
import { z } from "zod";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
markTargetInvalidated,
|
|
11
|
+
publishCdpEvent,
|
|
12
|
+
} from "../../browser-session/events.js";
|
|
13
|
+
import { requireBoundGuardian } from "../auth/require-bound-guardian.js";
|
|
14
|
+
import type { AuthContext } from "../auth/types.js";
|
|
15
|
+
import { httpError } from "../http-errors.js";
|
|
16
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
17
|
+
import * as pendingInteractions from "../pending-interactions.js";
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Result of attempting to resolve a host browser result frame. Used by both
|
|
21
|
+
* the HTTP endpoint and the WS relay path so they share the same validation
|
|
22
|
+
* and resolution semantics.
|
|
23
|
+
*
|
|
24
|
+
* Success → the pending interaction was consumed and the conversation (or
|
|
25
|
+
* CLI shim callback) was notified.
|
|
26
|
+
*
|
|
27
|
+
* Error variants mirror the HTTP status codes the `/v1/host-browser-result`
|
|
28
|
+
* endpoint returns, so the caller can log/translate them consistently.
|
|
29
|
+
*/
|
|
30
|
+
export type HostBrowserResultResolution =
|
|
31
|
+
| { ok: true }
|
|
32
|
+
| {
|
|
33
|
+
ok: false;
|
|
34
|
+
code: "BAD_REQUEST" | "NOT_FOUND" | "CONFLICT";
|
|
35
|
+
status: 400 | 404 | 409;
|
|
36
|
+
message: string;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Shared resolver used by both the HTTP route handler and the WS
|
|
41
|
+
* `host_browser_result` frame handler. Looks up the pending interaction
|
|
42
|
+
* by requestId, validates its kind, and forwards the response to the
|
|
43
|
+
* owning conversation (or CLI shim callback).
|
|
44
|
+
*
|
|
45
|
+
* This function does NOT perform auth — callers are expected to have
|
|
46
|
+
* already authenticated the caller (the HTTP route uses
|
|
47
|
+
* `requireBoundGuardian`, the WS path relies on the JWT check performed
|
|
48
|
+
* at WebSocket upgrade time).
|
|
49
|
+
*/
|
|
50
|
+
export function resolveHostBrowserResultByRequestId(frame: {
|
|
51
|
+
requestId?: unknown;
|
|
52
|
+
content?: unknown;
|
|
53
|
+
isError?: unknown;
|
|
54
|
+
}): HostBrowserResultResolution {
|
|
55
|
+
const { requestId, content, isError } = frame;
|
|
56
|
+
|
|
57
|
+
if (!requestId || typeof requestId !== "string") {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
code: "BAD_REQUEST",
|
|
61
|
+
status: 400,
|
|
62
|
+
message: "requestId is required",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Peek first (non-destructive) so we can validate the interaction kind
|
|
67
|
+
// without accidentally consuming a confirmation or secret interaction.
|
|
68
|
+
const peeked = pendingInteractions.get(requestId);
|
|
69
|
+
if (!peeked) {
|
|
70
|
+
return {
|
|
71
|
+
ok: false,
|
|
72
|
+
code: "NOT_FOUND",
|
|
73
|
+
status: 404,
|
|
74
|
+
message: "No pending interaction found for this requestId",
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (peeked.kind !== "host_browser") {
|
|
79
|
+
return {
|
|
80
|
+
ok: false,
|
|
81
|
+
code: "CONFLICT",
|
|
82
|
+
status: 409,
|
|
83
|
+
message: `Pending interaction is of kind "${peeked.kind}", expected "host_browser"`,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Validation passed — consume the pending interaction.
|
|
88
|
+
const interaction = pendingInteractions.resolve(requestId)!;
|
|
89
|
+
|
|
90
|
+
const normalizedContent = typeof content === "string" ? content : "";
|
|
91
|
+
const normalizedIsError = typeof isError === "boolean" ? isError : false;
|
|
92
|
+
|
|
93
|
+
// CLI shim path: pending interactions registered by /v1/browser-cdp carry
|
|
94
|
+
// a directBrowserResolve callback and are not bound to a Conversation.
|
|
95
|
+
// Resolve them in place without touching the conversation object.
|
|
96
|
+
if (interaction.directBrowserResolve) {
|
|
97
|
+
interaction.directBrowserResolve({
|
|
98
|
+
content: normalizedContent,
|
|
99
|
+
isError: normalizedIsError,
|
|
100
|
+
});
|
|
101
|
+
return { ok: true };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// The host_browser kind always has a conversation attached at register time
|
|
105
|
+
// (HostBrowserProxy.request wires it through), so this guard exists so a
|
|
106
|
+
// future refactor of pending-interactions can change the type without
|
|
107
|
+
// silently breaking the host_browser path. Prefer an explicit error over an
|
|
108
|
+
// optional-chain no-op that would leave the proxy request unresolved.
|
|
109
|
+
if (!interaction.conversation) {
|
|
110
|
+
return {
|
|
111
|
+
ok: false,
|
|
112
|
+
code: "BAD_REQUEST",
|
|
113
|
+
status: 400,
|
|
114
|
+
message:
|
|
115
|
+
"host_browser pending interaction has no associated conversation",
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interaction.conversation.resolveHostBrowser(requestId, {
|
|
120
|
+
content: normalizedContent,
|
|
121
|
+
isError: normalizedIsError,
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
return { ok: true };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Result of attempting to resolve a `host_browser_event` frame. The
|
|
129
|
+
* event surface never fails — every well-formed frame is published
|
|
130
|
+
* to the runtime-side CDP event bus — so the resolution is either
|
|
131
|
+
* `ok: true` or a `BAD_REQUEST` when the frame is malformed. Kept as
|
|
132
|
+
* a discriminated union (rather than `void`) so the WS dispatcher
|
|
133
|
+
* can log rejected frames with a consistent shape.
|
|
134
|
+
*/
|
|
135
|
+
export type HostBrowserEventResolution =
|
|
136
|
+
| { ok: true }
|
|
137
|
+
| {
|
|
138
|
+
ok: false;
|
|
139
|
+
code: "BAD_REQUEST";
|
|
140
|
+
status: 400;
|
|
141
|
+
message: string;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Shared resolver for `host_browser_event` envelopes. Publishes the
|
|
146
|
+
* event into the module-level browser-session event bus where
|
|
147
|
+
* runtime-side consumers can subscribe. Validation is intentionally
|
|
148
|
+
* minimal — the frame is opaque to the bus and the bus's listeners
|
|
149
|
+
* are the ones that care about params shape.
|
|
150
|
+
*/
|
|
151
|
+
export function resolveHostBrowserEvent(frame: {
|
|
152
|
+
method?: unknown;
|
|
153
|
+
params?: unknown;
|
|
154
|
+
cdpSessionId?: unknown;
|
|
155
|
+
}): HostBrowserEventResolution {
|
|
156
|
+
const { method, params, cdpSessionId } = frame;
|
|
157
|
+
|
|
158
|
+
if (!method || typeof method !== "string") {
|
|
159
|
+
return {
|
|
160
|
+
ok: false,
|
|
161
|
+
code: "BAD_REQUEST",
|
|
162
|
+
status: 400,
|
|
163
|
+
message: "method is required",
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
publishCdpEvent({
|
|
168
|
+
method,
|
|
169
|
+
params,
|
|
170
|
+
cdpSessionId:
|
|
171
|
+
typeof cdpSessionId === "string" && cdpSessionId.length > 0
|
|
172
|
+
? cdpSessionId
|
|
173
|
+
: undefined,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return { ok: true };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Result of attempting to resolve a `host_browser_session_invalidated`
|
|
181
|
+
* frame. Mirrors {@link HostBrowserEventResolution} — publishing into
|
|
182
|
+
* the invalidated-target registry never fails, so the resolution is
|
|
183
|
+
* either `ok: true` or a `BAD_REQUEST` on a malformed frame.
|
|
184
|
+
*/
|
|
185
|
+
export type HostBrowserSessionInvalidatedResolution =
|
|
186
|
+
| { ok: true }
|
|
187
|
+
| {
|
|
188
|
+
ok: false;
|
|
189
|
+
code: "BAD_REQUEST";
|
|
190
|
+
status: 400;
|
|
191
|
+
message: string;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Shared resolver for `host_browser_session_invalidated` envelopes.
|
|
196
|
+
* Marks the target as invalidated in the runtime-side registry; the
|
|
197
|
+
* next `BrowserSessionManager.send()` against that target will
|
|
198
|
+
* evict the stale session and force the owning tool to reattach.
|
|
199
|
+
*
|
|
200
|
+
* A frame without a `targetId` is tolerated (some detach notifications
|
|
201
|
+
* carry only a `reason`) but logged at info because it is less
|
|
202
|
+
* actionable than a targeted invalidation — the caller can still
|
|
203
|
+
* subscribe to the event bus to observe the signal.
|
|
204
|
+
*/
|
|
205
|
+
export function resolveHostBrowserSessionInvalidated(frame: {
|
|
206
|
+
targetId?: unknown;
|
|
207
|
+
reason?: unknown;
|
|
208
|
+
}): HostBrowserSessionInvalidatedResolution {
|
|
209
|
+
const { targetId, reason } = frame;
|
|
210
|
+
|
|
211
|
+
if (targetId !== undefined && typeof targetId !== "string") {
|
|
212
|
+
return {
|
|
213
|
+
ok: false,
|
|
214
|
+
code: "BAD_REQUEST",
|
|
215
|
+
status: 400,
|
|
216
|
+
message: "targetId must be a string when present",
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
if (typeof targetId === "string" && targetId.length > 0) {
|
|
221
|
+
markTargetInvalidated(
|
|
222
|
+
targetId,
|
|
223
|
+
typeof reason === "string" ? reason : undefined,
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return { ok: true };
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* POST /v1/host-browser-result — resolve a pending host browser request by requestId.
|
|
232
|
+
* Requires AuthContext with guardian-bound actor.
|
|
233
|
+
*/
|
|
234
|
+
export async function handleHostBrowserResult(
|
|
235
|
+
req: Request,
|
|
236
|
+
authContext: AuthContext,
|
|
237
|
+
): Promise<Response> {
|
|
238
|
+
const authError = requireBoundGuardian(authContext);
|
|
239
|
+
if (authError) return authError;
|
|
240
|
+
|
|
241
|
+
const body = (await req.json()) as {
|
|
242
|
+
requestId?: string;
|
|
243
|
+
content?: string;
|
|
244
|
+
isError?: boolean;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const resolution = resolveHostBrowserResultByRequestId(body);
|
|
248
|
+
if (!resolution.ok) {
|
|
249
|
+
return httpError(resolution.code, resolution.message, resolution.status);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return Response.json({ accepted: true });
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Route definitions
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
|
|
259
|
+
export function hostBrowserRouteDefinitions(): RouteDefinition[] {
|
|
260
|
+
return [
|
|
261
|
+
{
|
|
262
|
+
endpoint: "host-browser-result",
|
|
263
|
+
method: "POST",
|
|
264
|
+
summary: "Submit host browser result",
|
|
265
|
+
description: "Resolve a pending host browser request by requestId.",
|
|
266
|
+
tags: ["host"],
|
|
267
|
+
requestBody: z.object({
|
|
268
|
+
requestId: z.string().describe("Pending browser request ID"),
|
|
269
|
+
content: z.string().optional(),
|
|
270
|
+
isError: z.boolean().optional(),
|
|
271
|
+
}),
|
|
272
|
+
responseBody: z.object({
|
|
273
|
+
accepted: z.boolean(),
|
|
274
|
+
}),
|
|
275
|
+
handler: async ({ req, authContext }) =>
|
|
276
|
+
handleHostBrowserResult(req, authContext),
|
|
277
|
+
},
|
|
278
|
+
];
|
|
279
|
+
}
|
|
@@ -27,9 +27,10 @@ export async function handleHostFileResult(
|
|
|
27
27
|
requestId?: string;
|
|
28
28
|
content?: string;
|
|
29
29
|
isError?: boolean;
|
|
30
|
+
imageData?: string;
|
|
30
31
|
};
|
|
31
32
|
|
|
32
|
-
const { requestId, content, isError } = body;
|
|
33
|
+
const { requestId, content, isError, imageData } = body;
|
|
33
34
|
|
|
34
35
|
if (!requestId || typeof requestId !== "string") {
|
|
35
36
|
return httpError("BAD_REQUEST", "requestId is required", 400);
|
|
@@ -60,6 +61,7 @@ export async function handleHostFileResult(
|
|
|
60
61
|
interaction.conversation!.resolveHostFile(requestId, {
|
|
61
62
|
content: content ?? "",
|
|
62
63
|
isError: isError ?? false,
|
|
64
|
+
imageData,
|
|
63
65
|
});
|
|
64
66
|
|
|
65
67
|
return Response.json({ accepted: true });
|
|
@@ -87,6 +89,12 @@ export function hostFileRouteDefinitions(): RouteDefinition[] {
|
|
|
87
89
|
.boolean()
|
|
88
90
|
.describe("Whether the result is an error")
|
|
89
91
|
.optional(),
|
|
92
|
+
imageData: z
|
|
93
|
+
.string()
|
|
94
|
+
.describe(
|
|
95
|
+
"Optional base64-encoded image bytes for successful image reads",
|
|
96
|
+
)
|
|
97
|
+
.optional(),
|
|
90
98
|
}),
|
|
91
99
|
responseBody: z.object({
|
|
92
100
|
accepted: z.boolean(),
|