@vellumai/assistant 0.6.3 → 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 +5 -13
- 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/openapi.yaml +982 -72
- package/package.json +4 -6
- package/scripts/generate-openapi.ts +0 -1
- package/scripts/test.sh +73 -18
- 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__/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 +11 -0
- 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 +138 -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-schema.test.ts +1013 -66
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- 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 +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +98 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -4
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- 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 +901 -60
- package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
- 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-tool-setup-batch-authorized.test.ts +226 -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-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/credential-vault-unit.test.ts +379 -3
- package/src/__tests__/credentials-cli.test.ts +40 -16
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- 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__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
- package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +64 -0
- 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__/headless-browser-interactions.test.ts +43 -0
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +142 -5
- package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
- 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 +0 -5
- package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
- package/src/__tests__/host-cu-proxy.test.ts +0 -5
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- 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__/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-export-http.test.ts +6 -6
- package/src/__tests__/migration-import-commit-http.test.ts +8 -6
- 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__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +2 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
- package/src/__tests__/oauth-providers-routes.test.ts +2 -0
- package/src/__tests__/oauth-store.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
- package/src/__tests__/onboarding-template-contract.test.ts +6 -13
- package/src/__tests__/openai-provider.test.ts +176 -0
- 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-unsubscribe.test.ts +31 -2
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -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__/search-skills-unified.test.ts +118 -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 +5 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- 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 +276 -145
- package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
- 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 +564 -1
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +112 -26
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +18 -7
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- 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__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- 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__/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/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +43 -3
- 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/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/types.ts +16 -0
- 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 +0 -1
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +255 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +15 -0
- package/src/cli/commands/oauth/shared.ts +2 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
- 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/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/references/CUSTOM_ROUTES.md +37 -1
- 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 +2 -2
- package/src/config/bundled-skills/gmail/SKILL.md +53 -7
- 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 +2 -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/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 +24 -0
- package/src/config/env.ts +34 -10
- package/src/config/feature-flag-registry.json +46 -14
- package/src/config/loader.ts +26 -12
- package/src/config/schema.ts +35 -10
- 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 +47 -1
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/services.ts +44 -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 -0
- 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 +3 -2
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +45 -12
- 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 +17 -8
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +101 -24
- 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 +581 -19
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +11 -1
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +94 -4
- package/src/daemon/conversation-tool-setup.ts +25 -0
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation.ts +86 -28
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +4 -1
- package/src/daemon/handlers/shared.ts +22 -0
- package/src/daemon/handlers/skills.ts +321 -77
- package/src/daemon/host-browser-proxy.ts +2 -1
- package/src/daemon/lifecycle.ts +122 -25
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/conversations.ts +34 -1
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +14 -0
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +16 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +347 -2
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +9 -0
- 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 +12 -3
- 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/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 +1 -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 +99 -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/db-init.ts +6 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/graph/conversation-graph-memory.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 +27 -18
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- 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 +92 -56
- 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 +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/oauth.ts +3 -0
- 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/__tests__/identity-verifier.test.ts +1 -0
- package/src/oauth/byo-connection.test.ts +18 -1
- package/src/oauth/byo-connection.ts +3 -1
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -2
- package/src/oauth/connection.ts +2 -0
- package/src/oauth/oauth-store.ts +9 -0
- package/src/oauth/platform-connection.test.ts +98 -0
- package/src/oauth/platform-connection.ts +52 -31
- package/src/oauth/seed-providers.ts +7 -0
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/trust-store.ts +3 -3
- 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 +59 -18
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- 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 -61
- 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 +9 -0
- package/src/runtime/AGENTS.md +43 -1
- package/src/runtime/__tests__/agent-wake.test.ts +831 -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/auth/__tests__/route-policy.test.ts +40 -0
- package/src/runtime/auth/route-policy.ts +30 -5
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +10 -10
- 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 +38 -2
- package/src/runtime/http-server.ts +395 -10
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +1 -0
- package/src/runtime/migrations/migration-wizard.ts +1 -0
- package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
- package/src/runtime/migrations/vbundle-importer.ts +34 -0
- package/src/runtime/pending-interactions.ts +0 -11
- 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/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 +82 -23
- 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 -142
- package/src/runtime/routes/conversation-management-routes.ts +115 -0
- package/src/runtime/routes/conversation-routes.ts +367 -146
- package/src/runtime/routes/filing-routes.ts +93 -0
- 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 +3 -14
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +3 -17
- 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/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/migration-routes.ts +40 -5
- package/src/runtime/routes/settings-routes.ts +22 -5
- package/src/runtime/routes/skills-routes.ts +76 -7
- package/src/runtime/routes/stt-routes.ts +233 -0
- 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 +30 -2
- 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/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 +50 -0
- package/src/security/oauth2.ts +26 -4
- package/src/security/secure-keys.ts +25 -2
- package/src/security/token-manager.ts +8 -0
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +64 -2
- 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 +38 -14
- 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/browser-execution.ts +1163 -23
- package/src/tools/browser/browser-manager.ts +45 -0
- 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__/cdp-inspect-client.test.ts +393 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
- package/src/tools/browser/cdp-client/errors.ts +15 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
- package/src/tools/browser/cdp-client/factory.ts +797 -87
- package/src/tools/browser/cdp-client/index.ts +16 -2
- package/src/tools/browser/cdp-client/types.ts +68 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- 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/terminal/safe-env.ts +10 -2
- package/src/tools/terminal/shell.ts +15 -4
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +17 -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 +54 -10
- 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 +13 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/cli/commands/browser-relay.ts +0 -466
- 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/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/runtime/routes/browser-cdp-routes.ts +0 -229
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP route definitions for speech-to-text transcription.
|
|
3
|
+
*
|
|
4
|
+
* POST /v1/stt/transcribe — transcribe base64-encoded audio to text
|
|
5
|
+
*
|
|
6
|
+
* Uses the globally configured STT provider via the `services.stt`
|
|
7
|
+
* abstraction. Provider selection is resolved via `resolveBatchTranscriber()`
|
|
8
|
+
* from `providers/speech-to-text/resolve.ts`.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { z } from "zod";
|
|
12
|
+
|
|
13
|
+
import { resolveBatchTranscriber } from "../../providers/speech-to-text/resolve.js";
|
|
14
|
+
import { normalizeSttError } from "../../stt/daemon-batch-transcriber.js";
|
|
15
|
+
import type { SttErrorCategory } from "../../stt/types.js";
|
|
16
|
+
import { getLogger } from "../../util/logger.js";
|
|
17
|
+
import { httpError } from "../http-errors.js";
|
|
18
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
19
|
+
|
|
20
|
+
const log = getLogger("stt-routes");
|
|
21
|
+
|
|
22
|
+
/** Timeout for a single transcription request. */
|
|
23
|
+
const TRANSCRIPTION_TIMEOUT_MS = 30_000;
|
|
24
|
+
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
// Error category -> HTTP status / message mapping
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
const STT_ERROR_MAP: Record<
|
|
30
|
+
SttErrorCategory,
|
|
31
|
+
{ status: number; code: string; message: string }
|
|
32
|
+
> = {
|
|
33
|
+
auth: {
|
|
34
|
+
status: 401,
|
|
35
|
+
code: "UNAUTHORIZED",
|
|
36
|
+
message: "STT provider credentials are invalid or missing",
|
|
37
|
+
},
|
|
38
|
+
"rate-limit": {
|
|
39
|
+
status: 429,
|
|
40
|
+
code: "RATE_LIMITED",
|
|
41
|
+
message: "STT provider rate limit exceeded",
|
|
42
|
+
},
|
|
43
|
+
timeout: {
|
|
44
|
+
status: 504,
|
|
45
|
+
code: "INTERNAL_ERROR",
|
|
46
|
+
message: "STT transcription timed out",
|
|
47
|
+
},
|
|
48
|
+
"invalid-audio": {
|
|
49
|
+
status: 400,
|
|
50
|
+
code: "BAD_REQUEST",
|
|
51
|
+
message: "Audio payload was rejected by the STT provider",
|
|
52
|
+
},
|
|
53
|
+
"provider-error": {
|
|
54
|
+
status: 502,
|
|
55
|
+
code: "INTERNAL_ERROR",
|
|
56
|
+
message: "STT provider error",
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Route definitions
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
export function sttRouteDefinitions(): RouteDefinition[] {
|
|
65
|
+
return [
|
|
66
|
+
{
|
|
67
|
+
endpoint: "stt/transcribe",
|
|
68
|
+
method: "POST",
|
|
69
|
+
policyKey: "stt/transcribe",
|
|
70
|
+
summary: "Transcribe audio to text",
|
|
71
|
+
description:
|
|
72
|
+
"Transcribe base64-encoded audio to text using the configured STT provider. " +
|
|
73
|
+
"Provider selection is resolved globally via config.",
|
|
74
|
+
tags: ["stt"],
|
|
75
|
+
requestBody: z.object({
|
|
76
|
+
audioBase64: z
|
|
77
|
+
.string()
|
|
78
|
+
.describe("Base64-encoded audio data to transcribe"),
|
|
79
|
+
mimeType: z
|
|
80
|
+
.string()
|
|
81
|
+
.describe(
|
|
82
|
+
'MIME type of the audio data (must start with "audio/", e.g. "audio/wav", "audio/ogg")',
|
|
83
|
+
),
|
|
84
|
+
source: z
|
|
85
|
+
.string()
|
|
86
|
+
.optional()
|
|
87
|
+
.describe(
|
|
88
|
+
"Optional source identifier for analytics (e.g. 'dictation', 'voice-mode')",
|
|
89
|
+
),
|
|
90
|
+
}),
|
|
91
|
+
handler: async ({ req }) => {
|
|
92
|
+
// -- Parse body -------------------------------------------------------
|
|
93
|
+
let body: {
|
|
94
|
+
audioBase64?: unknown;
|
|
95
|
+
mimeType?: unknown;
|
|
96
|
+
source?: unknown;
|
|
97
|
+
};
|
|
98
|
+
try {
|
|
99
|
+
body = (await req.json()) as typeof body;
|
|
100
|
+
} catch {
|
|
101
|
+
return httpError("BAD_REQUEST", "Invalid JSON body", 400);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (!body || typeof body !== "object") {
|
|
105
|
+
return httpError("BAD_REQUEST", "Invalid JSON body", 400);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// -- Validate audioBase64 ---------------------------------------------
|
|
109
|
+
if (
|
|
110
|
+
!body.audioBase64 ||
|
|
111
|
+
typeof body.audioBase64 !== "string" ||
|
|
112
|
+
body.audioBase64.length === 0
|
|
113
|
+
) {
|
|
114
|
+
return httpError(
|
|
115
|
+
"BAD_REQUEST",
|
|
116
|
+
"audioBase64 is required and must be a non-empty string",
|
|
117
|
+
400,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// -- Validate mimeType ------------------------------------------------
|
|
122
|
+
if (
|
|
123
|
+
!body.mimeType ||
|
|
124
|
+
typeof body.mimeType !== "string" ||
|
|
125
|
+
!body.mimeType.startsWith("audio/")
|
|
126
|
+
) {
|
|
127
|
+
return httpError(
|
|
128
|
+
"BAD_REQUEST",
|
|
129
|
+
'mimeType is required and must start with "audio/"',
|
|
130
|
+
400,
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// -- Decode audio -----------------------------------------------------
|
|
135
|
+
// Buffer.from(str, "base64") silently accepts malformed input rather
|
|
136
|
+
// than throwing, so we validate the characters explicitly first.
|
|
137
|
+
const base64Str = body.audioBase64 as string;
|
|
138
|
+
if (
|
|
139
|
+
!/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(
|
|
140
|
+
base64Str,
|
|
141
|
+
)
|
|
142
|
+
) {
|
|
143
|
+
return httpError(
|
|
144
|
+
"BAD_REQUEST",
|
|
145
|
+
"Invalid base64 encoding in audioBase64",
|
|
146
|
+
400,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
let audioBuffer: Buffer;
|
|
151
|
+
try {
|
|
152
|
+
audioBuffer = Buffer.from(base64Str, "base64");
|
|
153
|
+
} catch {
|
|
154
|
+
return httpError(
|
|
155
|
+
"BAD_REQUEST",
|
|
156
|
+
"audioBase64 could not be decoded",
|
|
157
|
+
400,
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (audioBuffer.length === 0) {
|
|
162
|
+
return httpError(
|
|
163
|
+
"BAD_REQUEST",
|
|
164
|
+
"Decoded audio payload is empty",
|
|
165
|
+
400,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// -- Resolve transcriber ----------------------------------------------
|
|
170
|
+
let transcriber;
|
|
171
|
+
try {
|
|
172
|
+
transcriber = await resolveBatchTranscriber();
|
|
173
|
+
} catch (err) {
|
|
174
|
+
log.error({ err }, "Failed to resolve STT transcriber");
|
|
175
|
+
return httpError(
|
|
176
|
+
"SERVICE_UNAVAILABLE",
|
|
177
|
+
"STT provider is not available",
|
|
178
|
+
503,
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (!transcriber) {
|
|
183
|
+
return httpError(
|
|
184
|
+
"SERVICE_UNAVAILABLE",
|
|
185
|
+
"No speech-to-text provider is configured",
|
|
186
|
+
503,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// -- Transcribe with timeout ------------------------------------------
|
|
191
|
+
const abortController = new AbortController();
|
|
192
|
+
const timeoutId = setTimeout(
|
|
193
|
+
() => abortController.abort(),
|
|
194
|
+
TRANSCRIPTION_TIMEOUT_MS,
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
const result = await transcriber.transcribe({
|
|
199
|
+
audio: audioBuffer,
|
|
200
|
+
mimeType: body.mimeType as string,
|
|
201
|
+
signal: abortController.signal,
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
return Response.json({
|
|
205
|
+
text: result.text,
|
|
206
|
+
providerId: transcriber.providerId,
|
|
207
|
+
boundaryId: transcriber.boundaryId,
|
|
208
|
+
});
|
|
209
|
+
} catch (err) {
|
|
210
|
+
const sttErr = normalizeSttError(err);
|
|
211
|
+
const mapped = STT_ERROR_MAP[sttErr.category];
|
|
212
|
+
|
|
213
|
+
log.warn(
|
|
214
|
+
{
|
|
215
|
+
category: sttErr.category,
|
|
216
|
+
message: sttErr.message,
|
|
217
|
+
source: body.source,
|
|
218
|
+
},
|
|
219
|
+
"STT transcription failed",
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
return httpError(
|
|
223
|
+
mapped.code as Parameters<typeof httpError>[0],
|
|
224
|
+
mapped.message,
|
|
225
|
+
mapped.status,
|
|
226
|
+
);
|
|
227
|
+
} finally {
|
|
228
|
+
clearTimeout(timeoutId);
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
];
|
|
233
|
+
}
|
|
@@ -27,7 +27,7 @@ interface SurfaceActionTarget {
|
|
|
27
27
|
surfaceId: string,
|
|
28
28
|
actionId: string,
|
|
29
29
|
data?: Record<string, unknown>,
|
|
30
|
-
): void
|
|
30
|
+
): void | Promise<unknown>;
|
|
31
31
|
handleSurfaceUndo?(surfaceId: string): void;
|
|
32
32
|
setTrustContext?(ctx: {
|
|
33
33
|
trustClass: "guardian" | "trusted_contact" | "unknown";
|
|
@@ -143,11 +143,44 @@ export async function handleSurfaceAction(
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
try {
|
|
146
|
-
|
|
146
|
+
// Most action paths return `void` (regular button/selection forwards);
|
|
147
|
+
// the `launch_conversation` dispatch branch returns a structured result
|
|
148
|
+
// so we can surface validation errors (e.g. missing title / seedPrompt)
|
|
149
|
+
// as 4xx responses instead of silently reporting success.
|
|
150
|
+
const raw = await conversation.handleSurfaceAction(
|
|
151
|
+
surfaceId,
|
|
152
|
+
actionId,
|
|
153
|
+
data,
|
|
154
|
+
);
|
|
155
|
+
const result =
|
|
156
|
+
raw && typeof raw === "object" && "accepted" in raw
|
|
157
|
+
? (raw as
|
|
158
|
+
| { accepted: true; conversationId?: string }
|
|
159
|
+
| { accepted: false; error: string })
|
|
160
|
+
: undefined;
|
|
161
|
+
if (result && result.accepted === false) {
|
|
162
|
+
log.warn(
|
|
163
|
+
{
|
|
164
|
+
conversationId: conversationId ?? undefined,
|
|
165
|
+
surfaceId,
|
|
166
|
+
actionId,
|
|
167
|
+
error: result.error,
|
|
168
|
+
},
|
|
169
|
+
"Surface action rejected",
|
|
170
|
+
);
|
|
171
|
+
return httpError("BAD_REQUEST", result.error, 400);
|
|
172
|
+
}
|
|
147
173
|
log.info(
|
|
148
174
|
{ conversationId: conversationId ?? undefined, surfaceId, actionId },
|
|
149
175
|
"Surface action handled via HTTP",
|
|
150
176
|
);
|
|
177
|
+
if (
|
|
178
|
+
result &&
|
|
179
|
+
result.accepted === true &&
|
|
180
|
+
typeof result.conversationId === "string"
|
|
181
|
+
) {
|
|
182
|
+
return Response.json({ ok: true, conversationId: result.conversationId });
|
|
183
|
+
}
|
|
151
184
|
return Response.json({ ok: true });
|
|
152
185
|
} catch (err) {
|
|
153
186
|
log.error(
|
|
@@ -239,6 +272,12 @@ export function surfaceActionRouteDefinitions(deps: {
|
|
|
239
272
|
}),
|
|
240
273
|
responseBody: z.object({
|
|
241
274
|
ok: z.boolean(),
|
|
275
|
+
conversationId: z
|
|
276
|
+
.string()
|
|
277
|
+
.describe(
|
|
278
|
+
"Id of a newly launched conversation when the action dispatched one (e.g. launch_conversation). Omitted otherwise.",
|
|
279
|
+
)
|
|
280
|
+
.optional(),
|
|
242
281
|
}),
|
|
243
282
|
handler: async ({ req, authContext }) => {
|
|
244
283
|
if (!deps.findConversation) {
|
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* HTTP route definitions for
|
|
2
|
+
* HTTP route definitions for text-to-speech synthesis.
|
|
3
3
|
*
|
|
4
4
|
* POST /v1/messages/:id/tts?conversationId=... — synthesize message text to audio
|
|
5
|
+
* POST /v1/tts/synthesize — synthesize arbitrary text to audio
|
|
5
6
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
7
|
+
* Both endpoints use the globally configured TTS provider via the provider
|
|
8
|
+
* abstraction. The message endpoint is gated behind the `message-tts`
|
|
9
|
+
* assistant feature flag; the generic endpoint is always available when a
|
|
10
|
+
* TTS provider is configured.
|
|
8
11
|
*/
|
|
9
12
|
|
|
10
|
-
import {
|
|
13
|
+
import { z } from "zod";
|
|
14
|
+
|
|
11
15
|
import { sanitizeForTts } from "../../calls/tts-text-sanitizer.js";
|
|
12
16
|
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
13
17
|
import { getConfig } from "../../config/loader.js";
|
|
14
18
|
import { getMessageContent } from "../../daemon/handlers/conversation-history.js";
|
|
19
|
+
import { synthesizeText } from "../../tts/synthesize-text.js";
|
|
15
20
|
import { getLogger } from "../../util/logger.js";
|
|
16
21
|
import { httpError } from "../http-errors.js";
|
|
17
22
|
import type { RouteDefinition } from "../http-router.js";
|
|
@@ -32,7 +37,7 @@ export function ttsRouteDefinitions(): RouteDefinition[] {
|
|
|
32
37
|
policyKey: "messages/tts",
|
|
33
38
|
summary: "Synthesize message to speech",
|
|
34
39
|
description:
|
|
35
|
-
"Synthesize a message's text content to audio using
|
|
40
|
+
"Synthesize a message's text content to audio using the configured TTS provider.",
|
|
36
41
|
tags: ["messages"],
|
|
37
42
|
queryParams: [
|
|
38
43
|
{
|
|
@@ -70,35 +75,114 @@ export function ttsRouteDefinitions(): RouteDefinition[] {
|
|
|
70
75
|
);
|
|
71
76
|
}
|
|
72
77
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
)
|
|
78
|
+
try {
|
|
79
|
+
const { audio, contentType } = await synthesizeText({
|
|
80
|
+
text: sanitizedText,
|
|
81
|
+
useCase: "message-playback",
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return new Response(new Uint8Array(audio), {
|
|
85
|
+
status: 200,
|
|
86
|
+
headers: { "Content-Type": contentType },
|
|
87
|
+
});
|
|
88
|
+
} catch (err) {
|
|
89
|
+
log.error({ err, messageId }, "TTS synthesis failed");
|
|
90
|
+
|
|
91
|
+
// Surface provider-not-configured as 503
|
|
92
|
+
if (
|
|
93
|
+
err instanceof Error &&
|
|
94
|
+
"code" in err &&
|
|
95
|
+
(err as { code: string }).code === "TTS_PROVIDER_NOT_CONFIGURED"
|
|
96
|
+
) {
|
|
97
|
+
return httpError(
|
|
98
|
+
"SERVICE_UNAVAILABLE",
|
|
99
|
+
"TTS provider is not configured",
|
|
100
|
+
503,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return httpError("INTERNAL_ERROR", "TTS synthesis failed", 502);
|
|
80
105
|
}
|
|
106
|
+
},
|
|
107
|
+
},
|
|
81
108
|
|
|
109
|
+
// -- Generic text synthesis -----------------------------------------------
|
|
110
|
+
|
|
111
|
+
{
|
|
112
|
+
endpoint: "tts/synthesize",
|
|
113
|
+
method: "POST",
|
|
114
|
+
policyKey: "tts/synthesize",
|
|
115
|
+
summary: "Synthesize text to speech",
|
|
116
|
+
description:
|
|
117
|
+
"Synthesize arbitrary text to audio using the configured TTS provider. " +
|
|
118
|
+
"Provider selection is resolved globally via config — callers do not " +
|
|
119
|
+
"specify a provider.",
|
|
120
|
+
tags: ["tts"],
|
|
121
|
+
requestBody: z.object({
|
|
122
|
+
text: z.string().describe("Text to synthesize into speech"),
|
|
123
|
+
context: z
|
|
124
|
+
.string()
|
|
125
|
+
.optional()
|
|
126
|
+
.describe(
|
|
127
|
+
"Optional context hint for output policy or capability selection (e.g. voice-mode). " +
|
|
128
|
+
"Does not affect provider selection.",
|
|
129
|
+
),
|
|
130
|
+
conversationId: z
|
|
131
|
+
.string()
|
|
132
|
+
.optional()
|
|
133
|
+
.describe("Optional conversation ID for scoping or analytics."),
|
|
134
|
+
}),
|
|
135
|
+
handler: async ({ req }) => {
|
|
136
|
+
let body: { text?: string; context?: string; conversationId?: string };
|
|
82
137
|
try {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
138
|
+
body = (await req.json()) as typeof body;
|
|
139
|
+
} catch {
|
|
140
|
+
return httpError("BAD_REQUEST", "Invalid JSON body", 400);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (!body || typeof body !== "object") {
|
|
144
|
+
return httpError("BAD_REQUEST", "Invalid JSON body", 400);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!body.text || typeof body.text !== "string") {
|
|
148
|
+
return httpError("BAD_REQUEST", "text is required", 400);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const sanitizedText = sanitizeForTts(body.text).trim();
|
|
152
|
+
if (!sanitizedText) {
|
|
153
|
+
return httpError(
|
|
154
|
+
"BAD_REQUEST",
|
|
155
|
+
"Text has no speakable content after sanitization",
|
|
156
|
+
400,
|
|
86
157
|
);
|
|
158
|
+
}
|
|
87
159
|
|
|
88
|
-
|
|
89
|
-
const contentType =
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
? "audio/opus"
|
|
94
|
-
: "audio/mpeg";
|
|
160
|
+
try {
|
|
161
|
+
const { audio, contentType } = await synthesizeText({
|
|
162
|
+
text: sanitizedText,
|
|
163
|
+
useCase: "message-playback",
|
|
164
|
+
});
|
|
95
165
|
|
|
96
|
-
return new Response(new Uint8Array(
|
|
166
|
+
return new Response(new Uint8Array(audio), {
|
|
97
167
|
status: 200,
|
|
98
168
|
headers: { "Content-Type": contentType },
|
|
99
169
|
});
|
|
100
170
|
} catch (err) {
|
|
101
|
-
log.error({ err,
|
|
171
|
+
log.error({ err, context: body.context }, "TTS synthesis failed");
|
|
172
|
+
|
|
173
|
+
// Surface provider-not-configured as 503
|
|
174
|
+
if (
|
|
175
|
+
err instanceof Error &&
|
|
176
|
+
"code" in err &&
|
|
177
|
+
(err as { code: string }).code === "TTS_PROVIDER_NOT_CONFIGURED"
|
|
178
|
+
) {
|
|
179
|
+
return httpError(
|
|
180
|
+
"SERVICE_UNAVAILABLE",
|
|
181
|
+
"TTS provider is not configured",
|
|
182
|
+
503,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
|
|
102
186
|
return httpError("INTERNAL_ERROR", "TTS synthesis failed", 502);
|
|
103
187
|
}
|
|
104
188
|
},
|
|
@@ -14,11 +14,30 @@ import {
|
|
|
14
14
|
getUsageHourBuckets,
|
|
15
15
|
getUsageTotals,
|
|
16
16
|
} from "../../memory/llm-usage-store.js";
|
|
17
|
+
import { validateTimezone } from "../../memory/usage-buckets.js";
|
|
17
18
|
import { httpError } from "../http-errors.js";
|
|
18
19
|
import type { RouteDefinition } from "../http-router.js";
|
|
19
20
|
|
|
20
21
|
const VALID_GROUP_BY = new Set(["actor", "provider", "model", "conversation"]);
|
|
21
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Resolve the optional `tz` query param to a validated IANA identifier.
|
|
25
|
+
* Returns the tz string, or an error Response if the value is not a valid tz.
|
|
26
|
+
*/
|
|
27
|
+
function resolveTimezone(url: URL): string | Response {
|
|
28
|
+
const tz = url.searchParams.get("tz") ?? "UTC";
|
|
29
|
+
try {
|
|
30
|
+
validateTimezone(tz);
|
|
31
|
+
} catch (err) {
|
|
32
|
+
return httpError(
|
|
33
|
+
"BAD_REQUEST",
|
|
34
|
+
(err as Error).message,
|
|
35
|
+
400,
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
return tz;
|
|
39
|
+
}
|
|
40
|
+
|
|
22
41
|
/**
|
|
23
42
|
* Parse and validate the `from` and `to` epoch-millis query parameters.
|
|
24
43
|
* Returns the parsed range or an error Response.
|
|
@@ -110,6 +129,12 @@ export function usageRouteDefinitions(): RouteDefinition[] {
|
|
|
110
129
|
schema: { type: "string", enum: ["daily", "hourly"] },
|
|
111
130
|
description: 'Bucket granularity: "daily" (default) or "hourly"',
|
|
112
131
|
},
|
|
132
|
+
{
|
|
133
|
+
name: "tz",
|
|
134
|
+
schema: { type: "string" },
|
|
135
|
+
description:
|
|
136
|
+
'IANA timezone identifier (e.g. "America/Los_Angeles"). Bucket boundaries and display labels are computed in this timezone. Defaults to "UTC" for backwards compatibility.',
|
|
137
|
+
},
|
|
113
138
|
],
|
|
114
139
|
responseBody: z.object({
|
|
115
140
|
buckets: z.array(z.unknown()).describe("Usage bucket objects"),
|
|
@@ -125,10 +150,13 @@ export function usageRouteDefinitions(): RouteDefinition[] {
|
|
|
125
150
|
400,
|
|
126
151
|
);
|
|
127
152
|
}
|
|
153
|
+
const tz = resolveTimezone(url);
|
|
154
|
+
if (tz instanceof Response) return tz;
|
|
155
|
+
// The chart wants a continuous time axis, so fill empty buckets.
|
|
128
156
|
const buckets =
|
|
129
157
|
granularity === "hourly"
|
|
130
|
-
? getUsageHourBuckets(range)
|
|
131
|
-
: getUsageDayBuckets(range);
|
|
158
|
+
? getUsageHourBuckets(range, tz, { fillEmpty: true })
|
|
159
|
+
: getUsageDayBuckets(range, tz, { fillEmpty: true });
|
|
132
160
|
return Response.json({ buckets });
|
|
133
161
|
},
|
|
134
162
|
},
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
* exports named functions for HTTP methods (GET, POST, PUT, etc.) using
|
|
7
7
|
* the standard Web API Request/Response signature.
|
|
8
8
|
*
|
|
9
|
+
* Handlers receive a second `context` argument with runtime singletons
|
|
10
|
+
* (event hub, assistant ID, etc.) that would otherwise be unreachable
|
|
11
|
+
* from dynamically imported modules because Bun's cache-busting import
|
|
12
|
+
* creates separate module instances.
|
|
13
|
+
*
|
|
9
14
|
* Modules are lazily loaded on first request and cached by file path +
|
|
10
15
|
* mtime. When a file changes on disk, the next request reloads it via
|
|
11
16
|
* Bun's dynamic `import()` with a cache-busting query parameter.
|
|
@@ -16,10 +21,35 @@ import { join, resolve } from "node:path";
|
|
|
16
21
|
|
|
17
22
|
import { getLogger } from "../../util/logger.js";
|
|
18
23
|
import { getWorkspaceRoutesDir } from "../../util/platform.js";
|
|
24
|
+
import type { AssistantEventHub } from "../assistant-event-hub.js";
|
|
19
25
|
import { httpError } from "../http-errors.js";
|
|
20
26
|
|
|
21
27
|
const log = getLogger("user-routes");
|
|
22
28
|
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// User route context — injected into every handler as the second argument
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Runtime context passed to user-defined route handlers.
|
|
35
|
+
*
|
|
36
|
+
* Because user route modules are loaded via dynamic `import()` with
|
|
37
|
+
* cache-busting query parameters, they get isolated module instances
|
|
38
|
+
* and cannot import process-level singletons like the event hub
|
|
39
|
+
* directly. This context bridges the gap by carrying references to
|
|
40
|
+
* the daemon's real singletons.
|
|
41
|
+
*/
|
|
42
|
+
export interface UserRouteContext {
|
|
43
|
+
/** The daemon's event hub singleton — use this to publish events to connected SSE clients. */
|
|
44
|
+
readonly assistantEventHub: AssistantEventHub;
|
|
45
|
+
/** The logical assistant ID used by the daemon (typically "self"). */
|
|
46
|
+
readonly assistantId: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Route handler types
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
23
53
|
/** HTTP methods that can be exported from a handler module. */
|
|
24
54
|
const HTTP_METHODS = [
|
|
25
55
|
"GET",
|
|
@@ -33,8 +63,18 @@ const HTTP_METHODS = [
|
|
|
33
63
|
|
|
34
64
|
type HttpMethod = (typeof HTTP_METHODS)[number];
|
|
35
65
|
|
|
36
|
-
/**
|
|
37
|
-
|
|
66
|
+
/**
|
|
67
|
+
* The function signature that user-defined route handlers must follow.
|
|
68
|
+
*
|
|
69
|
+
* Handlers may accept an optional second `context` argument with runtime
|
|
70
|
+
* singletons (event hub, assistant ID). Legacy handlers that only accept
|
|
71
|
+
* `request` continue to work — the context is passed positionally but
|
|
72
|
+
* ignored if the handler doesn't declare the parameter.
|
|
73
|
+
*/
|
|
74
|
+
type RouteHandler = (
|
|
75
|
+
request: Request,
|
|
76
|
+
context: UserRouteContext,
|
|
77
|
+
) => Response | Promise<Response>;
|
|
38
78
|
|
|
39
79
|
/** A loaded handler module with its cached metadata. */
|
|
40
80
|
interface CachedModule {
|
|
@@ -55,10 +95,15 @@ const HANDLER_EXTENSIONS = [".ts", ".js"] as const;
|
|
|
55
95
|
export class UserRouteDispatcher {
|
|
56
96
|
private moduleCache = new Map<string, CachedModule>();
|
|
57
97
|
private handlerTimeoutMs: number;
|
|
98
|
+
private context: UserRouteContext;
|
|
58
99
|
|
|
59
|
-
constructor(options
|
|
100
|
+
constructor(options: {
|
|
101
|
+
handlerTimeoutMs?: number;
|
|
102
|
+
context: UserRouteContext;
|
|
103
|
+
}) {
|
|
60
104
|
this.handlerTimeoutMs =
|
|
61
|
-
options
|
|
105
|
+
options.handlerTimeoutMs ?? DEFAULT_HANDLER_TIMEOUT_MS;
|
|
106
|
+
this.context = Object.freeze({ ...options.context });
|
|
62
107
|
}
|
|
63
108
|
|
|
64
109
|
/**
|
|
@@ -192,7 +237,7 @@ export class UserRouteDispatcher {
|
|
|
192
237
|
): Promise<Response> {
|
|
193
238
|
try {
|
|
194
239
|
const result = await Promise.race([
|
|
195
|
-
Promise.resolve(handler(request)),
|
|
240
|
+
Promise.resolve(handler(request, this.context)),
|
|
196
241
|
new Promise<never>((_, reject) =>
|
|
197
242
|
setTimeout(
|
|
198
243
|
() => reject(new Error("Handler timed out")),
|
|
@@ -4,12 +4,24 @@
|
|
|
4
4
|
* Registers a single catch-all route that delegates to the
|
|
5
5
|
* UserRouteDispatcher for file-based dispatch from
|
|
6
6
|
* `$VELLUM_WORKSPACE_DIR/routes/`.
|
|
7
|
+
*
|
|
8
|
+
* The dispatcher injects a `UserRouteContext` into every handler so
|
|
9
|
+
* that dynamically imported route modules can access daemon singletons
|
|
10
|
+
* (event hub, assistant ID) without relying on module-level imports
|
|
11
|
+
* that would resolve to separate instances due to cache-busting.
|
|
7
12
|
*/
|
|
8
13
|
|
|
14
|
+
import { assistantEventHub } from "../assistant-event-hub.js";
|
|
15
|
+
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
9
16
|
import type { RouteDefinition } from "../http-router.js";
|
|
10
17
|
import { UserRouteDispatcher } from "./user-route-dispatcher.js";
|
|
11
18
|
|
|
12
|
-
const dispatcher = new UserRouteDispatcher(
|
|
19
|
+
const dispatcher = new UserRouteDispatcher({
|
|
20
|
+
context: {
|
|
21
|
+
assistantEventHub,
|
|
22
|
+
assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
|
|
23
|
+
},
|
|
24
|
+
});
|
|
13
25
|
|
|
14
26
|
/**
|
|
15
27
|
* HTTP methods supported by user-defined route handlers.
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
getToolDescription,
|
|
20
20
|
sanitizeToolList,
|
|
21
21
|
} from "../../tasks/tool-sanitizer.js";
|
|
22
|
+
import { createAbortReason } from "../../util/abort-reasons.js";
|
|
22
23
|
import { getLogger } from "../../util/logger.js";
|
|
23
24
|
import { truncate } from "../../util/truncate.js";
|
|
24
25
|
import { resolveRequiredTools } from "../../work-items/resolve-required-tools.js";
|
|
@@ -587,7 +588,13 @@ export function workItemRouteDefinitions(
|
|
|
587
588
|
const conversation = deps.findConversation(conversationId);
|
|
588
589
|
if (conversation) {
|
|
589
590
|
conversation.headlessLock = false;
|
|
590
|
-
conversation.abort(
|
|
591
|
+
conversation.abort(
|
|
592
|
+
createAbortReason(
|
|
593
|
+
"work_item_aborted",
|
|
594
|
+
"work-items-routes.cancel",
|
|
595
|
+
conversationId,
|
|
596
|
+
),
|
|
597
|
+
);
|
|
591
598
|
getSubagentManager().abortAllForParent(conversationId);
|
|
592
599
|
}
|
|
593
600
|
}
|