@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
|
@@ -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";
|
|
@@ -114,6 +124,8 @@ import type {
|
|
|
114
124
|
UsageStats,
|
|
115
125
|
UserMessageAttachment,
|
|
116
126
|
} from "./message-protocol.js";
|
|
127
|
+
import type { ConversationTransportMetadata } from "./message-types/conversations.js";
|
|
128
|
+
import { isHostProxyTransport } from "./message-types/conversations.js";
|
|
117
129
|
import type {
|
|
118
130
|
AssistantActivityState,
|
|
119
131
|
ConfirmationStateChanged,
|
|
@@ -156,7 +168,6 @@ export class Conversation {
|
|
|
156
168
|
/** @internal */ sendToClient: (msg: ServerMessage) => void;
|
|
157
169
|
/** @internal */ eventBus = new EventBus<AssistantDomainEvents>();
|
|
158
170
|
/** @internal */ workingDir: string;
|
|
159
|
-
/** @internal */ sandboxOverride?: boolean;
|
|
160
171
|
/** @internal */ allowedToolNames?: Set<string>;
|
|
161
172
|
/** @internal */ toolsDisabledDepth = 0;
|
|
162
173
|
/** @internal */ preactivatedSkillIds?: string[];
|
|
@@ -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,13 +578,41 @@ 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
|
|
|
543
|
-
|
|
544
|
-
|
|
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);
|
|
545
616
|
}
|
|
546
617
|
|
|
547
618
|
setSubagentAllowedTools(tools: Set<string> | undefined): void {
|
|
@@ -552,6 +623,35 @@ export class Conversation {
|
|
|
552
623
|
this.isSubagent = value;
|
|
553
624
|
}
|
|
554
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
|
+
|
|
555
655
|
isProcessing(): boolean {
|
|
556
656
|
return this.processing;
|
|
557
657
|
}
|
|
@@ -574,6 +674,7 @@ export class Conversation {
|
|
|
574
674
|
dispose(): void {
|
|
575
675
|
approvalOverrides.clearMode(this.conversationId);
|
|
576
676
|
this.hostBashProxy?.dispose();
|
|
677
|
+
this.hostBrowserProxy?.dispose();
|
|
577
678
|
this.hostCuProxy?.dispose();
|
|
578
679
|
this.hostFileProxy?.dispose();
|
|
579
680
|
// CES client is owned by DaemonServer — just drop the reference.
|
|
@@ -609,6 +710,7 @@ export class Conversation {
|
|
|
609
710
|
metadata?: Record<string, unknown>,
|
|
610
711
|
options?: { isInteractive?: boolean },
|
|
611
712
|
displayContent?: string,
|
|
713
|
+
transport?: ConversationTransportMetadata,
|
|
612
714
|
): { queued: boolean; requestId: string; rejected?: boolean } {
|
|
613
715
|
return enqueueMessageImpl(
|
|
614
716
|
this,
|
|
@@ -621,6 +723,7 @@ export class Conversation {
|
|
|
621
723
|
metadata,
|
|
622
724
|
options,
|
|
623
725
|
displayContent,
|
|
726
|
+
transport,
|
|
624
727
|
);
|
|
625
728
|
}
|
|
626
729
|
|
|
@@ -674,12 +777,35 @@ export class Conversation {
|
|
|
674
777
|
return;
|
|
675
778
|
}
|
|
676
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
|
+
|
|
677
803
|
// Capture toolUseId before resolving (resolution deletes the pending entry)
|
|
678
804
|
const toolUseId = this.prompter.getToolUseId(requestId);
|
|
679
805
|
|
|
680
806
|
this.prompter.resolveConfirmation(
|
|
681
807
|
requestId,
|
|
682
|
-
|
|
808
|
+
effectiveDecision,
|
|
683
809
|
selectedPattern,
|
|
684
810
|
selectedScope,
|
|
685
811
|
decisionContext,
|
|
@@ -693,7 +819,7 @@ export class Conversation {
|
|
|
693
819
|
// so ALL callers (HTTP handlers, /v1/confirm, channel bridges) get
|
|
694
820
|
// consistent events without duplicating emission logic.
|
|
695
821
|
const resolvedState =
|
|
696
|
-
|
|
822
|
+
effectiveDecision === "deny" || effectiveDecision === "always_deny"
|
|
697
823
|
? ("denied" as const)
|
|
698
824
|
: ("approved" as const);
|
|
699
825
|
this.emitConfirmationStateChanged({
|
|
@@ -737,7 +863,12 @@ export class Conversation {
|
|
|
737
863
|
}
|
|
738
864
|
|
|
739
865
|
// Cascade to other pending confirmations that match this decision
|
|
740
|
-
this.cascadePendingApprovals(
|
|
866
|
+
this.cascadePendingApprovals(
|
|
867
|
+
requestId,
|
|
868
|
+
effectiveDecision,
|
|
869
|
+
selectedPattern,
|
|
870
|
+
hostAccessEnablePrompt,
|
|
871
|
+
);
|
|
741
872
|
}
|
|
742
873
|
|
|
743
874
|
/**
|
|
@@ -753,9 +884,14 @@ export class Conversation {
|
|
|
753
884
|
primaryRequestId: string,
|
|
754
885
|
decision: UserDecision,
|
|
755
886
|
selectedPattern?: string,
|
|
887
|
+
hostAccessEnablePrompt = false,
|
|
756
888
|
): void {
|
|
757
889
|
// Single-action decisions don't cascade
|
|
758
|
-
if (decision === "allow" || decision === "deny")
|
|
890
|
+
if (decision === "allow" || decision === "deny") {
|
|
891
|
+
if (!hostAccessEnablePrompt || decision !== "allow") {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
759
895
|
|
|
760
896
|
const pendingRequestIds = this.prompter.getPendingRequestIds();
|
|
761
897
|
if (pendingRequestIds.length === 0) return;
|
|
@@ -768,6 +904,28 @@ export class Conversation {
|
|
|
768
904
|
if (interaction.conversationId !== this.conversationId) continue;
|
|
769
905
|
if (interaction.kind !== "confirmation") continue;
|
|
770
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
|
+
|
|
771
929
|
const cascadeResult = this.shouldCascade(
|
|
772
930
|
decision,
|
|
773
931
|
selectedPattern,
|
|
@@ -870,9 +1028,23 @@ export class Conversation {
|
|
|
870
1028
|
this.hostBashProxy = proxy;
|
|
871
1029
|
}
|
|
872
1030
|
|
|
873
|
-
|
|
1031
|
+
resolveHostBrowser(
|
|
874
1032
|
requestId: string,
|
|
875
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 },
|
|
876
1048
|
): void {
|
|
877
1049
|
this.hostFileProxy?.resolve(requestId, response);
|
|
878
1050
|
}
|
|
@@ -986,6 +1158,40 @@ export class Conversation {
|
|
|
986
1158
|
this.transportHints = hints;
|
|
987
1159
|
}
|
|
988
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
|
+
|
|
989
1195
|
setAssistantId(assistantId: string | null): void {
|
|
990
1196
|
this.assistantId = assistantId ?? undefined;
|
|
991
1197
|
}
|
|
@@ -1028,6 +1234,16 @@ export class Conversation {
|
|
|
1028
1234
|
return this.currentTurnInterfaceContext;
|
|
1029
1235
|
}
|
|
1030
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
|
+
|
|
1031
1247
|
async persistUserMessage(
|
|
1032
1248
|
content: string,
|
|
1033
1249
|
attachments: UserMessageAttachment[],
|
|
@@ -18,14 +18,14 @@ export interface TemporalContextOptions {
|
|
|
18
18
|
userTimeZone?: string | null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
const
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
21
|
+
const WEEKDAY_LONG = [
|
|
22
|
+
"Sunday",
|
|
23
|
+
"Monday",
|
|
24
|
+
"Tuesday",
|
|
25
|
+
"Wednesday",
|
|
26
|
+
"Thursday",
|
|
27
|
+
"Friday",
|
|
28
|
+
"Saturday",
|
|
29
29
|
] as const;
|
|
30
30
|
const UTC_GMT_OFFSET_TOKEN_RE = /^(?:UTC|GMT)([+-])(\d{1,2})(?::?(\d{2}))?$/i;
|
|
31
31
|
|
|
@@ -295,7 +295,7 @@ function formatLocalDate(date: Date, timeZone: string): string {
|
|
|
295
295
|
* Uses the timezone resolution cascade:
|
|
296
296
|
* explicit override → configured user tz → profile user tz → host fallback.
|
|
297
297
|
*
|
|
298
|
-
* Returns format: `2026-04-02 (
|
|
298
|
+
* Returns format: `2026-04-02 (Thursday) 01:52:33 -05:00 (America/Chicago)`
|
|
299
299
|
*/
|
|
300
300
|
export function formatTurnTimestamp(
|
|
301
301
|
options: TemporalContextOptions = {},
|
|
@@ -322,7 +322,7 @@ export function formatTurnTimestamp(
|
|
|
322
322
|
|
|
323
323
|
const dateStr = formatLocalDate(now, timeZone);
|
|
324
324
|
const todayParts = localDateParts(now, timeZone);
|
|
325
|
-
const dayName =
|
|
325
|
+
const dayName = WEEKDAY_LONG[todayParts.weekday];
|
|
326
326
|
|
|
327
327
|
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
328
328
|
timeZone,
|
|
@@ -5,10 +5,11 @@ import { getWorkspacePromptPath } from "../util/platform.js";
|
|
|
5
5
|
/**
|
|
6
6
|
* The canned assistant response for the wake-up greeting on a fresh workspace.
|
|
7
7
|
* Warm, non-presumptuous greeting that communicates "I'm new," "I improve over
|
|
8
|
-
* time,"
|
|
8
|
+
* time," and invites the user to lead with whatever they want — a task, a
|
|
9
|
+
* question, or getting to know each other.
|
|
9
10
|
*/
|
|
10
11
|
export const CANNED_FIRST_GREETING =
|
|
11
|
-
"Hey
|
|
12
|
+
"Hey — I'm brand new. No name, no memories, no idea who you are yet. I'll get sharper the more we work together. What can I do for you?";
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Returns `true` when all of the following are true:
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { v4 as uuid } from "uuid";
|
|
2
2
|
|
|
3
|
-
import {
|
|
4
|
-
type InterfaceId,
|
|
5
|
-
parseChannelId,
|
|
6
|
-
parseInterfaceId,
|
|
7
|
-
} from "../../channels/types.js";
|
|
8
3
|
import { getConfig } from "../../config/loader.js";
|
|
9
4
|
import {
|
|
10
5
|
createCanonicalGuardianRequest,
|
|
@@ -13,28 +8,18 @@ import {
|
|
|
13
8
|
import {
|
|
14
9
|
batchSetDisplayOrders,
|
|
15
10
|
clearAll,
|
|
16
|
-
createConversation,
|
|
17
11
|
getConversation,
|
|
18
12
|
updateConversationTitle,
|
|
19
13
|
} from "../../memory/conversation-crud.js";
|
|
20
14
|
import { resolveConversationId } from "../../memory/conversation-key-store.js";
|
|
21
|
-
import {
|
|
22
|
-
GENERATING_TITLE,
|
|
23
|
-
queueGenerateConversationTitle,
|
|
24
|
-
UNTITLED_FALLBACK,
|
|
25
|
-
} from "../../memory/conversation-title-service.js";
|
|
26
15
|
import * as pendingInteractions from "../../runtime/pending-interactions.js";
|
|
27
16
|
import { redactSecrets } from "../../security/secret-scanner.js";
|
|
28
17
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
29
18
|
import { summarizeToolInput } from "../../tools/tool-input-summary.js";
|
|
30
19
|
import { truncate } from "../../util/truncate.js";
|
|
31
20
|
import type { Conversation } from "../conversation.js";
|
|
32
|
-
import { HostBashProxy } from "../host-bash-proxy.js";
|
|
33
|
-
import { HostCuProxy } from "../host-cu-proxy.js";
|
|
34
|
-
import { HostFileProxy } from "../host-file-proxy.js";
|
|
35
21
|
import type {
|
|
36
22
|
ConfirmationResponse,
|
|
37
|
-
ConversationCreateRequest,
|
|
38
23
|
ConversationRenameRequest,
|
|
39
24
|
ConversationSwitchRequest,
|
|
40
25
|
DeleteQueuedMessage,
|
|
@@ -131,6 +116,12 @@ export function makeEventSender(params: {
|
|
|
131
116
|
conversationId,
|
|
132
117
|
kind: "host_bash",
|
|
133
118
|
});
|
|
119
|
+
} else if (event.type === "host_browser_request") {
|
|
120
|
+
pendingInteractions.register(event.requestId, {
|
|
121
|
+
conversation,
|
|
122
|
+
conversationId,
|
|
123
|
+
kind: "host_browser",
|
|
124
|
+
});
|
|
134
125
|
} else if (event.type === "host_file_request") {
|
|
135
126
|
pendingInteractions.register(event.requestId, {
|
|
136
127
|
conversation,
|
|
@@ -226,130 +217,6 @@ export function clearAllConversations(ctx: HandlerContext): number {
|
|
|
226
217
|
return cleared;
|
|
227
218
|
}
|
|
228
219
|
|
|
229
|
-
export async function handleConversationCreate(
|
|
230
|
-
msg: ConversationCreateRequest,
|
|
231
|
-
ctx: HandlerContext,
|
|
232
|
-
): Promise<void> {
|
|
233
|
-
const conversationType = normalizeConversationType(msg.conversationType);
|
|
234
|
-
const title =
|
|
235
|
-
msg.title ?? (msg.initialMessage ? GENERATING_TITLE : "New Conversation");
|
|
236
|
-
const conversation = createConversation({
|
|
237
|
-
title,
|
|
238
|
-
conversationType,
|
|
239
|
-
});
|
|
240
|
-
const conversationObj = await ctx.getOrCreateConversation(conversation.id, {
|
|
241
|
-
systemPromptOverride: msg.systemPromptOverride,
|
|
242
|
-
maxResponseTokens: msg.maxResponseTokens,
|
|
243
|
-
transport: msg.transport,
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// Pre-activate skills before sending conversation_info so they're available
|
|
247
|
-
// for the initial message processing.
|
|
248
|
-
if (msg.preactivatedSkillIds?.length) {
|
|
249
|
-
conversationObj.setPreactivatedSkillIds(msg.preactivatedSkillIds);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
ctx.send({
|
|
253
|
-
type: "conversation_info",
|
|
254
|
-
conversationId: conversation.id,
|
|
255
|
-
title: conversation.title ?? "New Conversation",
|
|
256
|
-
...(msg.correlationId ? { correlationId: msg.correlationId } : {}),
|
|
257
|
-
conversationType: normalizeConversationType(conversation.conversationType),
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
// Auto-send the initial message if provided, kick-starting the skill.
|
|
261
|
-
if (msg.initialMessage) {
|
|
262
|
-
// Queue title generation eagerly — some processMessage paths (guardian
|
|
263
|
-
// replies, unknown slash commands) bypass the agent loop entirely, so
|
|
264
|
-
// we can't rely on the agent loop's early title generation alone.
|
|
265
|
-
// The agent loop also queues title generation, but isReplaceableTitle
|
|
266
|
-
// prevents double-writes since the first to complete sets a real title.
|
|
267
|
-
if (title === GENERATING_TITLE) {
|
|
268
|
-
queueGenerateConversationTitle({
|
|
269
|
-
conversationId: conversation.id,
|
|
270
|
-
context: { origin: "local" },
|
|
271
|
-
userMessage: msg.initialMessage,
|
|
272
|
-
onTitleUpdated: (newTitle) => {
|
|
273
|
-
ctx.send({
|
|
274
|
-
type: "conversation_title_updated",
|
|
275
|
-
conversationId: conversation.id,
|
|
276
|
-
title: newTitle,
|
|
277
|
-
});
|
|
278
|
-
},
|
|
279
|
-
});
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const requestId = uuid();
|
|
283
|
-
const transportChannel =
|
|
284
|
-
parseChannelId(msg.transport?.channelId) ?? "vellum";
|
|
285
|
-
const sendEvent = makeEventSender({
|
|
286
|
-
ctx,
|
|
287
|
-
conversation: conversationObj,
|
|
288
|
-
conversationId: conversation.id,
|
|
289
|
-
sourceChannel: transportChannel,
|
|
290
|
-
});
|
|
291
|
-
conversationObj.setTurnChannelContext({
|
|
292
|
-
userMessageChannel: transportChannel,
|
|
293
|
-
assistantMessageChannel: transportChannel,
|
|
294
|
-
});
|
|
295
|
-
const transportInterface: InterfaceId =
|
|
296
|
-
parseInterfaceId(msg.transport?.interfaceId) ?? "vellum";
|
|
297
|
-
conversationObj.setTurnInterfaceContext({
|
|
298
|
-
userMessageInterface: transportInterface,
|
|
299
|
-
assistantMessageInterface: transportInterface,
|
|
300
|
-
});
|
|
301
|
-
// Only create the host bash proxy for desktop client interfaces that can
|
|
302
|
-
// execute commands on the user's machine. Set before updateClient so
|
|
303
|
-
// updateClient's call to hostBashProxy.updateSender targets the new proxy.
|
|
304
|
-
if (transportInterface === "macos") {
|
|
305
|
-
const proxy = new HostBashProxy(sendEvent, (requestId) => {
|
|
306
|
-
pendingInteractions.resolve(requestId);
|
|
307
|
-
});
|
|
308
|
-
conversationObj.setHostBashProxy(proxy);
|
|
309
|
-
const fileProxy = new HostFileProxy(sendEvent, (requestId) => {
|
|
310
|
-
pendingInteractions.resolve(requestId);
|
|
311
|
-
});
|
|
312
|
-
conversationObj.setHostFileProxy(fileProxy);
|
|
313
|
-
const cuProxy = new HostCuProxy(sendEvent, (requestId) => {
|
|
314
|
-
pendingInteractions.resolve(requestId);
|
|
315
|
-
});
|
|
316
|
-
conversationObj.setHostCuProxy(cuProxy);
|
|
317
|
-
conversationObj.addPreactivatedSkillId("computer-use");
|
|
318
|
-
}
|
|
319
|
-
conversationObj.updateClient(sendEvent, false);
|
|
320
|
-
conversationObj
|
|
321
|
-
.processMessage(msg.initialMessage, [], sendEvent, requestId)
|
|
322
|
-
.catch((err) => {
|
|
323
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
324
|
-
log.error(
|
|
325
|
-
{ err, conversationId: conversation.id },
|
|
326
|
-
"Error processing initial message",
|
|
327
|
-
);
|
|
328
|
-
ctx.send({
|
|
329
|
-
type: "error",
|
|
330
|
-
message: `Failed to process initial message: ${message}`,
|
|
331
|
-
});
|
|
332
|
-
|
|
333
|
-
// Replace stuck loading placeholder with a stable fallback title
|
|
334
|
-
// if title generation hasn't already completed or been renamed.
|
|
335
|
-
try {
|
|
336
|
-
const current = getConversation(conversation.id);
|
|
337
|
-
if (current && current.title === GENERATING_TITLE) {
|
|
338
|
-
const fallback = UNTITLED_FALLBACK;
|
|
339
|
-
updateConversationTitle(conversation.id, fallback);
|
|
340
|
-
ctx.send({
|
|
341
|
-
type: "conversation_title_updated",
|
|
342
|
-
conversationId: conversation.id,
|
|
343
|
-
title: fallback,
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
} catch {
|
|
347
|
-
// Best-effort fallback
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
220
|
/**
|
|
354
221
|
* Switch to an existing conversation. Returns conversation info on success,
|
|
355
222
|
* or throws/returns an error result when the conversation is not found.
|
|
@@ -361,6 +228,7 @@ export async function switchConversation(
|
|
|
361
228
|
conversationId: string;
|
|
362
229
|
title: string;
|
|
363
230
|
conversationType: ReturnType<typeof normalizeConversationType>;
|
|
231
|
+
hostAccess: boolean;
|
|
364
232
|
} | null> {
|
|
365
233
|
const conversation = getConversation(conversationId);
|
|
366
234
|
if (!conversation) {
|
|
@@ -383,6 +251,7 @@ export async function switchConversation(
|
|
|
383
251
|
conversationId: conversation.id,
|
|
384
252
|
title: conversation.title ?? "Untitled",
|
|
385
253
|
conversationType: normalizeConversationType(conversation.conversationType),
|
|
254
|
+
hostAccess: conversation.hostAccess === 1,
|
|
386
255
|
};
|
|
387
256
|
}
|
|
388
257
|
|
|
@@ -404,6 +273,7 @@ export async function handleConversationSwitch(
|
|
|
404
273
|
conversationId: result.conversationId,
|
|
405
274
|
title: result.title,
|
|
406
275
|
conversationType: result.conversationType,
|
|
276
|
+
hostAccess: result.hostAccess,
|
|
407
277
|
});
|
|
408
278
|
}
|
|
409
279
|
|