@vellumai/assistant 0.6.2 → 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/docs/architecture/memory.md +1 -1
- 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__/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__/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 +16 -1
- 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 +2 -2
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -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 -1
- 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__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- 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__/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 +75 -57
- 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__/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 -1
- 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-tools.test.ts +9 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- 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 +14 -29
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -6
- 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 +53 -3
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- 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 +44 -44
- 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 +6 -3
- 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/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 +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
- 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 +1 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- 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 +44 -5
- 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 +8 -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 -15
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/context-overflow-approval.ts +5 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +58 -24
- package/src/daemon/conversation-attachments.ts +40 -0
- package/src/daemon/conversation-process.ts +48 -1
- package/src/daemon/conversation-runtime-assembly.ts +118 -36
- package/src/daemon/conversation-surfaces.ts +37 -36
- package/src/daemon/conversation-tool-setup.ts +74 -8
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +226 -8
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -140
- package/src/daemon/handlers/shared.ts +58 -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 +65 -11
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +55 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -5
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +92 -12
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +5 -24
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +23 -0
- 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 +176 -17
- 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/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/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 +3 -3
- 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 +126 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -1
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -105
- 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 +2 -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/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 +2 -1
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +301 -27
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- 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-routes.ts +42 -22
- 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/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 -82
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -0
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- 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/shell.ts +21 -16
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -0
- package/src/util/platform.ts +14 -19
- 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,9 +4,9 @@ import {
|
|
|
4
4
|
getApp,
|
|
5
5
|
getAppDirPath,
|
|
6
6
|
getAppPreview,
|
|
7
|
-
inlineDistAssets,
|
|
8
7
|
isMultifileApp,
|
|
9
8
|
resolveAppDir,
|
|
9
|
+
resolveEffectiveAppHtml,
|
|
10
10
|
updateApp,
|
|
11
11
|
} from "../memory/app-store.js";
|
|
12
12
|
import {
|
|
@@ -65,32 +65,36 @@ export function markSurfaceCompleted(
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// Persist to DB.
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
let
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
rb.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
68
|
+
try {
|
|
69
|
+
const rows = getMessages(ctx.conversationId);
|
|
70
|
+
for (let r = rows.length - 1; r >= 0; r--) {
|
|
71
|
+
let parsed: unknown[];
|
|
72
|
+
try {
|
|
73
|
+
const result = JSON.parse(rows[r].content);
|
|
74
|
+
if (!Array.isArray(result)) continue;
|
|
75
|
+
parsed = result;
|
|
76
|
+
} catch {
|
|
77
|
+
// Some rows store plain text content (e.g. notification seeding) —
|
|
78
|
+
// skip them and keep scanning.
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
let found = false;
|
|
82
|
+
for (const pb of parsed) {
|
|
83
|
+
const rb = pb as Record<string, unknown>;
|
|
84
|
+
if (rb.type === "ui_surface" && rb.surfaceId === surfaceId) {
|
|
85
|
+
rb.completed = true;
|
|
86
|
+
rb.completionSummary = summary;
|
|
87
|
+
found = true;
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (found) {
|
|
92
|
+
updateMessageContent(rows[r].id, JSON.stringify(parsed));
|
|
93
|
+
return;
|
|
88
94
|
}
|
|
89
95
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
log.warn({ err, surfaceId }, "Failed to persist surface completion to DB");
|
|
94
98
|
}
|
|
95
99
|
}
|
|
96
100
|
const TASK_PROGRESS_TEMPLATE_FIELDS = ["title", "status", "steps"] as const;
|
|
@@ -1146,10 +1150,12 @@ export function refreshSurfacesForApp(
|
|
|
1146
1150
|
// Push current HTML onto the undo stack before overwriting
|
|
1147
1151
|
pushUndoState(ctx.surfaceUndoStacks, surfaceId, data.html);
|
|
1148
1152
|
|
|
1149
|
-
// Update in-memory surface state so the next refinement gets fresh HTML
|
|
1153
|
+
// Update in-memory surface state so the next refinement gets fresh HTML.
|
|
1154
|
+
// For multifile apps, resolve the compiled dist/index.html with inlined
|
|
1155
|
+
// assets rather than the empty root index.html (app.htmlDefinition).
|
|
1150
1156
|
const updatedData: DynamicPageSurfaceData = {
|
|
1151
1157
|
...data,
|
|
1152
|
-
html: app
|
|
1158
|
+
html: resolveEffectiveAppHtml(app),
|
|
1153
1159
|
...(opts?.fileChange
|
|
1154
1160
|
? { reloadGeneration: (data.reloadGeneration ?? 0) + 1 }
|
|
1155
1161
|
: {}),
|
|
@@ -1539,11 +1545,10 @@ export async function surfaceProxyResolver(
|
|
|
1539
1545
|
const storedPreview = getAppPreview(app.id);
|
|
1540
1546
|
const { dirName } = resolveAppDir(app.id);
|
|
1541
1547
|
|
|
1542
|
-
// For multifile TSX apps,
|
|
1543
|
-
//
|
|
1544
|
-
let html = app.htmlDefinition;
|
|
1548
|
+
// For multifile TSX apps, auto-compile if dist is missing, then
|
|
1549
|
+
// resolve HTML from compiled dist/index.html with inlined assets.
|
|
1545
1550
|
if (isMultifileApp(app)) {
|
|
1546
|
-
const { existsSync
|
|
1551
|
+
const { existsSync } = await import("node:fs");
|
|
1547
1552
|
const { join } = await import("node:path");
|
|
1548
1553
|
const appDir = getAppDirPath(app.id);
|
|
1549
1554
|
const distIndex = join(appDir, "dist", "index.html");
|
|
@@ -1557,12 +1562,8 @@ export async function surfaceProxyResolver(
|
|
|
1557
1562
|
);
|
|
1558
1563
|
}
|
|
1559
1564
|
}
|
|
1560
|
-
if (existsSync(distIndex)) {
|
|
1561
|
-
html = inlineDistAssets(appDir, readFileSync(distIndex, "utf-8"));
|
|
1562
|
-
} else {
|
|
1563
|
-
html = `<p>App compilation failed. Edit a source file to trigger a rebuild.</p>`;
|
|
1564
|
-
}
|
|
1565
1565
|
}
|
|
1566
|
+
const html = resolveEffectiveAppHtml(app);
|
|
1566
1567
|
|
|
1567
1568
|
const surfaceData: DynamicPageSurfaceData = {
|
|
1568
1569
|
html,
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
* keeping the constructor body focused on wiring.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import {
|
|
10
|
+
type HostProxyCapability,
|
|
11
|
+
type InterfaceId,
|
|
12
|
+
supportsHostProxy,
|
|
13
|
+
} from "../channels/types.js";
|
|
9
14
|
import { isHttpAuthDisabled } from "../config/env.js";
|
|
10
15
|
import { getIsPlatform } from "../config/env-registry.js";
|
|
11
16
|
import type { CesClient } from "../credential-execution/client.js";
|
|
@@ -22,6 +27,7 @@ import {
|
|
|
22
27
|
findHighestPriorityRule,
|
|
23
28
|
} from "../permissions/trust-store.js";
|
|
24
29
|
import { isAllowDecision } from "../permissions/types.js";
|
|
30
|
+
import { isPermissionControlsV2Enabled } from "../permissions/v2-consent-policy.js";
|
|
25
31
|
import type { Message, ToolDefinition } from "../providers/types.js";
|
|
26
32
|
import type { TrustClass } from "../runtime/actor-trust-resolver.js";
|
|
27
33
|
import { coreAppProxyTools } from "../tools/apps/definitions.js";
|
|
@@ -106,6 +112,8 @@ export interface ToolSetupContext extends SurfaceConversationContext {
|
|
|
106
112
|
callSessionId?: string;
|
|
107
113
|
/** Optional proxy for delegating host_bash execution to a connected client. */
|
|
108
114
|
hostBashProxy?: import("./host-bash-proxy.js").HostBashProxy;
|
|
115
|
+
/** Optional proxy for delegating CDP commands to a connected client (managed/cloud-hosted mode). */
|
|
116
|
+
hostBrowserProxy?: import("./host-browser-proxy.js").HostBrowserProxy;
|
|
109
117
|
/** Optional proxy for delegating host_file_read/write/edit execution to a connected client. */
|
|
110
118
|
hostFileProxy?: import("./host-file-proxy.js").HostFileProxy;
|
|
111
119
|
/** CES RPC client for credential execution operations. Injected when CES tools are enabled and the CES process is available. */
|
|
@@ -190,6 +198,7 @@ export function createToolExecutor(
|
|
|
190
198
|
forcePromptSideEffects: ctx.memoryPolicy.strictSideEffects,
|
|
191
199
|
toolUseId,
|
|
192
200
|
hostBashProxy: ctx.hostBashProxy,
|
|
201
|
+
hostBrowserProxy: ctx.hostBrowserProxy,
|
|
193
202
|
hostFileProxy: ctx.hostFileProxy,
|
|
194
203
|
isPlatformHosted: getIsPlatform(),
|
|
195
204
|
cesClient: ctx.cesClient,
|
|
@@ -312,6 +321,13 @@ export function createProxyApprovalCallback(
|
|
|
312
321
|
const { scheme } = decision.target;
|
|
313
322
|
const url = `${scheme}://${hostname}${port ? ":" + port : ""}${path}`;
|
|
314
323
|
|
|
324
|
+
if (isPermissionControlsV2Enabled()) {
|
|
325
|
+
// Under v2 we suppress deterministic network approval cards entirely.
|
|
326
|
+
// Proxied asks should follow the same non-host auto-allow contract as
|
|
327
|
+
// regular network_request invocations instead of turning into hard blocks.
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
330
|
+
|
|
315
331
|
const input: Record<string, unknown> = {
|
|
316
332
|
url,
|
|
317
333
|
proxy_session_id: request.sessionId,
|
|
@@ -456,17 +472,49 @@ export interface SkillProjectionContext {
|
|
|
456
472
|
subagentAllowedTools?: Set<string>;
|
|
457
473
|
/** True when this conversation belongs to a subagent spawned by SubagentManager. */
|
|
458
474
|
readonly isSubagent?: boolean;
|
|
475
|
+
/**
|
|
476
|
+
* The interface id of the connected client driving the current turn (e.g.
|
|
477
|
+
* "macos", "chrome-extension"). Used to gate host tools by per-capability
|
|
478
|
+
* `supportsHostProxy(transport, capability)` so that interfaces which only
|
|
479
|
+
* support a subset of the host proxy set (e.g. chrome-extension supports
|
|
480
|
+
* `host_browser` but not `host_bash`/`host_file`) do not leak unsupported
|
|
481
|
+
* host tools into the LLM tool definitions.
|
|
482
|
+
*/
|
|
483
|
+
readonly transportInterface?: InterfaceId;
|
|
459
484
|
}
|
|
460
485
|
|
|
461
486
|
// ── Conditional tool sets ────────────────────────────────────────────
|
|
462
487
|
|
|
463
488
|
const UI_SURFACE_TOOL_NAMES = new Set(["ui_show", "ui_update", "ui_dismiss"]);
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
489
|
+
/**
|
|
490
|
+
* Single source of truth for which tools are host tools and the capability
|
|
491
|
+
* each one requires from the connected client interface. Adding a tool here
|
|
492
|
+
* automatically adds it to `HOST_TOOL_NAMES` below, so the two collections
|
|
493
|
+
* cannot drift apart: if a new host tool is added without a capability
|
|
494
|
+
* mapping, `isToolActiveForContext` cannot accidentally return `true` for
|
|
495
|
+
* chrome-extension (or any other partial-capability transport) because
|
|
496
|
+
* `HOST_TOOL_NAMES` wouldn't contain it either.
|
|
497
|
+
*
|
|
498
|
+
* `isToolActiveForContext` uses this map to gate each host tool individually
|
|
499
|
+
* so that partial-capability transports (e.g. chrome-extension only supports
|
|
500
|
+
* `host_browser`) only see the host tools their interface can actually
|
|
501
|
+
* service.
|
|
502
|
+
*
|
|
503
|
+
* Note: there is no `host_cu` tool exposed via the tool gating layer today;
|
|
504
|
+
* computer-use is preactivated as a skill and projected through the skill
|
|
505
|
+
* tools path. Only host tools that flow through the per-capability gate
|
|
506
|
+
* need entries here.
|
|
507
|
+
*/
|
|
508
|
+
export const HOST_TOOL_TO_CAPABILITY = new Map<string, HostProxyCapability>([
|
|
509
|
+
["host_bash", "host_bash"],
|
|
510
|
+
["host_file_read", "host_file"],
|
|
511
|
+
["host_file_write", "host_file"],
|
|
512
|
+
["host_file_edit", "host_file"],
|
|
513
|
+
["host_browser", "host_browser"],
|
|
469
514
|
]);
|
|
515
|
+
// Derived from HOST_TOOL_TO_CAPABILITY so the invariant "every host tool has
|
|
516
|
+
// a capability mapping" is a structural fact — no runtime assertion needed.
|
|
517
|
+
export const HOST_TOOL_NAMES = new Set(HOST_TOOL_TO_CAPABILITY.keys());
|
|
470
518
|
const CLIENT_CAPABILITY_TOOL_NAMES = new Set(["app_open"]);
|
|
471
519
|
const PLATFORM_TOOL_NAMES = new Set(["request_system_permission"]);
|
|
472
520
|
|
|
@@ -495,9 +543,27 @@ export function isToolActiveForContext(
|
|
|
495
543
|
return ctx.channelCapabilities?.supportsDynamicUi ?? !ctx.hasNoClient;
|
|
496
544
|
}
|
|
497
545
|
if (HOST_TOOL_NAMES.has(name)) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
546
|
+
const capability = HOST_TOOL_TO_CAPABILITY.get(name);
|
|
547
|
+
const transport = ctx.transportInterface;
|
|
548
|
+
|
|
549
|
+
// Per-capability check is authoritative for structural support: if the
|
|
550
|
+
// transport cannot service this capability, the tool is filtered out.
|
|
551
|
+
if (transport && capability && !supportsHostProxy(transport, capability)) {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// chrome-extension is its own executor — the extension's popup gates
|
|
556
|
+
// commands via its own UI, and the transport does not use an SSE-level
|
|
557
|
+
// interactive approval channel. hasNoClient is intentionally `true` for
|
|
558
|
+
// chrome-extension turns (chrome-extension is not in INTERACTIVE_INTERFACES)
|
|
559
|
+
// and must not gate host_browser. Trust the per-capability check.
|
|
560
|
+
if (transport === "chrome-extension") {
|
|
561
|
+
return true;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// For transports that surface approvals over SSE (macos, backwards-compat
|
|
565
|
+
// fallback), deny when no client is present so the guardian auto-approve
|
|
566
|
+
// path cannot execute host commands unattended.
|
|
501
567
|
return !ctx.hasNoClient;
|
|
502
568
|
}
|
|
503
569
|
if (CLIENT_CAPABILITY_TOOL_NAMES.has(name)) {
|
|
@@ -13,6 +13,16 @@ export interface WorkspaceConversationContext {
|
|
|
13
13
|
workingDir: string;
|
|
14
14
|
workspaceTopLevelContext: string | null;
|
|
15
15
|
workspaceTopLevelDirty: boolean;
|
|
16
|
+
/**
|
|
17
|
+
* Client-reported host home directory, populated from host-proxy
|
|
18
|
+
* transport metadata (see `supportsHostProxy` / `HostProxyInterfaceId`).
|
|
19
|
+
* Used to render the `<workspace>` block correctly for platform-managed
|
|
20
|
+
* daemons where `os.homedir()` would return the container's home instead
|
|
21
|
+
* of the user's actual client-side home.
|
|
22
|
+
*/
|
|
23
|
+
hostHomeDir?: string;
|
|
24
|
+
/** Client-reported host username. See `hostHomeDir`. */
|
|
25
|
+
hostUsername?: string;
|
|
16
26
|
}
|
|
17
27
|
|
|
18
28
|
/** Refresh workspace top-level directory context if needed. */
|
|
@@ -36,6 +46,8 @@ export function refreshWorkspaceTopLevelContextIfNeeded(
|
|
|
36
46
|
conversationAttachmentsPath: currentConversationPath
|
|
37
47
|
? `${currentConversationPath}attachments/`
|
|
38
48
|
: null,
|
|
49
|
+
hostHomeDir: ctx.hostHomeDir,
|
|
50
|
+
hostUsername: ctx.hostUsername,
|
|
39
51
|
});
|
|
40
52
|
ctx.workspaceTopLevelDirty = false;
|
|
41
53
|
}
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import type { ResolvedSystemPrompt } from "../agent/loop.js";
|
|
19
19
|
import { AgentLoop } from "../agent/loop.js";
|
|
20
20
|
import type {
|
|
21
|
+
InterfaceId,
|
|
21
22
|
TurnChannelContext,
|
|
22
23
|
TurnInterfaceContext,
|
|
23
24
|
} from "../channels/types.js";
|
|
@@ -43,12 +44,20 @@ import {
|
|
|
43
44
|
import { registerToolTraceListener } from "../events/tool-trace-listener.js";
|
|
44
45
|
import { getHookManager } from "../hooks/manager.js";
|
|
45
46
|
import { resolveCanonicalGuardianRequest } from "../memory/canonical-guardian-store.js";
|
|
46
|
-
import {
|
|
47
|
+
import {
|
|
48
|
+
updateConversationContextWindow,
|
|
49
|
+
updateConversationHostAccess,
|
|
50
|
+
} from "../memory/conversation-crud.js";
|
|
47
51
|
import { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
|
|
48
52
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
49
53
|
import { SecretPrompter } from "../permissions/secret-prompter.js";
|
|
50
54
|
import { patternMatchesCandidate } from "../permissions/trust-store.js";
|
|
51
55
|
import type { UserDecision } from "../permissions/types.js";
|
|
56
|
+
import {
|
|
57
|
+
isConversationHostAccessDecision,
|
|
58
|
+
isConversationHostAccessEnabled,
|
|
59
|
+
isConversationHostAccessEnablePrompt,
|
|
60
|
+
} from "../permissions/v2-consent-policy.js";
|
|
52
61
|
import { resolvePersonaContext } from "../prompts/persona-resolver.js";
|
|
53
62
|
import { buildSystemPrompt } from "../prompts/system-prompt.js";
|
|
54
63
|
import type { Message } from "../providers/types.js";
|
|
@@ -104,6 +113,7 @@ import {
|
|
|
104
113
|
} from "./conversation-tool-setup.js";
|
|
105
114
|
import { refreshWorkspaceTopLevelContextIfNeeded as refreshWorkspaceImpl } from "./conversation-workspace.js";
|
|
106
115
|
import { HostBashProxy } from "./host-bash-proxy.js";
|
|
116
|
+
import { HostBrowserProxy } from "./host-browser-proxy.js";
|
|
107
117
|
import type { CuObservationResult } from "./host-cu-proxy.js";
|
|
108
118
|
import { HostCuProxy } from "./host-cu-proxy.js";
|
|
109
119
|
import { HostFileProxy } from "./host-file-proxy.js";
|
|
@@ -115,6 +125,7 @@ import type {
|
|
|
115
125
|
UserMessageAttachment,
|
|
116
126
|
} from "./message-protocol.js";
|
|
117
127
|
import type { ConversationTransportMetadata } from "./message-types/conversations.js";
|
|
128
|
+
import { isHostProxyTransport } from "./message-types/conversations.js";
|
|
118
129
|
import type {
|
|
119
130
|
AssistantActivityState,
|
|
120
131
|
ConfirmationStateChanged,
|
|
@@ -180,8 +191,22 @@ export class Conversation {
|
|
|
180
191
|
/** @internal */ taskRunId?: string;
|
|
181
192
|
/** @internal */ callSessionId?: string;
|
|
182
193
|
/** @internal */ hostBashProxy?: HostBashProxy;
|
|
194
|
+
/** @internal */ hostBrowserProxy?: HostBrowserProxy;
|
|
183
195
|
/** @internal */ hostCuProxy?: HostCuProxy;
|
|
184
196
|
/** @internal */ hostFileProxy?: HostFileProxy;
|
|
197
|
+
/**
|
|
198
|
+
* Optional override sender used by `restoreBrowserProxyAvailability` so
|
|
199
|
+
* non-SSE transports (e.g. chrome-extension, whose host_browser_request
|
|
200
|
+
* frames flow through the ChromeExtensionRegistry WebSocket rather than
|
|
201
|
+
* the SSE hub) can preserve their registry-routed sender across drain
|
|
202
|
+
* queue restores. When set, `restoreBrowserProxyAvailability()` uses this
|
|
203
|
+
* function instead of `sendToClient` so the drain-queue path doesn't
|
|
204
|
+
* clobber the chrome-extension sender with the SSE hub emitter.
|
|
205
|
+
*
|
|
206
|
+
* Populated by the POST /messages handler for chrome-extension turns and
|
|
207
|
+
* cleared when an unrelated interface takes over (see `updateClient`).
|
|
208
|
+
*/
|
|
209
|
+
/** @internal */ hostBrowserSenderOverride?: (msg: ServerMessage) => void;
|
|
185
210
|
/** @internal */ cesClient?: CesClient;
|
|
186
211
|
/** @internal */ readonly queue = new MessageQueue();
|
|
187
212
|
/** @internal */ currentActiveSurfaceId?: string;
|
|
@@ -246,8 +271,24 @@ export class Conversation {
|
|
|
246
271
|
}> = [];
|
|
247
272
|
/** @internal */ workspaceTopLevelContext: string | null = null;
|
|
248
273
|
/** @internal */ workspaceTopLevelDirty = true;
|
|
274
|
+
/**
|
|
275
|
+
* Host home directory reported by the client (e.g. macOS
|
|
276
|
+
* `NSHomeDirectory()`). Populated from `HostProxyTransportMetadata` when
|
|
277
|
+
* a message arrives from an interface that supports host-proxy tools
|
|
278
|
+
* (see `supportsHostProxy`). Consumed by the `<workspace>` block renderer
|
|
279
|
+
* so platform-managed (containerized) daemons show the user's actual
|
|
280
|
+
* client-side home dir instead of the container's `os.homedir()`.
|
|
281
|
+
* @internal
|
|
282
|
+
*/
|
|
283
|
+
hostHomeDir?: string;
|
|
284
|
+
/**
|
|
285
|
+
* Host username reported by the client (e.g. macOS `NSUserName()`).
|
|
286
|
+
* See `hostHomeDir`.
|
|
287
|
+
* @internal
|
|
288
|
+
*/
|
|
289
|
+
hostUsername?: string;
|
|
249
290
|
public readonly traceEmitter: TraceEmitter;
|
|
250
|
-
|
|
291
|
+
/** @internal */ hasSystemPromptOverride: boolean;
|
|
251
292
|
public memoryPolicy: ConversationMemoryPolicy;
|
|
252
293
|
/** @internal */ readonly graphMemory: ConversationGraphMemory;
|
|
253
294
|
/** @internal */ activeContextNodeIds?: string[];
|
|
@@ -387,7 +428,7 @@ export class Conversation {
|
|
|
387
428
|
_history: import("../providers/types.js").Message[],
|
|
388
429
|
): ResolvedSystemPrompt => {
|
|
389
430
|
const resolved = {
|
|
390
|
-
systemPrompt: hasSystemPromptOverride
|
|
431
|
+
systemPrompt: this.hasSystemPromptOverride
|
|
391
432
|
? systemPrompt
|
|
392
433
|
: (() => {
|
|
393
434
|
const persona = resolvePersonaContext(
|
|
@@ -501,6 +542,7 @@ export class Conversation {
|
|
|
501
542
|
this.traceEmitter.updateSender(sendToClient);
|
|
502
543
|
if (!opts?.skipProxySenderUpdate) {
|
|
503
544
|
this.hostBashProxy?.updateSender(sendToClient, !hasNoClient);
|
|
545
|
+
this.hostBrowserProxy?.updateSender(sendToClient, !hasNoClient);
|
|
504
546
|
this.hostCuProxy?.updateSender(sendToClient, !hasNoClient);
|
|
505
547
|
this.hostFileProxy?.updateSender(sendToClient, !hasNoClient);
|
|
506
548
|
}
|
|
@@ -527,6 +569,7 @@ export class Conversation {
|
|
|
527
569
|
/** Mark host proxies as unavailable so tool execution uses local fallback. */
|
|
528
570
|
clearProxyAvailability(): void {
|
|
529
571
|
this.hostBashProxy?.updateSender(this.sendToClient, false);
|
|
572
|
+
this.hostBrowserProxy?.updateSender(this.sendToClient, false);
|
|
530
573
|
this.hostCuProxy?.updateSender(this.sendToClient, false);
|
|
531
574
|
this.hostFileProxy?.updateSender(this.sendToClient, false);
|
|
532
575
|
}
|
|
@@ -535,11 +578,43 @@ export class Conversation {
|
|
|
535
578
|
restoreProxyAvailability(): void {
|
|
536
579
|
if (!this.hasNoClient) {
|
|
537
580
|
this.hostBashProxy?.updateSender(this.sendToClient, true);
|
|
581
|
+
this.hostBrowserProxy?.updateSender(this.sendToClient, true);
|
|
538
582
|
this.hostCuProxy?.updateSender(this.sendToClient, true);
|
|
539
583
|
this.hostFileProxy?.updateSender(this.sendToClient, true);
|
|
540
584
|
}
|
|
541
585
|
}
|
|
542
586
|
|
|
587
|
+
/**
|
|
588
|
+
* Restore host browser proxy availability only. Used for non-desktop
|
|
589
|
+
* interfaces (e.g. chrome-extension) that support host_browser but not
|
|
590
|
+
* the full desktop proxy set, so calling restoreProxyAvailability() would
|
|
591
|
+
* incorrectly re-enable bash/file/CU proxies that should stay disabled.
|
|
592
|
+
*
|
|
593
|
+
* Unlike `restoreProxyAvailability()`, this helper does NOT gate on
|
|
594
|
+
* `hasNoClient`. The chrome-extension interface is non-interactive (so
|
|
595
|
+
* `hasNoClient === true`), but it DOES have a connected client that can
|
|
596
|
+
* service `host_browser_request` events. Gating on `hasNoClient` would
|
|
597
|
+
* leave the just-constructed proxy unavailable and the only way to make
|
|
598
|
+
* it available would be to flip `hasNoClient` false, which would
|
|
599
|
+
* incorrectly enable host_bash/host_file/host_cu tool gating downstream.
|
|
600
|
+
*
|
|
601
|
+
* When `hostBrowserSenderOverride` is set, that function is used as the
|
|
602
|
+
* sender instead of `sendToClient`. This is required for the
|
|
603
|
+
* chrome-extension interface whose host_browser frames route through the
|
|
604
|
+
* ChromeExtensionRegistry WebSocket rather than the SSE hub: if the
|
|
605
|
+
* queue-drain path called this helper with `sendToClient`, the
|
|
606
|
+
* registry-routed sender established at turn-start would be clobbered by
|
|
607
|
+
* the SSE hub emitter and host_browser_request frames would stop
|
|
608
|
+
* reaching the extension.
|
|
609
|
+
*
|
|
610
|
+
* Callers must only invoke this when they know the current interface
|
|
611
|
+
* supports host_browser (see `supportsHostProxy(id, "host_browser")`).
|
|
612
|
+
*/
|
|
613
|
+
restoreBrowserProxyAvailability(): void {
|
|
614
|
+
const sender = this.hostBrowserSenderOverride ?? this.sendToClient;
|
|
615
|
+
this.hostBrowserProxy?.updateSender(sender, true);
|
|
616
|
+
}
|
|
617
|
+
|
|
543
618
|
setSubagentAllowedTools(tools: Set<string> | undefined): void {
|
|
544
619
|
this.subagentAllowedTools = tools;
|
|
545
620
|
}
|
|
@@ -548,6 +623,35 @@ export class Conversation {
|
|
|
548
623
|
this.isSubagent = value;
|
|
549
624
|
}
|
|
550
625
|
|
|
626
|
+
/**
|
|
627
|
+
* Prepend inherited parent messages into the in-memory message array so that
|
|
628
|
+
* the AgentLoop includes them in provider calls (enabling KV cache sharing).
|
|
629
|
+
*
|
|
630
|
+
* These messages are NOT persisted to the database — they exist only in
|
|
631
|
+
* memory. When the conversation is later read from DB via getMessages(),
|
|
632
|
+
* only the conversation's own persisted messages appear.
|
|
633
|
+
*
|
|
634
|
+
* Must be called before the first persistUserMessage() call — i.e. while
|
|
635
|
+
* `this.messages` is still empty.
|
|
636
|
+
*/
|
|
637
|
+
injectInheritedContext(messages: Message[]): void {
|
|
638
|
+
if (this.messages.length !== 0) {
|
|
639
|
+
throw new Error(
|
|
640
|
+
"injectInheritedContext must be called before any messages have been added",
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
this.messages = [...messages];
|
|
644
|
+
this.contextWindowManager.nonPersistedPrefixCount = messages.length;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/**
|
|
648
|
+
* Return the system prompt string set at construction time (or its override).
|
|
649
|
+
* Fork consumers use this to pass the parent's system prompt to the fork.
|
|
650
|
+
*/
|
|
651
|
+
getCurrentSystemPrompt(): string {
|
|
652
|
+
return this.systemPrompt;
|
|
653
|
+
}
|
|
654
|
+
|
|
551
655
|
isProcessing(): boolean {
|
|
552
656
|
return this.processing;
|
|
553
657
|
}
|
|
@@ -570,6 +674,7 @@ export class Conversation {
|
|
|
570
674
|
dispose(): void {
|
|
571
675
|
approvalOverrides.clearMode(this.conversationId);
|
|
572
676
|
this.hostBashProxy?.dispose();
|
|
677
|
+
this.hostBrowserProxy?.dispose();
|
|
573
678
|
this.hostCuProxy?.dispose();
|
|
574
679
|
this.hostFileProxy?.dispose();
|
|
575
680
|
// CES client is owned by DaemonServer — just drop the reference.
|
|
@@ -672,12 +777,35 @@ export class Conversation {
|
|
|
672
777
|
return;
|
|
673
778
|
}
|
|
674
779
|
|
|
780
|
+
const hostAccessEnablePrompt =
|
|
781
|
+
this.prompter.isHostAccessEnablePrompt(requestId) ||
|
|
782
|
+
isConversationHostAccessEnablePrompt(
|
|
783
|
+
pendingInteractions.get(requestId)?.confirmationDetails,
|
|
784
|
+
);
|
|
785
|
+
const effectiveDecision =
|
|
786
|
+
hostAccessEnablePrompt && !isConversationHostAccessDecision(decision)
|
|
787
|
+
? "deny"
|
|
788
|
+
: decision;
|
|
789
|
+
|
|
790
|
+
if (
|
|
791
|
+
hostAccessEnablePrompt &&
|
|
792
|
+
effectiveDecision === "allow" &&
|
|
793
|
+
!isConversationHostAccessEnabled(this.conversationId)
|
|
794
|
+
) {
|
|
795
|
+
updateConversationHostAccess(this.conversationId, true);
|
|
796
|
+
this.sendToClient({
|
|
797
|
+
type: "conversation_host_access_updated",
|
|
798
|
+
conversationId: this.conversationId,
|
|
799
|
+
hostAccess: true,
|
|
800
|
+
});
|
|
801
|
+
}
|
|
802
|
+
|
|
675
803
|
// Capture toolUseId before resolving (resolution deletes the pending entry)
|
|
676
804
|
const toolUseId = this.prompter.getToolUseId(requestId);
|
|
677
805
|
|
|
678
806
|
this.prompter.resolveConfirmation(
|
|
679
807
|
requestId,
|
|
680
|
-
|
|
808
|
+
effectiveDecision,
|
|
681
809
|
selectedPattern,
|
|
682
810
|
selectedScope,
|
|
683
811
|
decisionContext,
|
|
@@ -691,7 +819,7 @@ export class Conversation {
|
|
|
691
819
|
// so ALL callers (HTTP handlers, /v1/confirm, channel bridges) get
|
|
692
820
|
// consistent events without duplicating emission logic.
|
|
693
821
|
const resolvedState =
|
|
694
|
-
|
|
822
|
+
effectiveDecision === "deny" || effectiveDecision === "always_deny"
|
|
695
823
|
? ("denied" as const)
|
|
696
824
|
: ("approved" as const);
|
|
697
825
|
this.emitConfirmationStateChanged({
|
|
@@ -735,7 +863,12 @@ export class Conversation {
|
|
|
735
863
|
}
|
|
736
864
|
|
|
737
865
|
// Cascade to other pending confirmations that match this decision
|
|
738
|
-
this.cascadePendingApprovals(
|
|
866
|
+
this.cascadePendingApprovals(
|
|
867
|
+
requestId,
|
|
868
|
+
effectiveDecision,
|
|
869
|
+
selectedPattern,
|
|
870
|
+
hostAccessEnablePrompt,
|
|
871
|
+
);
|
|
739
872
|
}
|
|
740
873
|
|
|
741
874
|
/**
|
|
@@ -751,9 +884,14 @@ export class Conversation {
|
|
|
751
884
|
primaryRequestId: string,
|
|
752
885
|
decision: UserDecision,
|
|
753
886
|
selectedPattern?: string,
|
|
887
|
+
hostAccessEnablePrompt = false,
|
|
754
888
|
): void {
|
|
755
889
|
// Single-action decisions don't cascade
|
|
756
|
-
if (decision === "allow" || decision === "deny")
|
|
890
|
+
if (decision === "allow" || decision === "deny") {
|
|
891
|
+
if (!hostAccessEnablePrompt || decision !== "allow") {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
757
895
|
|
|
758
896
|
const pendingRequestIds = this.prompter.getPendingRequestIds();
|
|
759
897
|
if (pendingRequestIds.length === 0) return;
|
|
@@ -766,6 +904,28 @@ export class Conversation {
|
|
|
766
904
|
if (interaction.conversationId !== this.conversationId) continue;
|
|
767
905
|
if (interaction.kind !== "confirmation") continue;
|
|
768
906
|
|
|
907
|
+
if (
|
|
908
|
+
hostAccessEnablePrompt &&
|
|
909
|
+
(this.prompter.isHostAccessEnablePrompt(candidateId) ||
|
|
910
|
+
isConversationHostAccessEnablePrompt(interaction.confirmationDetails))
|
|
911
|
+
) {
|
|
912
|
+
// Enabling computer access is conversation-scoped, so sibling host
|
|
913
|
+
// prompts should resolve immediately once the first approval lands.
|
|
914
|
+
pendingInteractions.resolve(candidateId);
|
|
915
|
+
this.handleConfirmationResponse(
|
|
916
|
+
candidateId,
|
|
917
|
+
"allow",
|
|
918
|
+
undefined,
|
|
919
|
+
undefined,
|
|
920
|
+
undefined,
|
|
921
|
+
{
|
|
922
|
+
source: "system",
|
|
923
|
+
causedByRequestId: primaryRequestId,
|
|
924
|
+
},
|
|
925
|
+
);
|
|
926
|
+
continue;
|
|
927
|
+
}
|
|
928
|
+
|
|
769
929
|
const cascadeResult = this.shouldCascade(
|
|
770
930
|
decision,
|
|
771
931
|
selectedPattern,
|
|
@@ -868,9 +1028,23 @@ export class Conversation {
|
|
|
868
1028
|
this.hostBashProxy = proxy;
|
|
869
1029
|
}
|
|
870
1030
|
|
|
871
|
-
|
|
1031
|
+
resolveHostBrowser(
|
|
872
1032
|
requestId: string,
|
|
873
1033
|
response: { content: string; isError: boolean },
|
|
1034
|
+
): void {
|
|
1035
|
+
this.hostBrowserProxy?.resolve(requestId, response);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
setHostBrowserProxy(proxy: HostBrowserProxy | undefined): void {
|
|
1039
|
+
if (this.hostBrowserProxy && this.hostBrowserProxy !== proxy) {
|
|
1040
|
+
this.hostBrowserProxy.dispose();
|
|
1041
|
+
}
|
|
1042
|
+
this.hostBrowserProxy = proxy;
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
resolveHostFile(
|
|
1046
|
+
requestId: string,
|
|
1047
|
+
response: { content: string; isError: boolean; imageData?: string },
|
|
874
1048
|
): void {
|
|
875
1049
|
this.hostFileProxy?.resolve(requestId, response);
|
|
876
1050
|
}
|
|
@@ -984,6 +1158,40 @@ export class Conversation {
|
|
|
984
1158
|
this.transportHints = hints;
|
|
985
1159
|
}
|
|
986
1160
|
|
|
1161
|
+
/**
|
|
1162
|
+
* Apply client-reported host environment (home dir, username) from
|
|
1163
|
+
* transport metadata onto the conversation. Only interfaces whose
|
|
1164
|
+
* interfaceId passes `supportsHostProxy()` contribute values — all other
|
|
1165
|
+
* interfaces (CLI, channels, iOS, chrome-extension) clear any previously
|
|
1166
|
+
* stored values so a conversation reused across interfaces doesn't leak
|
|
1167
|
+
* stale paths into later `<workspace>` blocks.
|
|
1168
|
+
*
|
|
1169
|
+
* Gating on `supportsHostProxy` (rather than a specific interface name)
|
|
1170
|
+
* keeps this in lock-step with the capability set defined in
|
|
1171
|
+
* `HostProxyInterfaceId` — adding a new host-capable client only requires
|
|
1172
|
+
* extending those two, not touching this method.
|
|
1173
|
+
*
|
|
1174
|
+
* Invalidates the cached workspace top-level block when values change so
|
|
1175
|
+
* the next render picks up the new host env.
|
|
1176
|
+
*/
|
|
1177
|
+
applyHostEnvFromTransport(transport: ConversationTransportMetadata): void {
|
|
1178
|
+
const prevHomeDir = this.hostHomeDir;
|
|
1179
|
+
const prevUsername = this.hostUsername;
|
|
1180
|
+
if (isHostProxyTransport(transport)) {
|
|
1181
|
+
this.hostHomeDir = transport.hostHomeDir;
|
|
1182
|
+
this.hostUsername = transport.hostUsername;
|
|
1183
|
+
} else {
|
|
1184
|
+
this.hostHomeDir = undefined;
|
|
1185
|
+
this.hostUsername = undefined;
|
|
1186
|
+
}
|
|
1187
|
+
if (
|
|
1188
|
+
prevHomeDir !== this.hostHomeDir ||
|
|
1189
|
+
prevUsername !== this.hostUsername
|
|
1190
|
+
) {
|
|
1191
|
+
this.workspaceTopLevelDirty = true;
|
|
1192
|
+
}
|
|
1193
|
+
}
|
|
1194
|
+
|
|
987
1195
|
setAssistantId(assistantId: string | null): void {
|
|
988
1196
|
this.assistantId = assistantId ?? undefined;
|
|
989
1197
|
}
|
|
@@ -1026,6 +1234,16 @@ export class Conversation {
|
|
|
1026
1234
|
return this.currentTurnInterfaceContext;
|
|
1027
1235
|
}
|
|
1028
1236
|
|
|
1237
|
+
/**
|
|
1238
|
+
* Implements the `transportInterface` field of `SkillProjectionContext` so
|
|
1239
|
+
* that `isToolActiveForContext` can gate host tools by per-capability
|
|
1240
|
+
* `supportsHostProxy(transport, capability)`. Derived from the live turn
|
|
1241
|
+
* interface context so it tracks the connected client across turns.
|
|
1242
|
+
*/
|
|
1243
|
+
get transportInterface(): InterfaceId | undefined {
|
|
1244
|
+
return this.currentTurnInterfaceContext?.userMessageInterface;
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1029
1247
|
async persistUserMessage(
|
|
1030
1248
|
content: string,
|
|
1031
1249
|
attachments: UserMessageAttachment[],
|