@vellumai/assistant 0.6.2 → 0.6.4
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/ARCHITECTURE.md +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +41 -49
- package/bunfig.toml +3 -0
- package/docs/architecture/memory.md +1 -1
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +1111 -86
- package/package.json +40 -42
- package/scripts/generate-openapi.ts +0 -2
- package/scripts/test.sh +73 -18
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- 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__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +240 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +1000 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -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 +1248 -224
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-overflow-approval.test.ts +16 -1
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
- package/src/__tests__/conversation-agent-loop.test.ts +99 -3
- 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 +290 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +12 -4
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +946 -62
- package/src/__tests__/conversation-routes-disk-view.test.ts +275 -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 +324 -46
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- 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-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +6 -3
- package/src/__tests__/credential-vault-unit.test.ts +383 -7
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +42 -18
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +2 -0
- package/src/__tests__/gemini-provider.test.ts +66 -2
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +738 -359
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +528 -49
- package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
- package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +145 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -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 +423 -0
- package/src/__tests__/host-cu-proxy.test.ts +166 -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__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +67 -8
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +109 -7
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/oauth-apps-routes.test.ts +18 -12
- package/src/__tests__/oauth-cli.test.ts +709 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +52 -14
- package/src/__tests__/oauth-store.test.ts +1465 -176
- package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
- package/src/__tests__/onboarding-template-contract.test.ts +81 -70
- package/src/__tests__/openai-provider.test.ts +178 -2
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- 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 +32 -3
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- 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__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +8 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +801 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +576 -16
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- 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 +184 -27
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +25 -5
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -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__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +55 -9
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- 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/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +69 -3
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +3 -4
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +273 -16
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -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 +32 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -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/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +215 -36
- package/src/cli/commands/oauth/shared.ts +7 -6
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -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 +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -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/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +5 -2
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +54 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +9 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -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/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +38 -0
- package/src/config/env.ts +49 -4
- package/src/config/feature-flag-registry.json +85 -14
- package/src/config/loader.ts +82 -13
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +81 -15
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +112 -0
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +52 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -1
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +177 -0
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +61 -10
- package/src/credential-execution/approval-bridge.ts +49 -15
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +195 -0
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/context-overflow-approval.ts +5 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
- package/src/daemon/conversation-agent-loop.ts +153 -42
- package/src/daemon/conversation-attachments.ts +40 -0
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +622 -13
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +128 -36
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +131 -40
- package/src/daemon/conversation-tool-setup.ts +99 -8
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +292 -16
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +13 -141
- package/src/daemon/handlers/shared.ts +80 -0
- package/src/daemon/handlers/skills.ts +483 -44
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +192 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +179 -28
- package/src/daemon/message-protocol.ts +13 -0
- package/src/daemon/message-types/conversations.ts +89 -14
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +19 -5
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +26 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +439 -14
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/transport-hints.ts +5 -24
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +30 -20
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/mcp/client.ts +59 -24
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +31 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +122 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -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 +18 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -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/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +67 -40
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +137 -60
- 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/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/oauth.ts +21 -13
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +26 -9
- package/src/oauth/byo-connection.ts +10 -8
- package/src/oauth/connect-orchestrator.ts +25 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +22 -18
- package/src/oauth/connection.ts +3 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +223 -100
- package/src/oauth/platform-connection.test.ts +101 -3
- package/src/oauth/platform-connection.ts +56 -35
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +133 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +76 -38
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -105
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -60
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +10 -1
- package/src/runtime/AGENTS.md +65 -0
- package/src/runtime/__tests__/agent-wake.test.ts +831 -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/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -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 +48 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +33 -9
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +368 -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 +815 -75
- package/src/runtime/http-types.ts +6 -2
- 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 +198 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +7 -0
- package/src/runtime/migrations/migration-wizard.ts +23 -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 +96 -1
- package/src/runtime/migrations/vbundle-importer.ts +89 -5
- package/src/runtime/pending-interactions.ts +18 -13
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +556 -0
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -141
- package/src/runtime/routes/conversation-management-routes.ts +223 -0
- package/src/runtime/routes/conversation-routes.ts +598 -103
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +268 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +262 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/log-export-routes.ts +42 -22
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +122 -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 +31 -102
- package/src/runtime/routes/skills-routes.ts +128 -9
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +38 -9
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- 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/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +57 -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 +68 -29
- package/src/security/secure-keys.ts +143 -27
- package/src/security/token-manager.ts +31 -10
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +554 -0
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +169 -40
- 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/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +1787 -342
- package/src/tools/browser/browser-manager.ts +81 -12
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- 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 +1263 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -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 +1007 -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 +744 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
- package/src/tools/browser/cdp-client/errors.ts +49 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
- package/src/tools/browser/cdp-client/factory.ts +914 -0
- package/src/tools/browser/cdp-client/index.ts +28 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +120 -0
- package/src/tools/credentials/vault.ts +35 -6
- 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/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- 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/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- 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 +15 -0
- package/src/tools/terminal/shell.ts +36 -20
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +19 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +63 -24
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +31 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- 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/cli/commands/browser-relay.ts +0 -536
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -5,17 +5,26 @@
|
|
|
5
5
|
* configured port (default: 7821).
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
8
|
+
import {
|
|
9
|
+
existsSync,
|
|
10
|
+
mkdirSync,
|
|
11
|
+
readFileSync,
|
|
12
|
+
renameSync,
|
|
13
|
+
unlinkSync,
|
|
14
|
+
writeFileSync,
|
|
15
|
+
} from "node:fs";
|
|
16
|
+
import { dirname, resolve } from "node:path";
|
|
10
17
|
|
|
11
18
|
import type { ServerWebSocket } from "bun";
|
|
12
19
|
|
|
13
|
-
import type { BrowserRelayWebSocketData } from "../browser-extension-relay/server.js";
|
|
14
|
-
import { extensionRelayServer } from "../browser-extension-relay/server.js";
|
|
15
20
|
import {
|
|
16
21
|
startGuardianActionSweep,
|
|
17
22
|
stopGuardianActionSweep,
|
|
18
23
|
} from "../calls/guardian-action-sweep.js";
|
|
24
|
+
import {
|
|
25
|
+
activeMediaStreamSessions,
|
|
26
|
+
MediaStreamCallSession,
|
|
27
|
+
} from "../calls/media-stream-server.js";
|
|
19
28
|
import type { RelayWebSocketData } from "../calls/relay-server.js";
|
|
20
29
|
import {
|
|
21
30
|
activeRelayConnections,
|
|
@@ -32,6 +41,7 @@ import {
|
|
|
32
41
|
hasUngatedHttpAuthDisabled,
|
|
33
42
|
isHttpAuthDisabled,
|
|
34
43
|
} from "../config/env.js";
|
|
44
|
+
import { getConfig } from "../config/loader.js";
|
|
35
45
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
36
46
|
import { PairingStore } from "../daemon/pairing-store.js";
|
|
37
47
|
import {
|
|
@@ -57,23 +67,35 @@ import {
|
|
|
57
67
|
import type { ExternalConversationBinding } from "../memory/external-conversation-store.js";
|
|
58
68
|
import * as externalConversationStore from "../memory/external-conversation-store.js";
|
|
59
69
|
import { listGroups } from "../memory/group-crud.js";
|
|
70
|
+
import { resolveStreamingTranscriber } from "../providers/speech-to-text/resolve.js";
|
|
60
71
|
import {
|
|
61
72
|
consumeCallback,
|
|
62
73
|
consumeCallbackError,
|
|
63
74
|
} from "../security/oauth-callback-registry.js";
|
|
75
|
+
import {
|
|
76
|
+
activeSttStreamSessions,
|
|
77
|
+
SttStreamSession,
|
|
78
|
+
} from "../stt/stt-stream-session.js";
|
|
64
79
|
import { UserError } from "../util/errors.js";
|
|
65
80
|
import { getLogger } from "../util/logger.js";
|
|
81
|
+
import { getRuntimePortFilePath } from "../util/platform.js";
|
|
66
82
|
import { buildAssistantEvent } from "./assistant-event.js";
|
|
67
83
|
import { assistantEventHub } from "./assistant-event-hub.js";
|
|
68
84
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "./assistant-scope.js";
|
|
69
85
|
// Auth
|
|
70
|
-
import {
|
|
86
|
+
import {
|
|
87
|
+
authenticateHostBrowserResultRequest,
|
|
88
|
+
authenticateRequest,
|
|
89
|
+
} from "./auth/middleware.js";
|
|
90
|
+
import { parseSub } from "./auth/subject.js";
|
|
71
91
|
import {
|
|
72
92
|
mintDaemonDeliveryToken,
|
|
73
93
|
mintUiPageToken,
|
|
74
94
|
verifyToken,
|
|
75
95
|
} from "./auth/token-service.js";
|
|
96
|
+
import { verifyHostBrowserCapability } from "./capability-tokens.js";
|
|
76
97
|
import { sweepFailedEvents } from "./channel-retry-sweep.js";
|
|
98
|
+
import { getChromeExtensionRegistry } from "./chrome-extension-registry.js";
|
|
77
99
|
import { httpError } from "./http-errors.js";
|
|
78
100
|
import type { RouteDefinition } from "./http-router.js";
|
|
79
101
|
import { HttpRouter } from "./http-router.js";
|
|
@@ -109,7 +131,9 @@ import { approvalRouteDefinitions } from "./routes/approval-routes.js";
|
|
|
109
131
|
import { attachmentRouteDefinitions } from "./routes/attachment-routes.js";
|
|
110
132
|
import { handleGetAudio } from "./routes/audio-routes.js";
|
|
111
133
|
import { avatarRouteDefinitions } from "./routes/avatar-routes.js";
|
|
134
|
+
import { backupRouteDefinitions } from "./routes/backup-routes.js";
|
|
112
135
|
import { brainGraphRouteDefinitions } from "./routes/brain-graph-routes.js";
|
|
136
|
+
import { handleBrowserExtensionPair } from "./routes/browser-extension-pair-routes.js";
|
|
113
137
|
import { btwRouteDefinitions } from "./routes/btw-routes.js";
|
|
114
138
|
import { callRouteDefinitions } from "./routes/call-routes.js";
|
|
115
139
|
import {
|
|
@@ -140,13 +164,22 @@ import { debugRouteDefinitions } from "./routes/debug-routes.js";
|
|
|
140
164
|
import { diagnosticsRouteDefinitions } from "./routes/diagnostics-routes.js";
|
|
141
165
|
import { documentRouteDefinitions } from "./routes/documents-routes.js";
|
|
142
166
|
import { eventsRouteDefinitions } from "./routes/events-routes.js";
|
|
167
|
+
import { filingRouteDefinitions } from "./routes/filing-routes.js";
|
|
143
168
|
import { globalSearchRouteDefinitions } from "./routes/global-search-routes.js";
|
|
144
169
|
import { groupRouteDefinitions } from "./routes/group-routes.js";
|
|
145
170
|
import { guardianActionRouteDefinitions } from "./routes/guardian-action-routes.js";
|
|
146
171
|
import { handleGuardianBootstrap } from "./routes/guardian-bootstrap-routes.js";
|
|
147
172
|
import { handleGuardianRefresh } from "./routes/guardian-refresh-routes.js";
|
|
148
173
|
import { heartbeatRouteDefinitions } from "./routes/heartbeat-routes.js";
|
|
174
|
+
import { homeFeedRouteDefinitions } from "./routes/home-feed-routes.js";
|
|
175
|
+
import { homeStateRouteDefinitions } from "./routes/home-state-routes.js";
|
|
149
176
|
import { hostBashRouteDefinitions } from "./routes/host-bash-routes.js";
|
|
177
|
+
import {
|
|
178
|
+
hostBrowserRouteDefinitions,
|
|
179
|
+
resolveHostBrowserEvent,
|
|
180
|
+
resolveHostBrowserResultByRequestId,
|
|
181
|
+
resolveHostBrowserSessionInvalidated,
|
|
182
|
+
} from "./routes/host-browser-routes.js";
|
|
150
183
|
import { hostCuRouteDefinitions } from "./routes/host-cu-routes.js";
|
|
151
184
|
import { hostFileRouteDefinitions } from "./routes/host-file-routes.js";
|
|
152
185
|
import {
|
|
@@ -179,6 +212,7 @@ import { scheduleRouteDefinitions } from "./routes/schedule-routes.js";
|
|
|
179
212
|
import { secretRouteDefinitions } from "./routes/secret-routes.js";
|
|
180
213
|
import { settingsRouteDefinitions } from "./routes/settings-routes.js";
|
|
181
214
|
import { skillRouteDefinitions } from "./routes/skills-routes.js";
|
|
215
|
+
import { sttRouteDefinitions } from "./routes/stt-routes.js";
|
|
182
216
|
import { subagentRouteDefinitions } from "./routes/subagents-routes.js";
|
|
183
217
|
import { surfaceActionRouteDefinitions } from "./routes/surface-action-routes.js";
|
|
184
218
|
import { surfaceContentRouteDefinitions } from "./routes/surface-content-routes.js";
|
|
@@ -193,6 +227,8 @@ import { watchRouteDefinitions } from "./routes/watch-routes.js";
|
|
|
193
227
|
import { workItemRouteDefinitions } from "./routes/work-items-routes.js";
|
|
194
228
|
import { workspaceCommitRouteDefinitions } from "./routes/workspace-commit-routes.js";
|
|
195
229
|
import { workspaceRouteDefinitions } from "./routes/workspace-routes.js";
|
|
230
|
+
import { setAnalysisDeps } from "./services/analyze-deps-singleton.js";
|
|
231
|
+
import { matchSkillRoute } from "./skill-route-registry.js";
|
|
196
232
|
|
|
197
233
|
// Re-export for consumers
|
|
198
234
|
export { isPrivateAddress } from "./middleware/auth.js";
|
|
@@ -228,6 +264,74 @@ const DEFAULT_HOSTNAME = "127.0.0.1";
|
|
|
228
264
|
/** Global hard cap on request body size (512 MB — accommodates large .vbundle backup imports). */
|
|
229
265
|
const MAX_REQUEST_BODY_BYTES = 512 * 1024 * 1024;
|
|
230
266
|
|
|
267
|
+
/**
|
|
268
|
+
* WebSocket data attached to `/v1/browser-relay` connections. The route
|
|
269
|
+
* is used exclusively by the chrome-extension CDP proxy — outbound
|
|
270
|
+
* `host_browser_request` frames are pushed through the
|
|
271
|
+
* {@link ChromeExtensionRegistry}, and inbound `host_browser_result`
|
|
272
|
+
* frames are dispatched through
|
|
273
|
+
* `resolveHostBrowserResultByRequestId`. The extension may also submit
|
|
274
|
+
* results via `POST /v1/host-browser-result` (both transports resolve
|
|
275
|
+
* through the same core function).
|
|
276
|
+
*/
|
|
277
|
+
interface BrowserRelayWebSocketData {
|
|
278
|
+
wsType: "browser-relay";
|
|
279
|
+
connectionId: string;
|
|
280
|
+
/**
|
|
281
|
+
* Guardian identity derived from the JWT claims at WebSocket upgrade
|
|
282
|
+
* time. Used by the ChromeExtensionRegistry to route
|
|
283
|
+
* host_browser_request frames to the correct extension. Undefined when
|
|
284
|
+
* HTTP auth is disabled (dev bypass) or when the token's sub cannot be
|
|
285
|
+
* parsed into an actor principal.
|
|
286
|
+
*/
|
|
287
|
+
guardianId?: string;
|
|
288
|
+
/**
|
|
289
|
+
* Stable per-extension-install identifier supplied by the client on
|
|
290
|
+
* the WebSocket handshake (via the `clientInstanceId` query param or
|
|
291
|
+
* the `x-client-instance-id` header). Plumbed into the
|
|
292
|
+
* ChromeExtensionRegistry so multiple parallel installs for the same
|
|
293
|
+
* guardian (e.g. two Chrome profiles, two desktops) don't evict each
|
|
294
|
+
* other on register/unregister. Undefined on older extension builds
|
|
295
|
+
* — the registry synthesizes a connection-scoped fallback key in
|
|
296
|
+
* that case for backwards-compatible single-instance semantics.
|
|
297
|
+
*/
|
|
298
|
+
clientInstanceId?: string;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* WebSocket data attached to `/v1/calls/media-stream` connections.
|
|
303
|
+
* The `wsType` discriminator routes frames to the media-stream call
|
|
304
|
+
* session instead of the ConversationRelay or browser-relay handlers.
|
|
305
|
+
*/
|
|
306
|
+
interface MediaStreamWebSocketData {
|
|
307
|
+
wsType: "media-stream";
|
|
308
|
+
callSessionId: string;
|
|
309
|
+
/** Bound at open time so the close handler tears down the exact session
|
|
310
|
+
* that owns *this* socket, avoiding races with reconnects. */
|
|
311
|
+
session?: MediaStreamCallSession;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* WebSocket data attached to `/v1/stt/stream` connections.
|
|
316
|
+
* The `wsType` discriminator routes frames to the STT streaming
|
|
317
|
+
* session orchestrator instead of the other WebSocket handlers.
|
|
318
|
+
*
|
|
319
|
+
* `provider` is optional compatibility metadata from the client/gateway.
|
|
320
|
+
* The runtime is config-authoritative — it always resolves the streaming
|
|
321
|
+
* transcriber from `services.stt.provider` in the assistant config.
|
|
322
|
+
*/
|
|
323
|
+
interface SttStreamWebSocketData {
|
|
324
|
+
wsType: "stt-stream";
|
|
325
|
+
/** Optional requested provider — metadata only; runtime uses config. */
|
|
326
|
+
provider?: string;
|
|
327
|
+
mimeType: string;
|
|
328
|
+
sampleRate?: number;
|
|
329
|
+
/** The session ID for tracking in the active sessions registry. */
|
|
330
|
+
sessionId: string;
|
|
331
|
+
/** Bound at open time so the close handler tears down the exact session. */
|
|
332
|
+
session?: SttStreamSession;
|
|
333
|
+
}
|
|
334
|
+
|
|
231
335
|
export class RuntimeHttpServer {
|
|
232
336
|
private server: ReturnType<typeof Bun.serve> | null = null;
|
|
233
337
|
private port: number;
|
|
@@ -257,6 +361,7 @@ export class RuntimeHttpServer {
|
|
|
257
361
|
private getCesClient?: RuntimeHttpServerOptions["getCesClient"];
|
|
258
362
|
private onProviderCredentialsChanged?: RuntimeHttpServerOptions["onProviderCredentialsChanged"];
|
|
259
363
|
private getHeartbeatService?: RuntimeHttpServerOptions["getHeartbeatService"];
|
|
364
|
+
private getFilingService?: RuntimeHttpServerOptions["getFilingService"];
|
|
260
365
|
private router: HttpRouter;
|
|
261
366
|
|
|
262
367
|
constructor(options: RuntimeHttpServerOptions = {}) {
|
|
@@ -281,6 +386,7 @@ export class RuntimeHttpServer {
|
|
|
281
386
|
this.getCesClient = options.getCesClient;
|
|
282
387
|
this.onProviderCredentialsChanged = options.onProviderCredentialsChanged;
|
|
283
388
|
this.getHeartbeatService = options.getHeartbeatService;
|
|
389
|
+
this.getFilingService = options.getFilingService;
|
|
284
390
|
this.router = new HttpRouter(this.buildRouteTable());
|
|
285
391
|
}
|
|
286
392
|
|
|
@@ -318,7 +424,11 @@ export class RuntimeHttpServer {
|
|
|
318
424
|
}
|
|
319
425
|
|
|
320
426
|
async start(): Promise<void> {
|
|
321
|
-
type AllWebSocketData =
|
|
427
|
+
type AllWebSocketData =
|
|
428
|
+
| RelayWebSocketData
|
|
429
|
+
| BrowserRelayWebSocketData
|
|
430
|
+
| MediaStreamWebSocketData
|
|
431
|
+
| SttStreamWebSocketData;
|
|
322
432
|
this.server = Bun.serve<AllWebSocketData>({
|
|
323
433
|
port: this.port,
|
|
324
434
|
hostname: this.hostname,
|
|
@@ -329,8 +439,107 @@ export class RuntimeHttpServer {
|
|
|
329
439
|
open(ws) {
|
|
330
440
|
const data = ws.data as AllWebSocketData;
|
|
331
441
|
if ("wsType" in data && data.wsType === "browser-relay") {
|
|
332
|
-
|
|
333
|
-
|
|
442
|
+
// When the JWT sub resolved to a guardian principal at upgrade
|
|
443
|
+
// time, register this connection with the chrome-extension
|
|
444
|
+
// registry so host_browser_request frames can be routed to it.
|
|
445
|
+
if (data.guardianId) {
|
|
446
|
+
const now = Date.now();
|
|
447
|
+
getChromeExtensionRegistry().register({
|
|
448
|
+
id: data.connectionId,
|
|
449
|
+
guardianId: data.guardianId,
|
|
450
|
+
clientInstanceId: data.clientInstanceId,
|
|
451
|
+
ws,
|
|
452
|
+
connectedAt: now,
|
|
453
|
+
lastActiveAt: now,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
return;
|
|
457
|
+
}
|
|
458
|
+
if ("wsType" in data && data.wsType === "media-stream") {
|
|
459
|
+
const msData = data as MediaStreamWebSocketData;
|
|
460
|
+
log.info(
|
|
461
|
+
{ callSessionId: msData.callSessionId },
|
|
462
|
+
"Media-stream WebSocket opened",
|
|
463
|
+
);
|
|
464
|
+
const session = new MediaStreamCallSession(
|
|
465
|
+
ws,
|
|
466
|
+
msData.callSessionId,
|
|
467
|
+
);
|
|
468
|
+
activeMediaStreamSessions.set(msData.callSessionId, session);
|
|
469
|
+
// Bind the session instance to the websocket so the close
|
|
470
|
+
// handler tears down *this* session, not a replacement that
|
|
471
|
+
// a reconnect may have inserted under the same callSessionId.
|
|
472
|
+
msData.session = session;
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
if ("wsType" in data && data.wsType === "stt-stream") {
|
|
476
|
+
const sttData = data as SttStreamWebSocketData;
|
|
477
|
+
|
|
478
|
+
// The runtime is config-authoritative: always resolve the
|
|
479
|
+
// provider from `services.stt.provider` regardless of what
|
|
480
|
+
// the client/gateway requested.
|
|
481
|
+
//
|
|
482
|
+
// getConfig() can throw (e.g. after invalidateConfigCache()
|
|
483
|
+
// when config.json is temporarily invalid). Wrap in try/catch
|
|
484
|
+
// so the session still starts normally — resolveStreamingTranscriber
|
|
485
|
+
// reads config inside SttStreamSession.start()'s own guarded path.
|
|
486
|
+
let configuredProvider: string | undefined;
|
|
487
|
+
try {
|
|
488
|
+
configuredProvider = getConfig().services.stt.provider;
|
|
489
|
+
|
|
490
|
+
// Mismatch telemetry: when the optional requested provider
|
|
491
|
+
// disagrees with the configured provider, log a warning so
|
|
492
|
+
// operators can detect stale client builds.
|
|
493
|
+
if (sttData.provider && sttData.provider !== configuredProvider) {
|
|
494
|
+
log.warn(
|
|
495
|
+
{
|
|
496
|
+
requestedProvider: sttData.provider,
|
|
497
|
+
configuredProvider,
|
|
498
|
+
sessionId: sttData.sessionId,
|
|
499
|
+
},
|
|
500
|
+
"STT stream provider mismatch — requested provider differs from configured provider; using configured provider",
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
} catch (err) {
|
|
504
|
+
log.warn(
|
|
505
|
+
{
|
|
506
|
+
error: err instanceof Error ? err.message : String(err),
|
|
507
|
+
sessionId: sttData.sessionId,
|
|
508
|
+
},
|
|
509
|
+
"Failed to read config for STT provider mismatch telemetry — proceeding without mismatch check",
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// Fall back to the requested provider (or "unknown") when
|
|
514
|
+
// config reading failed, so the session constructor still
|
|
515
|
+
// gets a usable label for logging/error messages.
|
|
516
|
+
const effectiveProvider =
|
|
517
|
+
configuredProvider ?? sttData.provider ?? "unknown";
|
|
518
|
+
|
|
519
|
+
log.info(
|
|
520
|
+
{
|
|
521
|
+
requestedProvider: sttData.provider ?? "(none)",
|
|
522
|
+
configuredProvider: effectiveProvider,
|
|
523
|
+
mimeType: sttData.mimeType,
|
|
524
|
+
sessionId: sttData.sessionId,
|
|
525
|
+
},
|
|
526
|
+
"STT stream WebSocket opened",
|
|
527
|
+
);
|
|
528
|
+
const session = new SttStreamSession(
|
|
529
|
+
ws,
|
|
530
|
+
effectiveProvider,
|
|
531
|
+
sttData.mimeType,
|
|
532
|
+
{ sampleRate: sttData.sampleRate },
|
|
533
|
+
);
|
|
534
|
+
sttData.session = session;
|
|
535
|
+
activeSttStreamSessions.set(sttData.sessionId, session);
|
|
536
|
+
|
|
537
|
+
// Start the session asynchronously — resolves the streaming
|
|
538
|
+
// transcriber and sends a `ready` event on success.
|
|
539
|
+
void session.start(() =>
|
|
540
|
+
resolveStreamingTranscriber({
|
|
541
|
+
sampleRate: sttData.sampleRate,
|
|
542
|
+
}),
|
|
334
543
|
);
|
|
335
544
|
return;
|
|
336
545
|
}
|
|
@@ -351,10 +560,148 @@ export class RuntimeHttpServer {
|
|
|
351
560
|
? message
|
|
352
561
|
: new TextDecoder().decode(message);
|
|
353
562
|
if ("wsType" in data && data.wsType === "browser-relay") {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
563
|
+
// Inbound frames on `/v1/browser-relay` carry one of:
|
|
564
|
+
// - `host_browser_result` — paired response to an outbound
|
|
565
|
+
// `host_browser_request` (see PR2).
|
|
566
|
+
// - `host_browser_event` — unsolicited CDP event forwarded
|
|
567
|
+
// from the extension's `chrome.debugger.onEvent`
|
|
568
|
+
// subscription (PR10).
|
|
569
|
+
// - `host_browser_session_invalidated` — detach
|
|
570
|
+
// notification forwarded from the extension's
|
|
571
|
+
// `chrome.debugger.onDetach` subscription (PR10).
|
|
572
|
+
//
|
|
573
|
+
// Every supported frame type delegates into a shared
|
|
574
|
+
// resolver exported from `host-browser-routes.ts` so the
|
|
575
|
+
// validation and resolution semantics stay in lockstep
|
|
576
|
+
// with the HTTP path. Malformed or unsupported frames are
|
|
577
|
+
// logged at debug and swallowed — we never throw out of a
|
|
578
|
+
// WebSocket `message` handler because an uncaught
|
|
579
|
+
// exception would tear down the whole socket for an
|
|
580
|
+
// attacker-controlled payload.
|
|
581
|
+
let parsed: unknown;
|
|
582
|
+
try {
|
|
583
|
+
parsed = JSON.parse(raw);
|
|
584
|
+
} catch (err) {
|
|
585
|
+
log.debug(
|
|
586
|
+
{
|
|
587
|
+
connectionId: data.connectionId,
|
|
588
|
+
error: err instanceof Error ? err.message : String(err),
|
|
589
|
+
},
|
|
590
|
+
"browser-relay: dropped non-JSON inbound frame",
|
|
591
|
+
);
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
if (!parsed || typeof parsed !== "object") {
|
|
595
|
+
log.debug(
|
|
596
|
+
{ connectionId: data.connectionId },
|
|
597
|
+
"browser-relay: dropped non-object inbound frame",
|
|
598
|
+
);
|
|
599
|
+
return;
|
|
600
|
+
}
|
|
601
|
+
const frame = parsed as Record<string, unknown>;
|
|
602
|
+
switch (frame.type) {
|
|
603
|
+
case "host_browser_result": {
|
|
604
|
+
const resolution = resolveHostBrowserResultByRequestId({
|
|
605
|
+
requestId: frame.requestId,
|
|
606
|
+
content: frame.content,
|
|
607
|
+
isError: frame.isError,
|
|
608
|
+
});
|
|
609
|
+
if (!resolution.ok) {
|
|
610
|
+
log.warn(
|
|
611
|
+
{
|
|
612
|
+
connectionId: data.connectionId,
|
|
613
|
+
requestId:
|
|
614
|
+
typeof frame.requestId === "string"
|
|
615
|
+
? frame.requestId
|
|
616
|
+
: undefined,
|
|
617
|
+
code: resolution.code,
|
|
618
|
+
message: resolution.message,
|
|
619
|
+
},
|
|
620
|
+
"browser-relay: host_browser_result frame rejected",
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
case "host_browser_event": {
|
|
626
|
+
const resolution = resolveHostBrowserEvent({
|
|
627
|
+
method: frame.method,
|
|
628
|
+
params: frame.params,
|
|
629
|
+
cdpSessionId: frame.cdpSessionId,
|
|
630
|
+
});
|
|
631
|
+
if (!resolution.ok) {
|
|
632
|
+
log.warn(
|
|
633
|
+
{
|
|
634
|
+
connectionId: data.connectionId,
|
|
635
|
+
method:
|
|
636
|
+
typeof frame.method === "string"
|
|
637
|
+
? frame.method
|
|
638
|
+
: undefined,
|
|
639
|
+
code: resolution.code,
|
|
640
|
+
message: resolution.message,
|
|
641
|
+
},
|
|
642
|
+
"browser-relay: host_browser_event frame rejected",
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
case "host_browser_session_invalidated": {
|
|
648
|
+
const resolution = resolveHostBrowserSessionInvalidated({
|
|
649
|
+
targetId: frame.targetId,
|
|
650
|
+
reason: frame.reason,
|
|
651
|
+
});
|
|
652
|
+
if (!resolution.ok) {
|
|
653
|
+
log.warn(
|
|
654
|
+
{
|
|
655
|
+
connectionId: data.connectionId,
|
|
656
|
+
targetId:
|
|
657
|
+
typeof frame.targetId === "string"
|
|
658
|
+
? frame.targetId
|
|
659
|
+
: undefined,
|
|
660
|
+
code: resolution.code,
|
|
661
|
+
message: resolution.message,
|
|
662
|
+
},
|
|
663
|
+
"browser-relay: host_browser_session_invalidated frame rejected",
|
|
664
|
+
);
|
|
665
|
+
}
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
case "keepalive": {
|
|
669
|
+
// Extension keepalive frames refresh the connection's
|
|
670
|
+
// activity timestamp without producing log noise or
|
|
671
|
+
// altering routing semantics. Unknown extra keys on
|
|
672
|
+
// the frame are silently ignored (lenient validation).
|
|
673
|
+
getChromeExtensionRegistry().touch(data.connectionId);
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
default: {
|
|
677
|
+
log.debug(
|
|
678
|
+
{ connectionId: data.connectionId, type: frame.type },
|
|
679
|
+
"browser-relay: dropped unsupported inbound frame type",
|
|
680
|
+
);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
if ("wsType" in data && data.wsType === "media-stream") {
|
|
686
|
+
const msData = data as MediaStreamWebSocketData;
|
|
687
|
+
msData.session?.handleMessage(raw);
|
|
688
|
+
return;
|
|
689
|
+
}
|
|
690
|
+
if ("wsType" in data && data.wsType === "stt-stream") {
|
|
691
|
+
const sttData = data as SttStreamWebSocketData;
|
|
692
|
+
const session = sttData.session;
|
|
693
|
+
if (!session) return;
|
|
694
|
+
|
|
695
|
+
if (typeof message === "string") {
|
|
696
|
+
session.handleMessage(message);
|
|
697
|
+
} else {
|
|
698
|
+
// Binary frame — raw audio bytes.
|
|
699
|
+
const buffer =
|
|
700
|
+
message instanceof ArrayBuffer
|
|
701
|
+
? Buffer.from(new Uint8Array(message))
|
|
702
|
+
: Buffer.from(message);
|
|
703
|
+
session.handleBinaryAudio(buffer);
|
|
704
|
+
}
|
|
358
705
|
return;
|
|
359
706
|
}
|
|
360
707
|
const callSessionId = (data as RelayWebSocketData).callSessionId;
|
|
@@ -366,11 +713,62 @@ export class RuntimeHttpServer {
|
|
|
366
713
|
close(ws, code, reason) {
|
|
367
714
|
const data = ws.data as AllWebSocketData;
|
|
368
715
|
if ("wsType" in data && data.wsType === "browser-relay") {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
716
|
+
// Always attempt to unregister — the registry uses connectionId
|
|
717
|
+
// as the key and no-ops if the entry is absent (e.g. when the
|
|
718
|
+
// connection was never registered because guardianId was
|
|
719
|
+
// undefined, or when it was superseded by a newer registration
|
|
720
|
+
// for the same guardian).
|
|
721
|
+
getChromeExtensionRegistry().unregister(data.connectionId);
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
if ("wsType" in data && data.wsType === "media-stream") {
|
|
725
|
+
const msData = data as MediaStreamWebSocketData;
|
|
726
|
+
log.info(
|
|
727
|
+
{
|
|
728
|
+
callSessionId: msData.callSessionId,
|
|
729
|
+
code,
|
|
730
|
+
reason: reason?.toString(),
|
|
731
|
+
},
|
|
732
|
+
"Media-stream WebSocket closed",
|
|
373
733
|
);
|
|
734
|
+
// Use the session bound at open time so we tear down the
|
|
735
|
+
// exact session that owns *this* socket, not a replacement
|
|
736
|
+
// that a reconnect may have inserted under the same key.
|
|
737
|
+
const msSession = msData.session;
|
|
738
|
+
if (msSession) {
|
|
739
|
+
msSession.handleTransportClosed(code, reason?.toString());
|
|
740
|
+
msSession.destroy();
|
|
741
|
+
// Only delete from the map if *our* session is still the
|
|
742
|
+
// registered one — a reconnect may have already replaced it.
|
|
743
|
+
if (
|
|
744
|
+
activeMediaStreamSessions.get(msData.callSessionId) ===
|
|
745
|
+
msSession
|
|
746
|
+
) {
|
|
747
|
+
activeMediaStreamSessions.delete(msData.callSessionId);
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return;
|
|
751
|
+
}
|
|
752
|
+
if ("wsType" in data && data.wsType === "stt-stream") {
|
|
753
|
+
const sttData = data as SttStreamWebSocketData;
|
|
754
|
+
log.info(
|
|
755
|
+
{
|
|
756
|
+
provider: sttData.provider,
|
|
757
|
+
sessionId: sttData.sessionId,
|
|
758
|
+
code,
|
|
759
|
+
reason: reason?.toString(),
|
|
760
|
+
},
|
|
761
|
+
"STT stream WebSocket closed",
|
|
762
|
+
);
|
|
763
|
+
const session = sttData.session;
|
|
764
|
+
if (session) {
|
|
765
|
+
session.handleClose(code, reason?.toString());
|
|
766
|
+
// Only delete from the map if our session is still the
|
|
767
|
+
// registered one — avoids races with reconnects.
|
|
768
|
+
if (activeSttStreamSessions.get(sttData.sessionId) === session) {
|
|
769
|
+
activeSttStreamSessions.delete(sttData.sessionId);
|
|
770
|
+
}
|
|
771
|
+
}
|
|
374
772
|
return;
|
|
375
773
|
}
|
|
376
774
|
const callSessionId = (data as RelayWebSocketData).callSessionId;
|
|
@@ -388,7 +786,51 @@ export class RuntimeHttpServer {
|
|
|
388
786
|
},
|
|
389
787
|
});
|
|
390
788
|
|
|
391
|
-
|
|
789
|
+
this.startBackgroundSweeps();
|
|
790
|
+
|
|
791
|
+
log.info(
|
|
792
|
+
"Running in gateway-only ingress mode. Direct webhook routes disabled.",
|
|
793
|
+
);
|
|
794
|
+
if (!isLoopbackHost(this.hostname)) {
|
|
795
|
+
log.warn(
|
|
796
|
+
"RUNTIME_HTTP_HOST is not bound to loopback. This may expose the runtime to direct public access.",
|
|
797
|
+
);
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
this.pairingStore.start();
|
|
801
|
+
|
|
802
|
+
if (hasUngatedHttpAuthDisabled()) {
|
|
803
|
+
log.warn(
|
|
804
|
+
"DISABLE_HTTP_AUTH is set but VELLUM_UNSAFE_AUTH_BYPASS=1 is not — auth bypass is IGNORED and HTTP authentication remains enabled. Set VELLUM_UNSAFE_AUTH_BYPASS=1 to confirm the bypass.",
|
|
805
|
+
);
|
|
806
|
+
} else if (isHttpAuthDisabled()) {
|
|
807
|
+
log.warn(
|
|
808
|
+
"DISABLE_HTTP_AUTH is set — HTTP API authentication is DISABLED. All API endpoints are accessible without a bearer token. Do not use in production.",
|
|
809
|
+
);
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
log.info(
|
|
813
|
+
{
|
|
814
|
+
port: this.actualPort,
|
|
815
|
+
hostname: this.hostname,
|
|
816
|
+
auth: !!this.bearerToken,
|
|
817
|
+
},
|
|
818
|
+
"Runtime HTTP server listening",
|
|
819
|
+
);
|
|
820
|
+
|
|
821
|
+
// Advertise the actual port to thin helpers that need to reach the
|
|
822
|
+
// runtime without inheriting the daemon's environment (e.g. the
|
|
823
|
+
// chrome-extension native messaging helper, spawned by Chrome).
|
|
824
|
+
this.writeRuntimePortFile(this.actualPort);
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Start background sweep timers: retry sweep for failed channel events,
|
|
829
|
+
* guardian approval/action expiry sweeps, and canonical guardian expiry.
|
|
830
|
+
* Extracted from start() to allow future callers to defer sweep startup.
|
|
831
|
+
*/
|
|
832
|
+
private startBackgroundSweeps(): void {
|
|
833
|
+
if (this.processMessage && !this.retrySweepTimer) {
|
|
392
834
|
const pm = this.processMessage;
|
|
393
835
|
const mintBt = () => mintDaemonDeliveryToken();
|
|
394
836
|
this.retrySweepTimer = setInterval(() => {
|
|
@@ -416,36 +858,76 @@ export class RuntimeHttpServer {
|
|
|
416
858
|
|
|
417
859
|
startCanonicalGuardianExpirySweep();
|
|
418
860
|
log.info("Canonical guardian request expiry sweep started");
|
|
861
|
+
}
|
|
419
862
|
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
863
|
+
/**
|
|
864
|
+
* Atomically publish the runtime HTTP port to ~/.vellum/runtime-port so
|
|
865
|
+
* external helpers can locate a non-default `RUNTIME_HTTP_PORT` without
|
|
866
|
+
* any manifest changes. Best-effort — write failures never block
|
|
867
|
+
* daemon startup (see assistant/AGENTS.md "Daemon startup philosophy").
|
|
868
|
+
*/
|
|
869
|
+
private writeRuntimePortFile(actualPort: number): void {
|
|
870
|
+
try {
|
|
871
|
+
const portFile = getRuntimePortFilePath();
|
|
872
|
+
const dir = dirname(portFile);
|
|
873
|
+
if (!existsSync(dir)) {
|
|
874
|
+
mkdirSync(dir, { recursive: true });
|
|
875
|
+
}
|
|
876
|
+
const tmpPath = `${portFile}.tmp.${process.pid}`;
|
|
877
|
+
writeFileSync(tmpPath, String(actualPort), { mode: 0o644 });
|
|
878
|
+
renameSync(tmpPath, portFile);
|
|
879
|
+
log.info({ portFile, actualPort }, "Wrote runtime port file");
|
|
880
|
+
} catch (err) {
|
|
424
881
|
log.warn(
|
|
425
|
-
|
|
882
|
+
{ err },
|
|
883
|
+
"Failed to write runtime port file; non-default assistant ports may require --assistant-port on thin helpers",
|
|
426
884
|
);
|
|
427
885
|
}
|
|
886
|
+
}
|
|
428
887
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
888
|
+
/**
|
|
889
|
+
* Remove the runtime port file written by `writeRuntimePortFile`.
|
|
890
|
+
* Called from `stop()` on clean shutdown so a stale file does not
|
|
891
|
+
* point thin helpers (e.g. the chrome-extension native messaging
|
|
892
|
+
* helper) at a dead port until the next daemon start overwrites it.
|
|
893
|
+
* Best-effort — unlink failures never block shutdown.
|
|
894
|
+
*
|
|
895
|
+
* The unlink is conditional: we only remove the file if its current
|
|
896
|
+
* contents still match this server's port. The runtime-port file
|
|
897
|
+
* lives at the user-home level (`~/.vellum/runtime-port`) and is
|
|
898
|
+
* therefore shared across multiple daemon instances running on
|
|
899
|
+
* different `RUNTIME_HTTP_PORT`s. If a sibling instance has already
|
|
900
|
+
* rewritten the file with its own port, deleting it would strand
|
|
901
|
+
* thin helpers on the default port `7821` and break their ability
|
|
902
|
+
* to reach the still-running sibling.
|
|
903
|
+
*
|
|
904
|
+
* Note: this only runs on graceful shutdown. A crash leaves the
|
|
905
|
+
* file in place; the next successful startup overwrites it.
|
|
906
|
+
*/
|
|
907
|
+
private removeRuntimePortFile(): void {
|
|
908
|
+
try {
|
|
909
|
+
const portFile = getRuntimePortFilePath();
|
|
910
|
+
if (!existsSync(portFile)) return;
|
|
911
|
+
// Read-then-compare-then-unlink. Race-safe enough: the worst case
|
|
912
|
+
// is that another instance writes the file between our read and
|
|
913
|
+
// our unlink, in which case we erroneously delete its mapping.
|
|
914
|
+
// That window is short (a few microseconds) and a sibling startup
|
|
915
|
+
// will rewrite the file on its next port-publish call. The much
|
|
916
|
+
// more common multi-instance race — sibling already overwrote
|
|
917
|
+
// before our stop() runs — is correctly handled here as a no-op.
|
|
918
|
+
const current = readFileSync(portFile, "utf-8").trim();
|
|
919
|
+
if (current !== String(this.actualPort)) {
|
|
920
|
+
log.info(
|
|
921
|
+
{ portFile, current, actualPort: this.actualPort },
|
|
922
|
+
"Leaving runtime port file alone — owned by another instance",
|
|
923
|
+
);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
unlinkSync(portFile);
|
|
927
|
+
log.info({ portFile }, "Removed runtime port file");
|
|
928
|
+
} catch (err) {
|
|
929
|
+
log.warn({ err }, "Failed to remove runtime port file");
|
|
439
930
|
}
|
|
440
|
-
|
|
441
|
-
log.info(
|
|
442
|
-
{
|
|
443
|
-
port: this.actualPort,
|
|
444
|
-
hostname: this.hostname,
|
|
445
|
-
auth: !!this.bearerToken,
|
|
446
|
-
},
|
|
447
|
-
"Runtime HTTP server listening",
|
|
448
|
-
);
|
|
449
931
|
}
|
|
450
932
|
|
|
451
933
|
async stop(): Promise<void> {
|
|
@@ -457,11 +939,21 @@ export class RuntimeHttpServer {
|
|
|
457
939
|
clearInterval(this.retrySweepTimer);
|
|
458
940
|
this.retrySweepTimer = null;
|
|
459
941
|
}
|
|
942
|
+
|
|
943
|
+
// Deterministic teardown of active STT streaming sessions before
|
|
944
|
+
// stopping the HTTP server so provider sessions are cleaned up
|
|
945
|
+
// and clients receive proper close frames.
|
|
946
|
+
for (const [sessionId, session] of activeSttStreamSessions) {
|
|
947
|
+
session.destroy();
|
|
948
|
+
activeSttStreamSessions.delete(sessionId);
|
|
949
|
+
}
|
|
950
|
+
|
|
460
951
|
if (this.server) {
|
|
461
952
|
this.server.stop(true);
|
|
462
953
|
this.server = null;
|
|
463
954
|
log.info("Runtime HTTP server stopped");
|
|
464
955
|
}
|
|
956
|
+
this.removeRuntimePortFile();
|
|
465
957
|
}
|
|
466
958
|
|
|
467
959
|
private async handleRequest(
|
|
@@ -512,6 +1004,24 @@ export class RuntimeHttpServer {
|
|
|
512
1004
|
return this.handleRelayUpgrade(req, server);
|
|
513
1005
|
}
|
|
514
1006
|
|
|
1007
|
+
// WebSocket upgrade for Twilio Media Streams — same private-network
|
|
1008
|
+
// restrictions as relay upgrades.
|
|
1009
|
+
if (
|
|
1010
|
+
path.startsWith("/v1/calls/media-stream") &&
|
|
1011
|
+
req.headers.get("upgrade")?.toLowerCase() === "websocket"
|
|
1012
|
+
) {
|
|
1013
|
+
return this.handleMediaStreamUpgrade(req, server);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
// WebSocket upgrade for STT streaming — private-network restrictions
|
|
1017
|
+
// and explicit gateway-service token verification before upgrade.
|
|
1018
|
+
if (
|
|
1019
|
+
path === "/v1/stt/stream" &&
|
|
1020
|
+
req.headers.get("upgrade")?.toLowerCase() === "websocket"
|
|
1021
|
+
) {
|
|
1022
|
+
return this.handleSttStreamUpgrade(req, server);
|
|
1023
|
+
}
|
|
1024
|
+
|
|
515
1025
|
// Twilio webhook endpoints — before auth check because Twilio
|
|
516
1026
|
// webhook POSTs don't include bearer tokens.
|
|
517
1027
|
const twilioResponse = await this.handleTwilioWebhook(req, path);
|
|
@@ -532,6 +1042,13 @@ export class RuntimeHttpServer {
|
|
|
532
1042
|
return handlePairingStatus(url, this.pairingContext);
|
|
533
1043
|
}
|
|
534
1044
|
|
|
1045
|
+
// Chrome extension capability-token pair endpoint — unauthenticated but
|
|
1046
|
+
// restricted to loopback peers + an extension-id allowlist. Used by the
|
|
1047
|
+
// native messaging helper to bootstrap a scoped token.
|
|
1048
|
+
if (path === "/v1/browser-extension-pair") {
|
|
1049
|
+
return await handleBrowserExtensionPair(req, server);
|
|
1050
|
+
}
|
|
1051
|
+
|
|
535
1052
|
// Guardian bootstrap and refresh endpoints — before JWT auth because
|
|
536
1053
|
// bootstrap is the first endpoint called to obtain a JWT, and refresh
|
|
537
1054
|
// needs to work when the access token is expired. Bootstrap has its
|
|
@@ -544,9 +1061,29 @@ export class RuntimeHttpServer {
|
|
|
544
1061
|
return await handleGuardianRefresh(req);
|
|
545
1062
|
}
|
|
546
1063
|
|
|
1064
|
+
// Skill-registered routes (e.g. meet-bot event ingress). Handled before
|
|
1065
|
+
// JWT auth because skills may use their own auth (e.g. per-meeting bearer
|
|
1066
|
+
// tokens minted by a session manager).
|
|
1067
|
+
const skillMatch = matchSkillRoute(path, req.method);
|
|
1068
|
+
if (skillMatch) {
|
|
1069
|
+
return await skillMatch.route.handler(req, skillMatch.match);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
547
1072
|
// JWT bearer authentication — replaces the old shared-secret comparison.
|
|
548
1073
|
// authenticateRequest handles dev bypass (DISABLE_HTTP_AUTH) internally.
|
|
549
|
-
|
|
1074
|
+
//
|
|
1075
|
+
// Special-case: /v1/host-browser-result POST accepts either a
|
|
1076
|
+
// daemon-minted JWT (legacy/cloud) or a host_browser capability
|
|
1077
|
+
// token (self-hosted chrome extension). The chrome extension's
|
|
1078
|
+
// HTTP fallback (`postHostBrowserResult`) hands over the same
|
|
1079
|
+
// capability token it presented to `/v1/browser-relay`, so the
|
|
1080
|
+
// POST route must understand both auth shapes. Every other route
|
|
1081
|
+
// keeps the JWT-only flow via `authenticateRequest`.
|
|
1082
|
+
const normalizedPath = path.endsWith("/") ? path.slice(0, -1) : path;
|
|
1083
|
+
const authResult =
|
|
1084
|
+
normalizedPath === "/v1/host-browser-result" && req.method === "POST"
|
|
1085
|
+
? authenticateHostBrowserResultRequest(req)
|
|
1086
|
+
: authenticateRequest(req);
|
|
550
1087
|
if (!authResult.ok) {
|
|
551
1088
|
return authResult.response;
|
|
552
1089
|
}
|
|
@@ -634,15 +1171,110 @@ export class RuntimeHttpServer {
|
|
|
634
1171
|
);
|
|
635
1172
|
}
|
|
636
1173
|
|
|
1174
|
+
// When auth is enabled we accept two different kinds of token on the
|
|
1175
|
+
// `/v1/browser-relay` handshake:
|
|
1176
|
+
//
|
|
1177
|
+
// 1. **Capability token** — a signed `host_browser_command`
|
|
1178
|
+
// capability minted by `mintHostBrowserCapability()` and handed
|
|
1179
|
+
// to the chrome extension by the native-messaging pair flow
|
|
1180
|
+
// (`/v1/browser-extension-pair`). This is the preferred,
|
|
1181
|
+
// self-hosted default: the extension never has to touch a
|
|
1182
|
+
// gateway JWT.
|
|
1183
|
+
// 2. **JWT** (audience `vellum-daemon`) — the legacy path used by
|
|
1184
|
+
// the gateway-proxied cloud flow and by any compatibility
|
|
1185
|
+
// callers that still hold a daemon-bound JWT. In that case we
|
|
1186
|
+
// parse the JWT `sub` to extract the actor principal id and
|
|
1187
|
+
// fall back to the explicit `x-guardian-id` / `guardianId`
|
|
1188
|
+
// query param for service-token paths (see below).
|
|
1189
|
+
//
|
|
1190
|
+
// When auth is disabled (dev bypass), guardianId remains undefined
|
|
1191
|
+
// and the registration is skipped — host_browser_request routing
|
|
1192
|
+
// requires an authenticated guardian.
|
|
1193
|
+
//
|
|
1194
|
+
// Gateway path: when the WebSocket upgrade is proxied through the
|
|
1195
|
+
// gateway, the upstream token minted by `mintServiceToken()` has
|
|
1196
|
+
// `sub=svc:gateway:self` with no actor principal id. The gateway
|
|
1197
|
+
// parses the downstream edge token's `actorPrincipalId` and forwards
|
|
1198
|
+
// it as an explicit `guardianId` query parameter (and/or header) so
|
|
1199
|
+
// we can register the connection under the real guardian. Missing
|
|
1200
|
+
// guardian context on this path is rejected (fail closed).
|
|
1201
|
+
// Read the client-supplied stable instance id off the handshake.
|
|
1202
|
+
// The extension generates this on first run and persists it in
|
|
1203
|
+
// chrome.storage so it survives service-worker restarts and
|
|
1204
|
+
// browser restarts. The header form is preferred so gateway
|
|
1205
|
+
// forwarding and proxy logs don't surface instance ids in the
|
|
1206
|
+
// URL, but we also accept a query param for fetch-based clients
|
|
1207
|
+
// that can't mutate headers. An empty string is treated as absent
|
|
1208
|
+
// so sparse clients don't end up all sharing the same legacy key.
|
|
1209
|
+
const rawInstanceHeader = req.headers.get("x-client-instance-id")?.trim();
|
|
1210
|
+
const rawInstanceQuery = new URL(req.url).searchParams
|
|
1211
|
+
.get("clientInstanceId")
|
|
1212
|
+
?.trim();
|
|
1213
|
+
const clientInstanceId =
|
|
1214
|
+
(rawInstanceHeader ?? "") || (rawInstanceQuery ?? "") || undefined;
|
|
1215
|
+
|
|
1216
|
+
let guardianId: string | undefined;
|
|
637
1217
|
if (!isHttpAuthDisabled()) {
|
|
638
1218
|
const wsUrl = new URL(req.url);
|
|
639
1219
|
const token = wsUrl.searchParams.get("token");
|
|
640
1220
|
if (!token) {
|
|
641
1221
|
return httpError("UNAUTHORIZED", "Unauthorized", 401);
|
|
642
1222
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
1223
|
+
// 1) Capability-token path (self-hosted default). The chrome
|
|
1224
|
+
// extension presents the token it received from the native
|
|
1225
|
+
// messaging pair flow. We derive `guardianId` from the
|
|
1226
|
+
// capability claims directly — the claims are HMAC-signed by
|
|
1227
|
+
// the same daemon so there is no cross-tenant risk.
|
|
1228
|
+
const capabilityClaims = verifyHostBrowserCapability(token);
|
|
1229
|
+
if (capabilityClaims) {
|
|
1230
|
+
guardianId = capabilityClaims.guardianId;
|
|
1231
|
+
} else {
|
|
1232
|
+
// 2) JWT compatibility path (gateway / legacy). Fall back to the
|
|
1233
|
+
// existing verifyToken+parseSub flow so cloud callers and any
|
|
1234
|
+
// old self-hosted clients still holding a daemon JWT
|
|
1235
|
+
// continue to work during the cutover.
|
|
1236
|
+
const jwtResult = verifyToken(token, "vellum-daemon");
|
|
1237
|
+
if (!jwtResult.ok) {
|
|
1238
|
+
return httpError("UNAUTHORIZED", "Unauthorized", 401);
|
|
1239
|
+
}
|
|
1240
|
+
const subResult = parseSub(jwtResult.claims.sub);
|
|
1241
|
+
if (subResult.ok && subResult.actorPrincipalId) {
|
|
1242
|
+
// Direct actor principal — this is the loopback / desktop path.
|
|
1243
|
+
guardianId = subResult.actorPrincipalId;
|
|
1244
|
+
} else {
|
|
1245
|
+
// Service-token path (gateway-forwarded). The gateway must plumb
|
|
1246
|
+
// the resolved actor principal as an explicit `x-guardian-id`
|
|
1247
|
+
// header or `guardianId` query param. Header takes precedence
|
|
1248
|
+
// because headers are easier for the gateway to forward without
|
|
1249
|
+
// rewriting the URL.
|
|
1250
|
+
const headerGuardianId =
|
|
1251
|
+
req.headers.get("x-guardian-id")?.trim() ?? "";
|
|
1252
|
+
const queryGuardianId =
|
|
1253
|
+
wsUrl.searchParams.get("guardianId")?.trim() ?? "";
|
|
1254
|
+
const fallbackGuardianId = headerGuardianId || queryGuardianId;
|
|
1255
|
+
if (fallbackGuardianId) {
|
|
1256
|
+
guardianId = fallbackGuardianId;
|
|
1257
|
+
} else {
|
|
1258
|
+
// Fail closed: a service-token relay upgrade without a
|
|
1259
|
+
// guardian context cannot be routed safely. Allowing the
|
|
1260
|
+
// upgrade to proceed creates an unscoped socket that never
|
|
1261
|
+
// registers in the ChromeExtensionRegistry.
|
|
1262
|
+
log.warn(
|
|
1263
|
+
{
|
|
1264
|
+
principalType: subResult.ok
|
|
1265
|
+
? subResult.principalType
|
|
1266
|
+
: "unknown",
|
|
1267
|
+
sub: jwtResult.claims.sub,
|
|
1268
|
+
},
|
|
1269
|
+
"Browser relay upgrade denied: missing guardian context on service-token path",
|
|
1270
|
+
);
|
|
1271
|
+
return httpError(
|
|
1272
|
+
"UNAUTHORIZED",
|
|
1273
|
+
"Browser relay requires guardian context",
|
|
1274
|
+
401,
|
|
1275
|
+
);
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
646
1278
|
}
|
|
647
1279
|
}
|
|
648
1280
|
|
|
@@ -651,6 +1283,8 @@ export class RuntimeHttpServer {
|
|
|
651
1283
|
data: {
|
|
652
1284
|
wsType: "browser-relay",
|
|
653
1285
|
connectionId,
|
|
1286
|
+
guardianId,
|
|
1287
|
+
clientInstanceId,
|
|
654
1288
|
} satisfies BrowserRelayWebSocketData,
|
|
655
1289
|
});
|
|
656
1290
|
if (!upgraded) {
|
|
@@ -685,6 +1319,108 @@ export class RuntimeHttpServer {
|
|
|
685
1319
|
return undefined!;
|
|
686
1320
|
}
|
|
687
1321
|
|
|
1322
|
+
private handleMediaStreamUpgrade(
|
|
1323
|
+
req: Request,
|
|
1324
|
+
server: ReturnType<typeof Bun.serve>,
|
|
1325
|
+
): Response {
|
|
1326
|
+
if (!isPrivateNetworkPeer(server, req) || !isPrivateNetworkOrigin(req)) {
|
|
1327
|
+
return httpError(
|
|
1328
|
+
"FORBIDDEN",
|
|
1329
|
+
"Direct media-stream access disabled — only private network peers allowed",
|
|
1330
|
+
403,
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
const wsUrl = new URL(req.url);
|
|
1335
|
+
const callSessionId = wsUrl.searchParams.get("callSessionId");
|
|
1336
|
+
if (!callSessionId) {
|
|
1337
|
+
return new Response("Missing callSessionId", { status: 400 });
|
|
1338
|
+
}
|
|
1339
|
+
// Media-stream connections use a distinct wsType so the open/message/close
|
|
1340
|
+
// handlers route them to MediaStreamCallSession instead of RelayConnection.
|
|
1341
|
+
const upgraded = server.upgrade(req, {
|
|
1342
|
+
data: {
|
|
1343
|
+
wsType: "media-stream",
|
|
1344
|
+
callSessionId,
|
|
1345
|
+
} satisfies MediaStreamWebSocketData,
|
|
1346
|
+
});
|
|
1347
|
+
if (!upgraded) {
|
|
1348
|
+
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
1349
|
+
}
|
|
1350
|
+
// Bun's WebSocket upgrade consumes the request — no Response is sent.
|
|
1351
|
+
return undefined!;
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
/**
|
|
1355
|
+
* Handle WebSocket upgrade for `/v1/stt/stream`.
|
|
1356
|
+
*
|
|
1357
|
+
* Private-network restrictions apply (same as relay/media-stream) so the
|
|
1358
|
+
* runtime remains unreachable from the public internet. The gateway
|
|
1359
|
+
* authenticates the downstream client and proxies the upgrade with a
|
|
1360
|
+
* short-lived gateway service token.
|
|
1361
|
+
*/
|
|
1362
|
+
private handleSttStreamUpgrade(
|
|
1363
|
+
req: Request,
|
|
1364
|
+
server: ReturnType<typeof Bun.serve>,
|
|
1365
|
+
): Response {
|
|
1366
|
+
if (!isPrivateNetworkPeer(server, req) || !isPrivateNetworkOrigin(req)) {
|
|
1367
|
+
return httpError(
|
|
1368
|
+
"FORBIDDEN",
|
|
1369
|
+
"Direct STT stream access disabled — only private network peers allowed",
|
|
1370
|
+
403,
|
|
1371
|
+
);
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// Verify the gateway service token before accepting the upgrade.
|
|
1375
|
+
if (!isHttpAuthDisabled()) {
|
|
1376
|
+
const wsUrl = new URL(req.url);
|
|
1377
|
+
const token = wsUrl.searchParams.get("token");
|
|
1378
|
+
if (!token) {
|
|
1379
|
+
return httpError("UNAUTHORIZED", "Unauthorized", 401);
|
|
1380
|
+
}
|
|
1381
|
+
const jwtResult = verifyToken(token, "vellum-daemon");
|
|
1382
|
+
if (!jwtResult.ok) {
|
|
1383
|
+
return httpError("UNAUTHORIZED", "Unauthorized", 401);
|
|
1384
|
+
}
|
|
1385
|
+
// Accept gateway service tokens (svc:gateway:*) — these are the
|
|
1386
|
+
// only tokens the gateway mints for upstream connections.
|
|
1387
|
+
const subResult = parseSub(jwtResult.claims.sub);
|
|
1388
|
+
if (!subResult.ok || subResult.principalType !== "svc_gateway") {
|
|
1389
|
+
return httpError("UNAUTHORIZED", "Unauthorized", 401);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
const wsUrl = new URL(req.url);
|
|
1394
|
+
// provider is optional compatibility metadata — the runtime resolves
|
|
1395
|
+
// the streaming transcriber from config (`services.stt.provider`).
|
|
1396
|
+
const provider = wsUrl.searchParams.get("provider") ?? undefined;
|
|
1397
|
+
const mimeType = wsUrl.searchParams.get("mimeType");
|
|
1398
|
+
if (!mimeType) {
|
|
1399
|
+
return new Response("Missing required query parameter: mimeType", {
|
|
1400
|
+
status: 400,
|
|
1401
|
+
});
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
const sampleRateRaw = wsUrl.searchParams.get("sampleRate");
|
|
1405
|
+
const sampleRate = sampleRateRaw ? parseInt(sampleRateRaw, 10) : undefined;
|
|
1406
|
+
|
|
1407
|
+
const sessionId = crypto.randomUUID();
|
|
1408
|
+
const upgraded = server.upgrade(req, {
|
|
1409
|
+
data: {
|
|
1410
|
+
wsType: "stt-stream",
|
|
1411
|
+
provider,
|
|
1412
|
+
mimeType,
|
|
1413
|
+
sampleRate,
|
|
1414
|
+
sessionId,
|
|
1415
|
+
} satisfies SttStreamWebSocketData,
|
|
1416
|
+
});
|
|
1417
|
+
if (!upgraded) {
|
|
1418
|
+
return new Response("WebSocket upgrade failed", { status: 500 });
|
|
1419
|
+
}
|
|
1420
|
+
// Bun's WebSocket upgrade consumes the request — no Response is sent.
|
|
1421
|
+
return undefined!;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
688
1424
|
private async handleTwilioWebhook(
|
|
689
1425
|
req: Request,
|
|
690
1426
|
path: string,
|
|
@@ -833,6 +1569,7 @@ export class RuntimeHttpServer {
|
|
|
833
1569
|
lastMessageAt: conversation.lastMessageAt,
|
|
834
1570
|
conversationType: conversation.conversationType ?? "standard",
|
|
835
1571
|
source: conversation.source ?? "user",
|
|
1572
|
+
hostAccess: conversation.hostAccess === 1,
|
|
836
1573
|
...(conversation.scheduleJobId
|
|
837
1574
|
? { scheduleJobId: conversation.scheduleJobId }
|
|
838
1575
|
: {}),
|
|
@@ -861,6 +1598,9 @@ export class RuntimeHttpServer {
|
|
|
861
1598
|
: {}),
|
|
862
1599
|
groupId: displayMeta?.groupId ?? null,
|
|
863
1600
|
...(forkParent ? { forkParent } : {}),
|
|
1601
|
+
...(conversation.archivedAt != null
|
|
1602
|
+
? { archivedAt: conversation.archivedAt }
|
|
1603
|
+
: {}),
|
|
864
1604
|
};
|
|
865
1605
|
}
|
|
866
1606
|
|
|
@@ -963,6 +1703,11 @@ export class RuntimeHttpServer {
|
|
|
963
1703
|
...heartbeatRouteDefinitions({
|
|
964
1704
|
getHeartbeatService: this.getHeartbeatService,
|
|
965
1705
|
}),
|
|
1706
|
+
...filingRouteDefinitions({
|
|
1707
|
+
getFilingService: this.getFilingService,
|
|
1708
|
+
}),
|
|
1709
|
+
...homeStateRouteDefinitions(),
|
|
1710
|
+
...homeFeedRouteDefinitions(),
|
|
966
1711
|
...notificationRouteDefinitions(),
|
|
967
1712
|
...diagnosticsRouteDefinitions(),
|
|
968
1713
|
...logExportRouteDefinitions(),
|
|
@@ -996,29 +1741,7 @@ export class RuntimeHttpServer {
|
|
|
996
1741
|
: undefined,
|
|
997
1742
|
}),
|
|
998
1743
|
...ttsRouteDefinitions(),
|
|
999
|
-
|
|
1000
|
-
// Browser relay — not extracted into a domain module because
|
|
1001
|
-
// these two routes depend on the in-process extensionRelayServer
|
|
1002
|
-
// singleton which is only available here.
|
|
1003
|
-
{
|
|
1004
|
-
endpoint: "browser-relay/status",
|
|
1005
|
-
method: "GET",
|
|
1006
|
-
handler: () => Response.json(extensionRelayServer.getStatus()),
|
|
1007
|
-
},
|
|
1008
|
-
{
|
|
1009
|
-
endpoint: "browser-relay/command",
|
|
1010
|
-
method: "POST",
|
|
1011
|
-
handler: async ({ req }) => {
|
|
1012
|
-
const body = (await req.json()) as Record<string, unknown>;
|
|
1013
|
-
const resp = await extensionRelayServer.sendCommand(
|
|
1014
|
-
body as Omit<
|
|
1015
|
-
import("../browser-extension-relay/protocol.js").ExtensionCommand,
|
|
1016
|
-
"id"
|
|
1017
|
-
>,
|
|
1018
|
-
);
|
|
1019
|
-
return Response.json(resp);
|
|
1020
|
-
},
|
|
1021
|
-
},
|
|
1744
|
+
...sttRouteDefinitions(),
|
|
1022
1745
|
|
|
1023
1746
|
// Conversation list and seen signal — kept inline because they
|
|
1024
1747
|
// depend on multiple cross-cutting stores that aren't grouped
|
|
@@ -1085,13 +1808,28 @@ export class RuntimeHttpServer {
|
|
|
1085
1808
|
? conversationManagementRouteDefinitions(conversationManagementDeps)
|
|
1086
1809
|
: []),
|
|
1087
1810
|
|
|
1088
|
-
...(
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1811
|
+
...((): RouteDefinition[] => {
|
|
1812
|
+
const sendMessageDeps = this.sendMessageDeps;
|
|
1813
|
+
if (!sendMessageDeps) return [];
|
|
1814
|
+
const analysisDeps = {
|
|
1815
|
+
sendMessageDeps,
|
|
1816
|
+
buildConversationDetailResponse: (id: string) =>
|
|
1817
|
+
this.buildConversationDetailResponse(id),
|
|
1818
|
+
};
|
|
1819
|
+
// Also expose via the module singleton so background callers
|
|
1820
|
+
// (e.g. job handlers) can invoke analyzeConversation() without
|
|
1821
|
+
// HTTP-layer wiring. Daemon startup must never block, so failures
|
|
1822
|
+
// to register the singleton are logged and swallowed.
|
|
1823
|
+
try {
|
|
1824
|
+
setAnalysisDeps(analysisDeps);
|
|
1825
|
+
} catch (err) {
|
|
1826
|
+
log.warn(
|
|
1827
|
+
{ err },
|
|
1828
|
+
"Failed to register analysis deps singleton; background analysis jobs will be skipped",
|
|
1829
|
+
);
|
|
1830
|
+
}
|
|
1831
|
+
return conversationAnalysisRouteDefinitions(analysisDeps);
|
|
1832
|
+
})(),
|
|
1095
1833
|
|
|
1096
1834
|
...groupRouteDefinitions(),
|
|
1097
1835
|
|
|
@@ -1209,6 +1947,7 @@ export class RuntimeHttpServer {
|
|
|
1209
1947
|
...globalSearchRouteDefinitions(),
|
|
1210
1948
|
...approvalRouteDefinitions(),
|
|
1211
1949
|
...hostBashRouteDefinitions(),
|
|
1950
|
+
...hostBrowserRouteDefinitions(),
|
|
1212
1951
|
...hostCuRouteDefinitions(),
|
|
1213
1952
|
...hostFileRouteDefinitions(),
|
|
1214
1953
|
...(this.getSkillContext
|
|
@@ -1330,6 +2069,7 @@ export class RuntimeHttpServer {
|
|
|
1330
2069
|
...eventsRouteDefinitions(),
|
|
1331
2070
|
...traceEventRouteDefinitions(),
|
|
1332
2071
|
...migrationRouteDefinitions(),
|
|
2072
|
+
...backupRouteDefinitions(),
|
|
1333
2073
|
|
|
1334
2074
|
// User-defined routes under /x/* — must be LAST so built-in routes
|
|
1335
2075
|
// always take priority.
|