@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
|
@@ -14,6 +14,8 @@ interface PendingRequest {
|
|
|
14
14
|
reject: (err: Error) => void;
|
|
15
15
|
timer: ReturnType<typeof setTimeout>;
|
|
16
16
|
timeoutSec: number;
|
|
17
|
+
/** Detach the abort listener from the caller's signal. No-op when no signal was passed. */
|
|
18
|
+
detachAbort: () => void;
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
export class HostBashProxy {
|
|
@@ -60,8 +62,14 @@ export class HostBashProxy {
|
|
|
60
62
|
const timeoutSec = input.timeout_seconds ?? shellMaxTimeoutSec;
|
|
61
63
|
// Proxy timeout: slightly after client-side timeout, but before executor's outer timeout
|
|
62
64
|
const proxyTimeoutSec = timeoutSec + 3;
|
|
65
|
+
|
|
66
|
+
// Declared up-front so onAbort (defined before detachAbort is assigned)
|
|
67
|
+
// can close over a stable reference once it's wired below.
|
|
68
|
+
let detachAbort: () => void = () => {};
|
|
69
|
+
|
|
63
70
|
const timer = setTimeout(() => {
|
|
64
71
|
this.pending.delete(requestId);
|
|
72
|
+
detachAbort();
|
|
65
73
|
this.onInternalResolve?.(requestId);
|
|
66
74
|
log.warn(
|
|
67
75
|
{ requestId, command: input.command },
|
|
@@ -78,13 +86,14 @@ export class HostBashProxy {
|
|
|
78
86
|
);
|
|
79
87
|
}, proxyTimeoutSec * 1000);
|
|
80
88
|
|
|
81
|
-
this.pending.set(requestId, { resolve, reject, timer, timeoutSec });
|
|
82
|
-
|
|
83
89
|
if (signal) {
|
|
84
90
|
const onAbort = () => {
|
|
85
91
|
if (this.pending.has(requestId)) {
|
|
86
92
|
clearTimeout(timer);
|
|
87
93
|
this.pending.delete(requestId);
|
|
94
|
+
// Abort fired — nothing to detach, but call the no-op for symmetry
|
|
95
|
+
// so callers can rely on detachAbort being idempotent.
|
|
96
|
+
detachAbort();
|
|
88
97
|
this.onInternalResolve?.(requestId);
|
|
89
98
|
try {
|
|
90
99
|
this.sendToClient({
|
|
@@ -98,19 +107,43 @@ export class HostBashProxy {
|
|
|
98
107
|
}
|
|
99
108
|
};
|
|
100
109
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
110
|
+
detachAbort = () => signal.removeEventListener("abort", onAbort);
|
|
101
111
|
}
|
|
102
112
|
|
|
103
|
-
this.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
this.pending.set(requestId, {
|
|
114
|
+
resolve,
|
|
115
|
+
reject,
|
|
116
|
+
timer,
|
|
117
|
+
timeoutSec,
|
|
118
|
+
detachAbort,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
this.sendToClient({
|
|
123
|
+
type: "host_bash_request",
|
|
124
|
+
requestId,
|
|
125
|
+
conversationId,
|
|
126
|
+
command: input.command,
|
|
127
|
+
working_dir: input.working_dir,
|
|
128
|
+
timeout_seconds: input.timeout_seconds,
|
|
129
|
+
...(input.env && Object.keys(input.env).length > 0
|
|
130
|
+
? { env: input.env }
|
|
131
|
+
: {}),
|
|
132
|
+
} as ServerMessage);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
// Sender threw synchronously (e.g. client transport error during
|
|
135
|
+
// event emission). Clean up pending state and timer so we don't
|
|
136
|
+
// leak an in-flight entry that nothing will ever resolve.
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
this.pending.delete(requestId);
|
|
139
|
+
detachAbort();
|
|
140
|
+
this.onInternalResolve?.(requestId);
|
|
141
|
+
log.warn(
|
|
142
|
+
{ requestId, command: input.command, err },
|
|
143
|
+
"Host bash proxy send failed",
|
|
144
|
+
);
|
|
145
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
146
|
+
}
|
|
114
147
|
});
|
|
115
148
|
}
|
|
116
149
|
|
|
@@ -129,6 +162,7 @@ export class HostBashProxy {
|
|
|
129
162
|
return;
|
|
130
163
|
}
|
|
131
164
|
clearTimeout(entry.timer);
|
|
165
|
+
entry.detachAbort();
|
|
132
166
|
this.pending.delete(requestId);
|
|
133
167
|
const result = formatShellOutput(
|
|
134
168
|
response.stdout,
|
|
@@ -151,6 +185,7 @@ export class HostBashProxy {
|
|
|
151
185
|
dispose(): void {
|
|
152
186
|
for (const [requestId, entry] of this.pending) {
|
|
153
187
|
clearTimeout(entry.timer);
|
|
188
|
+
entry.detachAbort();
|
|
154
189
|
this.onInternalResolve?.(requestId);
|
|
155
190
|
try {
|
|
156
191
|
this.sendToClient({
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { v4 as uuid } from "uuid";
|
|
2
|
+
|
|
3
|
+
import type { ToolExecutionResult } from "../tools/types.js";
|
|
4
|
+
import { AssistantError, ErrorCode } from "../util/errors.js";
|
|
5
|
+
import { getLogger } from "../util/logger.js";
|
|
6
|
+
import type { ServerMessage } from "./message-protocol.js";
|
|
7
|
+
import type { HostBrowserRequest } from "./message-types/host-browser.js";
|
|
8
|
+
|
|
9
|
+
/** Distributive omit that preserves union variant fields. */
|
|
10
|
+
type DistributiveOmit<T, K extends PropertyKey> = T extends unknown
|
|
11
|
+
? Omit<T, K>
|
|
12
|
+
: never;
|
|
13
|
+
|
|
14
|
+
/** Clean input type for callers — transport envelope fields are added by the proxy. */
|
|
15
|
+
export type HostBrowserInput = DistributiveOmit<
|
|
16
|
+
HostBrowserRequest,
|
|
17
|
+
"type" | "requestId" | "conversationId"
|
|
18
|
+
>;
|
|
19
|
+
|
|
20
|
+
const log = getLogger("host-browser-proxy");
|
|
21
|
+
|
|
22
|
+
interface PendingRequest {
|
|
23
|
+
resolve: (result: ToolExecutionResult) => void;
|
|
24
|
+
reject: (err: Error) => void;
|
|
25
|
+
timer: ReturnType<typeof setTimeout>;
|
|
26
|
+
/** Detach the abort listener from the caller's signal. No-op when no signal was passed. */
|
|
27
|
+
detachAbort: () => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class HostBrowserProxy {
|
|
31
|
+
private pending = new Map<string, PendingRequest>();
|
|
32
|
+
private sendToClient: (msg: ServerMessage) => void;
|
|
33
|
+
private onInternalResolve?: (requestId: string) => void;
|
|
34
|
+
private clientConnected = false;
|
|
35
|
+
|
|
36
|
+
constructor(
|
|
37
|
+
sendToClient: (msg: ServerMessage) => void,
|
|
38
|
+
onInternalResolve?: (requestId: string) => void,
|
|
39
|
+
) {
|
|
40
|
+
this.sendToClient = sendToClient;
|
|
41
|
+
this.onInternalResolve = onInternalResolve;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
updateSender(
|
|
45
|
+
sendToClient: (msg: ServerMessage) => void,
|
|
46
|
+
clientConnected: boolean,
|
|
47
|
+
): void {
|
|
48
|
+
this.sendToClient = sendToClient;
|
|
49
|
+
this.clientConnected = clientConnected;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
request(
|
|
53
|
+
input: HostBrowserInput,
|
|
54
|
+
conversationId: string,
|
|
55
|
+
signal?: AbortSignal,
|
|
56
|
+
): Promise<ToolExecutionResult> {
|
|
57
|
+
if (signal?.aborted) {
|
|
58
|
+
return Promise.resolve({ content: "Aborted", isError: true });
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const requestId = uuid();
|
|
62
|
+
|
|
63
|
+
return new Promise<ToolExecutionResult>((resolve, reject) => {
|
|
64
|
+
// CDP operations should be fast — 30 second default timeout matches host_file.
|
|
65
|
+
const timeoutSec = input.timeout_seconds ?? 30;
|
|
66
|
+
|
|
67
|
+
// Declared up-front so onAbort (defined before detachAbort is assigned)
|
|
68
|
+
// can close over a stable reference once it's wired below.
|
|
69
|
+
let detachAbort: () => void = () => {};
|
|
70
|
+
|
|
71
|
+
const timer = setTimeout(() => {
|
|
72
|
+
this.pending.delete(requestId);
|
|
73
|
+
detachAbort();
|
|
74
|
+
this.onInternalResolve?.(requestId);
|
|
75
|
+
log.warn(
|
|
76
|
+
{ requestId, cdpMethod: input.cdpMethod },
|
|
77
|
+
"Host browser proxy request timed out",
|
|
78
|
+
);
|
|
79
|
+
resolve({
|
|
80
|
+
content: "Host browser proxy timed out waiting for client response",
|
|
81
|
+
isError: true,
|
|
82
|
+
});
|
|
83
|
+
}, timeoutSec * 1000);
|
|
84
|
+
|
|
85
|
+
if (signal) {
|
|
86
|
+
const onAbort = () => {
|
|
87
|
+
if (this.pending.has(requestId)) {
|
|
88
|
+
clearTimeout(timer);
|
|
89
|
+
this.pending.delete(requestId);
|
|
90
|
+
// Abort fired — nothing to detach, but call the no-op for symmetry
|
|
91
|
+
// so callers can rely on detachAbort being idempotent.
|
|
92
|
+
detachAbort();
|
|
93
|
+
this.onInternalResolve?.(requestId);
|
|
94
|
+
try {
|
|
95
|
+
this.sendToClient({
|
|
96
|
+
type: "host_browser_cancel",
|
|
97
|
+
requestId,
|
|
98
|
+
} as ServerMessage);
|
|
99
|
+
} catch {
|
|
100
|
+
// Best-effort cancel notification — connection may already be closed.
|
|
101
|
+
}
|
|
102
|
+
resolve({ content: "Aborted", isError: true });
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
106
|
+
detachAbort = () => signal.removeEventListener("abort", onAbort);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.pending.set(requestId, { resolve, reject, timer, detachAbort });
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
this.sendToClient({
|
|
113
|
+
...input,
|
|
114
|
+
type: "host_browser_request",
|
|
115
|
+
requestId,
|
|
116
|
+
conversationId,
|
|
117
|
+
} as ServerMessage);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
// Sender threw synchronously (e.g. client transport error during
|
|
120
|
+
// event emission). Clean up pending state and timer so we don't
|
|
121
|
+
// leak an in-flight entry that nothing will ever resolve.
|
|
122
|
+
clearTimeout(timer);
|
|
123
|
+
this.pending.delete(requestId);
|
|
124
|
+
detachAbort();
|
|
125
|
+
this.onInternalResolve?.(requestId);
|
|
126
|
+
log.warn(
|
|
127
|
+
{ requestId, cdpMethod: input.cdpMethod, err },
|
|
128
|
+
"Host browser proxy send failed",
|
|
129
|
+
);
|
|
130
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
resolve(
|
|
136
|
+
requestId: string,
|
|
137
|
+
response: { content: string; isError: boolean },
|
|
138
|
+
): void {
|
|
139
|
+
const entry = this.pending.get(requestId);
|
|
140
|
+
if (!entry) {
|
|
141
|
+
// Benign race, not an error. A late result frame with no matching
|
|
142
|
+
// pending entry means one of:
|
|
143
|
+
// - the proxy-side setTimeout has already resolved the caller;
|
|
144
|
+
// - the caller's AbortSignal fired and the entry was torn down;
|
|
145
|
+
// - a duplicate result frame was delivered (e.g. retry after a
|
|
146
|
+
// transient WS drop).
|
|
147
|
+
// Log at debug so operators don't chase false-positive "timeout"
|
|
148
|
+
// alerts on what is actually a cleanly-handled race.
|
|
149
|
+
log.debug(
|
|
150
|
+
{ requestId },
|
|
151
|
+
"Ignoring host_browser_result for unknown or already-resolved request",
|
|
152
|
+
);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
clearTimeout(entry.timer);
|
|
156
|
+
entry.detachAbort();
|
|
157
|
+
this.pending.delete(requestId);
|
|
158
|
+
entry.resolve({ content: response.content, isError: response.isError });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
hasPendingRequest(requestId: string): boolean {
|
|
162
|
+
return this.pending.has(requestId);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
isAvailable(): boolean {
|
|
166
|
+
return this.clientConnected;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
dispose(): void {
|
|
170
|
+
for (const [requestId, entry] of this.pending) {
|
|
171
|
+
clearTimeout(entry.timer);
|
|
172
|
+
entry.detachAbort();
|
|
173
|
+
this.onInternalResolve?.(requestId);
|
|
174
|
+
try {
|
|
175
|
+
this.sendToClient({
|
|
176
|
+
type: "host_browser_cancel",
|
|
177
|
+
requestId,
|
|
178
|
+
} as ServerMessage);
|
|
179
|
+
} catch {
|
|
180
|
+
// Best-effort cancel notification — connection may already be closed.
|
|
181
|
+
}
|
|
182
|
+
entry.reject(
|
|
183
|
+
new AssistantError(
|
|
184
|
+
"Host browser proxy disposed",
|
|
185
|
+
ErrorCode.INTERNAL_ERROR,
|
|
186
|
+
),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
this.pending.clear();
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -57,6 +57,8 @@ interface PendingRequest {
|
|
|
57
57
|
resolve: (result: ToolExecutionResult) => void;
|
|
58
58
|
reject: (err: Error) => void;
|
|
59
59
|
timer: ReturnType<typeof setTimeout>;
|
|
60
|
+
/** Detach the abort listener from the caller's signal. No-op when no signal was passed. */
|
|
61
|
+
detachAbort: () => void;
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
// ---------------------------------------------------------------------------
|
|
@@ -152,8 +154,13 @@ export class HostCuProxy {
|
|
|
152
154
|
const requestId = uuid();
|
|
153
155
|
|
|
154
156
|
return new Promise<ToolExecutionResult>((resolve, reject) => {
|
|
157
|
+
// Declared up-front so onAbort (defined before detachAbort is assigned)
|
|
158
|
+
// can close over a stable reference once it's wired below.
|
|
159
|
+
let detachAbort: () => void = () => {};
|
|
160
|
+
|
|
155
161
|
const timer = setTimeout(() => {
|
|
156
162
|
this.pending.delete(requestId);
|
|
163
|
+
detachAbort();
|
|
157
164
|
this.onInternalResolve?.(requestId);
|
|
158
165
|
log.warn({ requestId, toolName }, "Host CU proxy request timed out");
|
|
159
166
|
resolve({
|
|
@@ -162,13 +169,14 @@ export class HostCuProxy {
|
|
|
162
169
|
});
|
|
163
170
|
}, REQUEST_TIMEOUT_SEC * 1000);
|
|
164
171
|
|
|
165
|
-
this.pending.set(requestId, { resolve, reject, timer });
|
|
166
|
-
|
|
167
172
|
if (signal) {
|
|
168
173
|
const onAbort = () => {
|
|
169
174
|
if (this.pending.has(requestId)) {
|
|
170
175
|
clearTimeout(timer);
|
|
171
176
|
this.pending.delete(requestId);
|
|
177
|
+
// Abort fired — nothing to detach, but call the no-op for symmetry
|
|
178
|
+
// so callers can rely on detachAbort being idempotent.
|
|
179
|
+
detachAbort();
|
|
172
180
|
this.onInternalResolve?.(requestId);
|
|
173
181
|
try {
|
|
174
182
|
this.sendToClient({
|
|
@@ -182,17 +190,32 @@ export class HostCuProxy {
|
|
|
182
190
|
}
|
|
183
191
|
};
|
|
184
192
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
193
|
+
detachAbort = () => signal.removeEventListener("abort", onAbort);
|
|
185
194
|
}
|
|
186
195
|
|
|
187
|
-
this.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
+
this.pending.set(requestId, { resolve, reject, timer, detachAbort });
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
this.sendToClient({
|
|
200
|
+
type: "host_cu_request",
|
|
201
|
+
requestId,
|
|
202
|
+
conversationId,
|
|
203
|
+
toolName,
|
|
204
|
+
input,
|
|
205
|
+
stepNumber,
|
|
206
|
+
reasoning,
|
|
207
|
+
} as ServerMessage);
|
|
208
|
+
} catch (err) {
|
|
209
|
+
// Sender threw synchronously (e.g. client transport error during
|
|
210
|
+
// event emission). Clean up pending state and timer so we don't
|
|
211
|
+
// leak an in-flight entry that nothing will ever resolve.
|
|
212
|
+
clearTimeout(timer);
|
|
213
|
+
this.pending.delete(requestId);
|
|
214
|
+
detachAbort();
|
|
215
|
+
this.onInternalResolve?.(requestId);
|
|
216
|
+
log.warn({ requestId, toolName, err }, "Host CU proxy send failed");
|
|
217
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
218
|
+
}
|
|
196
219
|
});
|
|
197
220
|
}
|
|
198
221
|
|
|
@@ -203,6 +226,7 @@ export class HostCuProxy {
|
|
|
203
226
|
return;
|
|
204
227
|
}
|
|
205
228
|
clearTimeout(entry.timer);
|
|
229
|
+
entry.detachAbort();
|
|
206
230
|
this.pending.delete(requestId);
|
|
207
231
|
|
|
208
232
|
// Capture pre-update state so formatObservation sees the correct previous AX tree
|
|
@@ -388,6 +412,7 @@ export class HostCuProxy {
|
|
|
388
412
|
dispose(): void {
|
|
389
413
|
for (const [requestId, entry] of this.pending) {
|
|
390
414
|
clearTimeout(entry.timer);
|
|
415
|
+
entry.detachAbort();
|
|
391
416
|
this.onInternalResolve?.(requestId);
|
|
392
417
|
try {
|
|
393
418
|
this.sendToClient({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { v4 as uuid } from "uuid";
|
|
2
2
|
|
|
3
|
+
import { readImageBase64 } from "../tools/shared/filesystem/image-read.js";
|
|
3
4
|
import type { ToolExecutionResult } from "../tools/types.js";
|
|
4
5
|
import { AssistantError, ErrorCode } from "../util/errors.js";
|
|
5
6
|
import { getLogger } from "../util/logger.js";
|
|
@@ -23,6 +24,10 @@ interface PendingRequest {
|
|
|
23
24
|
resolve: (result: ToolExecutionResult) => void;
|
|
24
25
|
reject: (err: Error) => void;
|
|
25
26
|
timer: ReturnType<typeof setTimeout>;
|
|
27
|
+
operation: HostFileInput["operation"];
|
|
28
|
+
path: string;
|
|
29
|
+
/** Detach the abort listener from the caller's signal. No-op when no signal was passed. */
|
|
30
|
+
detachAbort: () => void;
|
|
26
31
|
}
|
|
27
32
|
|
|
28
33
|
export class HostFileProxy {
|
|
@@ -61,8 +66,14 @@ export class HostFileProxy {
|
|
|
61
66
|
return new Promise<ToolExecutionResult>((resolve, reject) => {
|
|
62
67
|
// File operations should be fast — 30 second timeout.
|
|
63
68
|
const timeoutSec = 30;
|
|
69
|
+
|
|
70
|
+
// Declared up-front so onAbort (defined before detachAbort is assigned)
|
|
71
|
+
// can close over a stable reference once it's wired below.
|
|
72
|
+
let detachAbort: () => void = () => {};
|
|
73
|
+
|
|
64
74
|
const timer = setTimeout(() => {
|
|
65
75
|
this.pending.delete(requestId);
|
|
76
|
+
detachAbort();
|
|
66
77
|
this.onInternalResolve?.(requestId);
|
|
67
78
|
log.warn(
|
|
68
79
|
{ requestId, operation: input.operation },
|
|
@@ -74,13 +85,14 @@ export class HostFileProxy {
|
|
|
74
85
|
});
|
|
75
86
|
}, timeoutSec * 1000);
|
|
76
87
|
|
|
77
|
-
this.pending.set(requestId, { resolve, reject, timer });
|
|
78
|
-
|
|
79
88
|
if (signal) {
|
|
80
89
|
const onAbort = () => {
|
|
81
90
|
if (this.pending.has(requestId)) {
|
|
82
91
|
clearTimeout(timer);
|
|
83
92
|
this.pending.delete(requestId);
|
|
93
|
+
// Abort fired — nothing to detach, but call the no-op for symmetry
|
|
94
|
+
// so callers can rely on detachAbort being idempotent.
|
|
95
|
+
detachAbort();
|
|
84
96
|
this.onInternalResolve?.(requestId);
|
|
85
97
|
try {
|
|
86
98
|
this.sendToClient({
|
|
@@ -94,20 +106,45 @@ export class HostFileProxy {
|
|
|
94
106
|
}
|
|
95
107
|
};
|
|
96
108
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
109
|
+
detachAbort = () => signal.removeEventListener("abort", onAbort);
|
|
97
110
|
}
|
|
98
111
|
|
|
99
|
-
this.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
112
|
+
this.pending.set(requestId, {
|
|
113
|
+
resolve,
|
|
114
|
+
reject,
|
|
115
|
+
timer,
|
|
116
|
+
operation: input.operation,
|
|
117
|
+
path: input.path,
|
|
118
|
+
detachAbort,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
this.sendToClient({
|
|
123
|
+
...input,
|
|
124
|
+
type: "host_file_request",
|
|
125
|
+
requestId,
|
|
126
|
+
conversationId,
|
|
127
|
+
} as ServerMessage);
|
|
128
|
+
} catch (err) {
|
|
129
|
+
// Sender threw synchronously (e.g. client transport error during
|
|
130
|
+
// event emission). Clean up pending state and timer so we don't
|
|
131
|
+
// leak an in-flight entry that nothing will ever resolve.
|
|
132
|
+
clearTimeout(timer);
|
|
133
|
+
this.pending.delete(requestId);
|
|
134
|
+
detachAbort();
|
|
135
|
+
this.onInternalResolve?.(requestId);
|
|
136
|
+
log.warn(
|
|
137
|
+
{ requestId, operation: input.operation, err },
|
|
138
|
+
"Host file proxy send failed",
|
|
139
|
+
);
|
|
140
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
141
|
+
}
|
|
105
142
|
});
|
|
106
143
|
}
|
|
107
144
|
|
|
108
145
|
resolve(
|
|
109
146
|
requestId: string,
|
|
110
|
-
response: { content: string; isError: boolean },
|
|
147
|
+
response: { content: string; isError: boolean; imageData?: string },
|
|
111
148
|
): void {
|
|
112
149
|
const entry = this.pending.get(requestId);
|
|
113
150
|
if (!entry) {
|
|
@@ -115,7 +152,17 @@ export class HostFileProxy {
|
|
|
115
152
|
return;
|
|
116
153
|
}
|
|
117
154
|
clearTimeout(entry.timer);
|
|
155
|
+
entry.detachAbort();
|
|
118
156
|
this.pending.delete(requestId);
|
|
157
|
+
if (
|
|
158
|
+
entry.operation === "read" &&
|
|
159
|
+
!response.isError &&
|
|
160
|
+
typeof response.imageData === "string" &&
|
|
161
|
+
response.imageData.length > 0
|
|
162
|
+
) {
|
|
163
|
+
entry.resolve(readImageBase64(response.imageData, entry.path));
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
119
166
|
entry.resolve({ content: response.content, isError: response.isError });
|
|
120
167
|
}
|
|
121
168
|
|
|
@@ -130,6 +177,7 @@ export class HostFileProxy {
|
|
|
130
177
|
dispose(): void {
|
|
131
178
|
for (const [requestId, entry] of this.pending) {
|
|
132
179
|
clearTimeout(entry.timer);
|
|
180
|
+
entry.detachAbort();
|
|
133
181
|
this.onInternalResolve?.(requestId);
|
|
134
182
|
try {
|
|
135
183
|
this.sendToClient({
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -75,6 +75,11 @@ import {
|
|
|
75
75
|
mintPairingBearerToken,
|
|
76
76
|
resolveSigningKey,
|
|
77
77
|
} from "../runtime/auth/token-service.js";
|
|
78
|
+
import {
|
|
79
|
+
initCapabilityTokenSecret,
|
|
80
|
+
loadOrCreateCapabilityTokenSecret,
|
|
81
|
+
writeDaemonTokenFallback,
|
|
82
|
+
} from "../runtime/capability-tokens.js";
|
|
78
83
|
import { ensureVellumGuardianBinding } from "../runtime/guardian-vellum-migration.js";
|
|
79
84
|
import { RuntimeHttpServer } from "../runtime/http-server.js";
|
|
80
85
|
import { startScheduler } from "../schedule/scheduler.js";
|
|
@@ -270,6 +275,20 @@ export async function runDaemon(): Promise<void> {
|
|
|
270
275
|
const signingKey = resolveSigningKey();
|
|
271
276
|
initAuthSigningKey(signingKey);
|
|
272
277
|
|
|
278
|
+
// Load (or generate + persist) the capability-token HMAC secret used
|
|
279
|
+
// to mint scoped tokens for the chrome extension pair endpoint.
|
|
280
|
+
// Wrapped in try/catch so a disk failure here never blocks startup —
|
|
281
|
+
// tokens can still be minted using a lazy on-demand load inside the
|
|
282
|
+
// capability-tokens module.
|
|
283
|
+
try {
|
|
284
|
+
initCapabilityTokenSecret(loadOrCreateCapabilityTokenSecret());
|
|
285
|
+
} catch (err) {
|
|
286
|
+
log.warn(
|
|
287
|
+
{ err },
|
|
288
|
+
"Failed to pre-load capability token secret — continuing startup (lazy load will handle subsequent calls)",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
273
292
|
// Pre-populate the feature flag cache from the gateway so all
|
|
274
293
|
// subsequent sync isAssistantFeatureFlagEnabled() calls have data.
|
|
275
294
|
// Fired non-blocking so a slow or unreachable gateway doesn't delay
|
|
@@ -432,8 +451,11 @@ export async function runDaemon(): Promise<void> {
|
|
|
432
451
|
|
|
433
452
|
// Ensure a vellum guardian binding exists so the identity system works
|
|
434
453
|
// without requiring a manual bootstrap step.
|
|
454
|
+
let localGuardianPrincipalId = "local";
|
|
435
455
|
try {
|
|
436
|
-
ensureVellumGuardianBinding(
|
|
456
|
+
localGuardianPrincipalId = ensureVellumGuardianBinding(
|
|
457
|
+
DAEMON_INTERNAL_ASSISTANT_ID,
|
|
458
|
+
);
|
|
437
459
|
} catch (err) {
|
|
438
460
|
log.warn(
|
|
439
461
|
{ err },
|
|
@@ -441,6 +463,19 @@ export async function runDaemon(): Promise<void> {
|
|
|
441
463
|
);
|
|
442
464
|
}
|
|
443
465
|
|
|
466
|
+
// Write a dev-only fallback capability token to `~/.vellum/daemon-token`
|
|
467
|
+
// so developers can manually pair the chrome extension without the
|
|
468
|
+
// native messaging helper. Production pairing goes through
|
|
469
|
+
// `POST /v1/browser-extension-pair` via the native helper.
|
|
470
|
+
try {
|
|
471
|
+
writeDaemonTokenFallback(localGuardianPrincipalId);
|
|
472
|
+
} catch (err) {
|
|
473
|
+
log.warn(
|
|
474
|
+
{ err },
|
|
475
|
+
"Failed to write dev daemon-token fallback — continuing startup",
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
444
479
|
try {
|
|
445
480
|
syncUpdateBulletinOnStartup();
|
|
446
481
|
} catch (err) {
|
|
@@ -589,8 +624,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
589
624
|
}
|
|
590
625
|
}
|
|
591
626
|
|
|
592
|
-
await initializeProvidersAndTools(config);
|
|
593
|
-
|
|
594
627
|
// Start the DaemonServer (conversation manager) before Qdrant so HTTP
|
|
595
628
|
// routes can begin accepting requests while Qdrant initializes.
|
|
596
629
|
log.info("Daemon startup: starting DaemonServer");
|
|
@@ -745,12 +778,17 @@ export async function runDaemon(): Promise<void> {
|
|
|
745
778
|
conversationId,
|
|
746
779
|
message,
|
|
747
780
|
undefined,
|
|
748
|
-
options
|
|
781
|
+
options
|
|
749
782
|
? {
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
783
|
+
...(options.trustClass
|
|
784
|
+
? {
|
|
785
|
+
trustContext: {
|
|
786
|
+
sourceChannel: "vellum",
|
|
787
|
+
trustClass: options.trustClass,
|
|
788
|
+
},
|
|
789
|
+
}
|
|
790
|
+
: {}),
|
|
791
|
+
...(options.taskRunId ? { taskRunId: options.taskRunId } : {}),
|
|
754
792
|
}
|
|
755
793
|
: undefined,
|
|
756
794
|
);
|
|
@@ -1170,6 +1208,21 @@ export async function runDaemon(): Promise<void> {
|
|
|
1170
1208
|
runtimeHttp = null;
|
|
1171
1209
|
}
|
|
1172
1210
|
|
|
1211
|
+
// Initialize providers and tools after the HTTP server is listening so
|
|
1212
|
+
// health-check and pairing requests can be served immediately. Wrapped in
|
|
1213
|
+
// its own try/catch so a failure here doesn't tear down the running HTTP
|
|
1214
|
+
// server (DaemonServer.start() already calls initializeProviders internally
|
|
1215
|
+
// and tools are resolved lazily at conversation creation time).
|
|
1216
|
+
try {
|
|
1217
|
+
log.info("Daemon startup: initializing providers and tools");
|
|
1218
|
+
await initializeProvidersAndTools(config);
|
|
1219
|
+
} catch (err) {
|
|
1220
|
+
log.warn(
|
|
1221
|
+
{ err },
|
|
1222
|
+
"Provider/tool initialization failed — continuing with degraded functionality",
|
|
1223
|
+
);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1173
1226
|
writePid(process.pid);
|
|
1174
1227
|
log.info({ pid: process.pid }, "Daemon started");
|
|
1175
1228
|
|
|
@@ -1198,10 +1251,11 @@ export async function runDaemon(): Promise<void> {
|
|
|
1198
1251
|
if (!runtimeManager.isReady()) {
|
|
1199
1252
|
log.info("Downloading embedding runtime in background...");
|
|
1200
1253
|
await runtimeManager.ensureInstalled();
|
|
1201
|
-
// Reset the
|
|
1202
|
-
|
|
1254
|
+
// Reset the sticky local-backend failure flag so auto mode retries
|
|
1255
|
+
// local embeddings without evicting a worker that may already be live.
|
|
1256
|
+
const { resetLocalEmbeddingFailureState } =
|
|
1203
1257
|
await import("../memory/embedding-backend.js");
|
|
1204
|
-
|
|
1258
|
+
resetLocalEmbeddingFailureState();
|
|
1205
1259
|
log.info("Embedding runtime download complete");
|
|
1206
1260
|
}
|
|
1207
1261
|
} catch (err) {
|
|
@@ -23,6 +23,7 @@ export * from "./message-types/diagnostics.js";
|
|
|
23
23
|
export * from "./message-types/documents.js";
|
|
24
24
|
export * from "./message-types/guardian-actions.js";
|
|
25
25
|
export * from "./message-types/host-bash.js";
|
|
26
|
+
export * from "./message-types/host-browser.js";
|
|
26
27
|
export * from "./message-types/host-cu.js";
|
|
27
28
|
export * from "./message-types/host-file.js";
|
|
28
29
|
export * from "./message-types/inbox.js";
|
|
@@ -77,6 +78,10 @@ import type {
|
|
|
77
78
|
_GuardianActionsServerMessages,
|
|
78
79
|
} from "./message-types/guardian-actions.js";
|
|
79
80
|
import type { _HostBashServerMessages } from "./message-types/host-bash.js";
|
|
81
|
+
import type {
|
|
82
|
+
_HostBrowserClientMessages,
|
|
83
|
+
_HostBrowserServerMessages,
|
|
84
|
+
} from "./message-types/host-browser.js";
|
|
80
85
|
import type { _HostCuServerMessages } from "./message-types/host-cu.js";
|
|
81
86
|
import type { _HostFileServerMessages } from "./message-types/host-file.js";
|
|
82
87
|
import type {
|
|
@@ -157,6 +162,7 @@ export type ClientMessage =
|
|
|
157
162
|
| _ContactsClientMessages
|
|
158
163
|
| _WorkItemsClientMessages
|
|
159
164
|
| _BrowserClientMessages
|
|
165
|
+
| _HostBrowserClientMessages
|
|
160
166
|
| _SubagentsClientMessages
|
|
161
167
|
| _DocumentsClientMessages
|
|
162
168
|
| _GuardianActionsClientMessages
|
|
@@ -186,6 +192,7 @@ export type ServerMessage =
|
|
|
186
192
|
| _DocumentsServerMessages
|
|
187
193
|
| _GuardianActionsServerMessages
|
|
188
194
|
| _HostBashServerMessages
|
|
195
|
+
| _HostBrowserServerMessages
|
|
189
196
|
| _HostCuServerMessages
|
|
190
197
|
| _HostFileServerMessages
|
|
191
198
|
| _MemoryServerMessages
|