@vellumai/assistant 0.11.3 → 0.11.4-staging.1
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 +11 -6
- package/docs/architecture/memory.md +11 -0
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/flux-turn-detection-spike.md +243 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/openapi.yaml +140 -38
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +2 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +28 -20
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +55 -4
- package/src/__tests__/conversation-runtime-assembly.test.ts +134 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/edit-propagation.test.ts +0 -7
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +6 -5
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +1 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -0
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +434 -105
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-spawn-and-await.test.ts +14 -10
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/user-plugin-loader.test.ts +1 -1
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +68 -2
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/index.ts +7 -0
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +173 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +94 -0
- package/src/calls/call-controller.ts +9 -2
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +71 -16
- package/src/calls/voice-triage-escalate.ts +104 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +43 -1
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +13 -0
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +4 -5
- package/src/config/default-profile-catalog.ts +82 -11
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +17 -6
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +9 -1
- package/src/daemon/conversation-process.ts +9 -6
- package/src/daemon/conversation-runtime-assembly.ts +3 -4
- package/src/daemon/conversation-tool-setup.ts +1 -2
- package/src/daemon/conversation.ts +48 -0
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -3
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/documents/document-store.ts +138 -5
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +50 -6
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +535 -314
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +108 -41
- package/src/persistence/delivery-crud.ts +38 -29
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/conversation-turn.ts +168 -5
- package/src/plugin-api/index.ts +21 -5
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/memory/AGENTS.md +4 -0
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +8 -12
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +33 -0
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +199 -2
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +25 -18
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +23 -49
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +5 -2
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-intents.ts +26 -6
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/worker.ts +17 -5
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +3 -3
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +219 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +62 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +112 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/inbound-message-handler.ts +17 -41
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +1 -3
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -4
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +11 -20
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/plugins-routes.ts +21 -3
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +39 -14
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/manager.ts +1 -3
- package/src/subagent/types.ts +7 -6
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +32 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/registry.ts +2 -1
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +63 -0
- package/src/tts/reasoning-tag-filter.ts +110 -0
- package/src/workspace/byok-default-profile-ensure.ts +61 -18
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
|
@@ -27,9 +27,12 @@
|
|
|
27
27
|
* shows no route depends on it.
|
|
28
28
|
*
|
|
29
29
|
* Modules are lazily loaded on first request and cached by file path +
|
|
30
|
-
* mtime. When a file changes on disk, the next request
|
|
31
|
-
*
|
|
32
|
-
*
|
|
30
|
+
* mtime. When a file changes on disk, the next request evicts the whole
|
|
31
|
+
* source tree that file belongs to (plugin root, or the workspace
|
|
32
|
+
* `routes/` directory) and re-imports the entry. Cache-busting only the
|
|
33
|
+
* entry file would re-bind it to stale helpers still in Bun's registry.
|
|
34
|
+
* A request whose file does not exist 404s. Nothing is registered ahead
|
|
35
|
+
* of time.
|
|
33
36
|
*/
|
|
34
37
|
|
|
35
38
|
import { statSync } from "node:fs";
|
|
@@ -46,6 +49,11 @@ import {
|
|
|
46
49
|
buildDeprecatedRouteContext,
|
|
47
50
|
type UserRouteContext,
|
|
48
51
|
} from "./deprecated-route-context.js";
|
|
52
|
+
import {
|
|
53
|
+
evictRouteSourceTree,
|
|
54
|
+
importRouteModule,
|
|
55
|
+
routeSourceRoot,
|
|
56
|
+
} from "./user-route-import.js";
|
|
49
57
|
import {
|
|
50
58
|
resolveHandlerFile,
|
|
51
59
|
resolveRouteLocation,
|
|
@@ -134,7 +142,7 @@ export class UserRouteDispatcher {
|
|
|
134
142
|
? resolveHandlerFile(location.routesDir, location.subPath)
|
|
135
143
|
: null;
|
|
136
144
|
|
|
137
|
-
if (!filePath) {
|
|
145
|
+
if (!location || !filePath) {
|
|
138
146
|
return httpError(
|
|
139
147
|
"NOT_FOUND",
|
|
140
148
|
`No route handler found for /x/${routePath}`,
|
|
@@ -146,7 +154,7 @@ export class UserRouteDispatcher {
|
|
|
146
154
|
return this.dispatchViaHost(filePath, routePath, request);
|
|
147
155
|
}
|
|
148
156
|
|
|
149
|
-
const mod = await this.loadModule(filePath);
|
|
157
|
+
const mod = await this.loadModule(filePath, location.routesDir);
|
|
150
158
|
const method = request.method as HttpMethod;
|
|
151
159
|
const handler = mod.handlers[method];
|
|
152
160
|
|
|
@@ -231,11 +239,15 @@ export class UserRouteDispatcher {
|
|
|
231
239
|
/**
|
|
232
240
|
* Load a handler module, using the mtime-based cache when possible.
|
|
233
241
|
*
|
|
234
|
-
* On cache miss or stale mtime,
|
|
235
|
-
*
|
|
236
|
-
*
|
|
242
|
+
* On cache miss or stale mtime, every source file in the handler's tree
|
|
243
|
+
* is evicted first so a new entry cannot import a helper that is still
|
|
244
|
+
* the pre-upgrade module. Then the entry is re-imported with an mtime
|
|
245
|
+
* query parameter.
|
|
237
246
|
*/
|
|
238
|
-
private async loadModule(
|
|
247
|
+
private async loadModule(
|
|
248
|
+
filePath: string,
|
|
249
|
+
routesDir: string,
|
|
250
|
+
): Promise<CachedModule> {
|
|
239
251
|
const stat = statSync(filePath);
|
|
240
252
|
const mtimeMs = stat.mtimeMs;
|
|
241
253
|
|
|
@@ -244,11 +256,11 @@ export class UserRouteDispatcher {
|
|
|
244
256
|
return cached;
|
|
245
257
|
}
|
|
246
258
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
259
|
+
const sourceRoot = routeSourceRoot(routesDir);
|
|
260
|
+
evictRouteSourceTree(sourceRoot);
|
|
261
|
+
this.dropCachedModulesUnder(sourceRoot);
|
|
262
|
+
|
|
263
|
+
const mod = await importRouteModule(filePath);
|
|
252
264
|
|
|
253
265
|
const handlers: Partial<Record<HttpMethod, RouteHandler>> = {};
|
|
254
266
|
for (const method of HTTP_METHODS) {
|
|
@@ -271,6 +283,19 @@ export class UserRouteDispatcher {
|
|
|
271
283
|
return entry;
|
|
272
284
|
}
|
|
273
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Drop every cached handler under `sourceRoot`. Sibling routes must not
|
|
288
|
+
* keep closures over the helpers we just evicted.
|
|
289
|
+
*/
|
|
290
|
+
private dropCachedModulesUnder(sourceRoot: string): void {
|
|
291
|
+
const prefix = sourceRoot.endsWith("/") ? sourceRoot : `${sourceRoot}/`;
|
|
292
|
+
for (const path of this.moduleCache.keys()) {
|
|
293
|
+
if (path === sourceRoot || path.startsWith(prefix)) {
|
|
294
|
+
this.moduleCache.delete(path);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
274
299
|
/**
|
|
275
300
|
* Execute a handler function with a per-request timeout and error boundary.
|
|
276
301
|
*/
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Import a user/plugin route module so its helpers match the files on disk.
|
|
3
|
+
*
|
|
4
|
+
* Bun caches every imported helper independently. Cache-busting only the
|
|
5
|
+
* entry file (`import(file?t=mtime)`) re-binds it to whatever helpers are
|
|
6
|
+
* already in the registry. That is how a new route that imports a new export
|
|
7
|
+
* from `src/http.ts` 500s with "Export named 'requireNumber' not found" while
|
|
8
|
+
* `/frame` (still on the old http.ts surface) keeps answering 200.
|
|
9
|
+
*
|
|
10
|
+
* Hooks and tools already treat the whole plugin directory as the reload
|
|
11
|
+
* unit (`plugins/source-fingerprint.ts`). Routes must do the same: when the
|
|
12
|
+
* entry file's mtime moves, evict every source file in that tree before the
|
|
13
|
+
* next import, so the new entry cannot pair with a stale helper.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { statSync } from "node:fs";
|
|
17
|
+
import { dirname, sep } from "node:path";
|
|
18
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
19
|
+
|
|
20
|
+
import { snapshotPluginSource } from "../../plugins/source-fingerprint.js";
|
|
21
|
+
import { evictModule } from "../../plugins/surface-import.js";
|
|
22
|
+
import { getWorkspaceRoutesDir } from "../../util/platform.js";
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Directory whose source files must stay mutually consistent with this
|
|
26
|
+
* handler. An installed or default plugin uses the plugin root (so `src/`
|
|
27
|
+
* helpers evict with `routes/`). Workspace routes use the workspace
|
|
28
|
+
* `routes/` directory.
|
|
29
|
+
*/
|
|
30
|
+
export function routeSourceRoot(routesDir: string): string {
|
|
31
|
+
if (routesDir === getWorkspaceRoutesDir()) {
|
|
32
|
+
return routesDir;
|
|
33
|
+
}
|
|
34
|
+
return dirname(routesDir);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Infer the source root from a handler path when the caller has no
|
|
39
|
+
* {@link routeSourceRoot} (the route-host worker only sees `filePath`).
|
|
40
|
+
*/
|
|
41
|
+
export function sourceRootForHandler(filePath: string): string {
|
|
42
|
+
const marker = `${sep}routes${sep}`;
|
|
43
|
+
const idx = filePath.lastIndexOf(marker);
|
|
44
|
+
if (idx !== -1) {
|
|
45
|
+
return filePath.slice(0, idx);
|
|
46
|
+
}
|
|
47
|
+
return dirname(filePath);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Drop every source module under `sourceRoot` from the runtime registry,
|
|
52
|
+
* including query-string aliases (`file.ts?t=mtime`) the dispatcher uses to
|
|
53
|
+
* cache-bust the entry file.
|
|
54
|
+
*/
|
|
55
|
+
export function evictRouteSourceTree(sourceRoot: string): void {
|
|
56
|
+
const { evictionPaths } = snapshotPluginSource(sourceRoot);
|
|
57
|
+
for (const path of evictionPaths) {
|
|
58
|
+
evictModule(path);
|
|
59
|
+
}
|
|
60
|
+
evictRegistryAliases(sourceRoot);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function evictRegistryAliases(sourceRoot: string): void {
|
|
64
|
+
const prefix = sourceRoot.endsWith("/") ? sourceRoot : `${sourceRoot}/`;
|
|
65
|
+
const fileUrlPrefix = pathToFileURL(prefix).href;
|
|
66
|
+
for (const key of Object.keys(require.cache)) {
|
|
67
|
+
if (!keyBelongsToTree(key, sourceRoot, prefix, fileUrlPrefix)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
delete require.cache[key];
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function keyBelongsToTree(
|
|
75
|
+
key: string,
|
|
76
|
+
sourceRoot: string,
|
|
77
|
+
prefix: string,
|
|
78
|
+
fileUrlPrefix: string,
|
|
79
|
+
): boolean {
|
|
80
|
+
const bare = key.split("?")[0] ?? key;
|
|
81
|
+
if (bare.includes("/node_modules/") || bare.includes("/node_modules?")) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
if (
|
|
85
|
+
bare === sourceRoot ||
|
|
86
|
+
bare.startsWith(prefix) ||
|
|
87
|
+
bare.startsWith(fileUrlPrefix)
|
|
88
|
+
) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
if (bare.startsWith("file:")) {
|
|
92
|
+
try {
|
|
93
|
+
const asPath = fileURLToPath(bare);
|
|
94
|
+
return asPath === sourceRoot || asPath.startsWith(prefix);
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/** Dynamic-import a handler, cache-busted by the file's current mtime. */
|
|
103
|
+
export async function importRouteModule(
|
|
104
|
+
filePath: string,
|
|
105
|
+
): Promise<Record<string, unknown>> {
|
|
106
|
+
const mtimeMs = statSync(filePath).mtimeMs;
|
|
107
|
+
return (await import(`${filePath}?t=${mtimeMs}`)) as Record<string, unknown>;
|
|
108
|
+
}
|
package/src/schedule/worker.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { writeFileSync } from "node:fs";
|
|
|
16
16
|
import { getConfig } from "../config/loader.js";
|
|
17
17
|
import { rehydratePlatformCredentials } from "../config/platform-rehydration.js";
|
|
18
18
|
import { resetDb } from "../persistence/db-connection.js";
|
|
19
|
+
import { registerWorkerPluginSurface } from "../plugins/worker-plugin-surface.js";
|
|
19
20
|
import { disableStreamSeqStamping } from "../runtime/assistant-stream-state.js";
|
|
20
21
|
import { initializeTools } from "../tools/registry.js";
|
|
21
22
|
import { getLogger } from "../util/logger.js";
|
|
@@ -51,6 +52,11 @@ async function main(): Promise<void> {
|
|
|
51
52
|
// rejected for both inference and background-wake requests.
|
|
52
53
|
await rehydratePlatformCredentials();
|
|
53
54
|
|
|
55
|
+
// This process is the sole runner of schedule execution and hosts real agent
|
|
56
|
+
// conversations (wake, execute, and workflow modes), so it needs the default
|
|
57
|
+
// plugins' hooks and injectors just like the daemon.
|
|
58
|
+
registerWorkerPluginSurface();
|
|
59
|
+
|
|
54
60
|
// Populate the tool registry (core built-ins + workspace tools). The daemon
|
|
55
61
|
// does this at startup; this standalone process has to do it itself so
|
|
56
62
|
// workflow schedules pass the core-tools readiness gate and agent-executed
|
package/src/security/oauth2.ts
CHANGED
|
@@ -273,20 +273,12 @@ async function runGatewayFlow(
|
|
|
273
273
|
): Promise<OAuth2FlowResult> {
|
|
274
274
|
// Dynamic imports required here to avoid circular dependencies with
|
|
275
275
|
// config/loader → security → oauth2 module chains.
|
|
276
|
-
const {
|
|
277
|
-
|
|
278
|
-
await import("../inbound/public-ingress-urls.js");
|
|
279
|
-
const { resolveCallbackUrl } =
|
|
280
|
-
await import("../inbound/platform-callback-registration.js");
|
|
276
|
+
const { resolveOauthCallbackUrl } =
|
|
277
|
+
await import("../inbound/oauth-callback-url.js");
|
|
281
278
|
const { registerPendingCallback } =
|
|
282
279
|
await import("./oauth-callback-registry.js");
|
|
283
280
|
|
|
284
|
-
const
|
|
285
|
-
const redirectUri = await resolveCallbackUrl(
|
|
286
|
-
() => getOAuthCallbackUrl(appConfig),
|
|
287
|
-
"webhooks/oauth/callback",
|
|
288
|
-
"oauth",
|
|
289
|
-
);
|
|
281
|
+
const redirectUri = await resolveOauthCallbackUrl();
|
|
290
282
|
|
|
291
283
|
const codePromise = new Promise<string>((resolve, reject) => {
|
|
292
284
|
registerPendingCallback(state, resolve, reject);
|
|
@@ -556,20 +548,12 @@ export async function prepareOAuth2Flow(
|
|
|
556
548
|
|
|
557
549
|
// Dynamic imports required here to avoid circular dependencies with
|
|
558
550
|
// config/loader → security → oauth2 module chains.
|
|
559
|
-
const {
|
|
560
|
-
|
|
561
|
-
await import("../inbound/public-ingress-urls.js");
|
|
562
|
-
const { resolveCallbackUrl } =
|
|
563
|
-
await import("../inbound/platform-callback-registration.js");
|
|
551
|
+
const { resolveOauthCallbackUrl } =
|
|
552
|
+
await import("../inbound/oauth-callback-url.js");
|
|
564
553
|
const { registerPendingCallback } =
|
|
565
554
|
await import("./oauth-callback-registry.js");
|
|
566
555
|
|
|
567
|
-
const
|
|
568
|
-
const redirectUri = await resolveCallbackUrl(
|
|
569
|
-
() => getOAuthCallbackUrl(appConfig),
|
|
570
|
-
"webhooks/oauth/callback",
|
|
571
|
-
"oauth",
|
|
572
|
-
);
|
|
556
|
+
const redirectUri = await resolveOauthCallbackUrl();
|
|
573
557
|
|
|
574
558
|
const codeVerifier = generateCodeVerifier();
|
|
575
559
|
const codeChallenge = generateCodeChallenge(codeVerifier);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import { batchBoundaryGapReason } from "../../providers/speech-to-text/provider-catalog.js";
|
|
3
4
|
import { SttError } from "../types.js";
|
|
4
5
|
|
|
5
6
|
// ---------------------------------------------------------------------------
|
|
@@ -620,3 +621,24 @@ describe("vellum factory case", () => {
|
|
|
620
621
|
expect(createDaemonBatchTranscriber(null, "deepgram")).toBeNull();
|
|
621
622
|
});
|
|
622
623
|
});
|
|
624
|
+
|
|
625
|
+
describe("deepgram-flux factory case", () => {
|
|
626
|
+
// The copy itself is pinned in provider-catalog.test.ts; what matters here
|
|
627
|
+
// is that the factory speaks with the catalog's voice rather than its own.
|
|
628
|
+
test("throws the catalog's streaming-only reason", () => {
|
|
629
|
+
expect(() =>
|
|
630
|
+
createDaemonBatchTranscriber("dg-test-key", "deepgram-flux"),
|
|
631
|
+
).toThrow(batchBoundaryGapReason("deepgram-flux"));
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
test("marks the error user-facing so friendly copy does not overwrite it", () => {
|
|
635
|
+
try {
|
|
636
|
+
createDaemonBatchTranscriber("dg-test-key", "deepgram-flux");
|
|
637
|
+
throw new Error("expected the factory to throw");
|
|
638
|
+
} catch (err) {
|
|
639
|
+
expect(err).toBeInstanceOf(SttError);
|
|
640
|
+
expect((err as SttError).category).toBe("provider-error");
|
|
641
|
+
expect((err as SttError).userFacing).toBe(true);
|
|
642
|
+
}
|
|
643
|
+
});
|
|
644
|
+
});
|
|
@@ -2,10 +2,14 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import type {
|
|
4
4
|
SttStreamServerClosedEvent,
|
|
5
|
+
SttStreamServerEagerTurnEndEvent,
|
|
5
6
|
SttStreamServerErrorEvent,
|
|
6
7
|
SttStreamServerEvent,
|
|
7
8
|
SttStreamServerFinalEvent,
|
|
8
9
|
SttStreamServerPartialEvent,
|
|
10
|
+
SttStreamServerTurnEndEvent,
|
|
11
|
+
SttStreamServerTurnResumedEvent,
|
|
12
|
+
SttStreamServerTurnStartEvent,
|
|
9
13
|
} from "../types.js";
|
|
10
14
|
|
|
11
15
|
// ---------------------------------------------------------------------------
|
|
@@ -87,3 +91,93 @@ describe("SttStreamServerEvent types", () => {
|
|
|
87
91
|
expect(_label).toBeUndefined();
|
|
88
92
|
});
|
|
89
93
|
});
|
|
94
|
+
|
|
95
|
+
describe("turn-detection server events", () => {
|
|
96
|
+
test("turn-start event compiles and round-trips", () => {
|
|
97
|
+
const event: SttStreamServerTurnStartEvent = { type: "turn-start" };
|
|
98
|
+
const asUnion: SttStreamServerEvent = event;
|
|
99
|
+
expect(asUnion.type).toBe("turn-start");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test("eager-turn-end event carries the speculated transcript", () => {
|
|
103
|
+
const event: SttStreamServerEagerTurnEndEvent = {
|
|
104
|
+
type: "eager-turn-end",
|
|
105
|
+
text: "is this the end",
|
|
106
|
+
};
|
|
107
|
+
const asUnion: SttStreamServerEvent = event;
|
|
108
|
+
expect(asUnion.type).toBe("eager-turn-end");
|
|
109
|
+
expect(event.text).toBe("is this the end");
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test("turn-resumed event compiles and round-trips", () => {
|
|
113
|
+
const event: SttStreamServerTurnResumedEvent = { type: "turn-resumed" };
|
|
114
|
+
const asUnion: SttStreamServerEvent = event;
|
|
115
|
+
expect(asUnion.type).toBe("turn-resumed");
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("turn-end event carries the committed transcript and optional confidence", () => {
|
|
119
|
+
const withoutConfidence: SttStreamServerTurnEndEvent = {
|
|
120
|
+
type: "turn-end",
|
|
121
|
+
text: "done speaking",
|
|
122
|
+
};
|
|
123
|
+
expect(withoutConfidence.confidence).toBeUndefined();
|
|
124
|
+
|
|
125
|
+
const event: SttStreamServerTurnEndEvent = {
|
|
126
|
+
type: "turn-end",
|
|
127
|
+
text: "done speaking",
|
|
128
|
+
confidence: 0.82,
|
|
129
|
+
};
|
|
130
|
+
const asUnion: SttStreamServerEvent = event;
|
|
131
|
+
expect(asUnion.type).toBe("turn-end");
|
|
132
|
+
expect(event.text).toBe("done speaking");
|
|
133
|
+
expect(event.confidence).toBe(0.82);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("a consumer switch over every variant is exhaustive", () => {
|
|
137
|
+
// Fails to compile if a variant is added to SttStreamServerEvent
|
|
138
|
+
// without a case here, which is what forces consumers to be revisited.
|
|
139
|
+
const label = (event: SttStreamServerEvent): string => {
|
|
140
|
+
switch (event.type) {
|
|
141
|
+
case "partial":
|
|
142
|
+
case "final":
|
|
143
|
+
case "finalized":
|
|
144
|
+
case "error":
|
|
145
|
+
case "closed":
|
|
146
|
+
return "transcript";
|
|
147
|
+
case "turn-start":
|
|
148
|
+
case "eager-turn-end":
|
|
149
|
+
case "turn-resumed":
|
|
150
|
+
case "turn-end":
|
|
151
|
+
return "turn";
|
|
152
|
+
default: {
|
|
153
|
+
const _exhaustive: never = event;
|
|
154
|
+
return _exhaustive;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const events: SttStreamServerEvent[] = [
|
|
160
|
+
{ type: "partial", text: "a" },
|
|
161
|
+
{ type: "final", text: "a" },
|
|
162
|
+
{ type: "finalized" },
|
|
163
|
+
{ type: "error", category: "provider-error", message: "boom" },
|
|
164
|
+
{ type: "closed" },
|
|
165
|
+
{ type: "turn-start" },
|
|
166
|
+
{ type: "eager-turn-end", text: "a" },
|
|
167
|
+
{ type: "turn-resumed" },
|
|
168
|
+
{ type: "turn-end", text: "a" },
|
|
169
|
+
];
|
|
170
|
+
|
|
171
|
+
expect(events.map(label)).toEqual([
|
|
172
|
+
"transcript",
|
|
173
|
+
"transcript",
|
|
174
|
+
"transcript",
|
|
175
|
+
"transcript",
|
|
176
|
+
"transcript",
|
|
177
|
+
"turn",
|
|
178
|
+
"turn",
|
|
179
|
+
"turn",
|
|
180
|
+
"turn",
|
|
181
|
+
]);
|
|
182
|
+
});
|
|
183
|
+
});
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* - xAI (`xai`)
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import { batchBoundaryGapReason } from "../providers/speech-to-text/provider-catalog.js";
|
|
16
17
|
import type {
|
|
17
18
|
BatchTranscriber,
|
|
18
19
|
SttProviderId,
|
|
@@ -245,6 +246,9 @@ class VellumManagedBatchTranscriber implements BatchTranscriber {
|
|
|
245
246
|
* language code), and the vellum managed path, whose platform proxy passes
|
|
246
247
|
* it to Deepgram server-side. Whisper and Gemini auto-detect natively and
|
|
247
248
|
* take no language parameter, so it is silently ignored for them.
|
|
249
|
+
*
|
|
250
|
+
* Throws an {@link SttError} for a provider with no batch endpoint at all,
|
|
251
|
+
* which no key can fix and `null` would understate.
|
|
248
252
|
*/
|
|
249
253
|
export function createDaemonBatchTranscriber(
|
|
250
254
|
apiKey: string | null | undefined,
|
|
@@ -268,6 +272,12 @@ export function createDaemonBatchTranscriber(
|
|
|
268
272
|
return new GoogleGeminiBatchTranscriber(apiKey);
|
|
269
273
|
case "xai":
|
|
270
274
|
return new XAIBatchTranscriber(apiKey, language);
|
|
275
|
+
case "deepgram-flux":
|
|
276
|
+
// Same copy the resolver raises, so a direct factory caller and a
|
|
277
|
+
// config-driven one report the mismatch identically.
|
|
278
|
+
throw new SttError("provider-error", batchBoundaryGapReason(providerId), {
|
|
279
|
+
userFacing: true,
|
|
280
|
+
});
|
|
271
281
|
default: {
|
|
272
282
|
// Exhaustive check — compile error if a new SttProviderId is added
|
|
273
283
|
// without a corresponding case here.
|
|
@@ -5,8 +5,11 @@
|
|
|
5
5
|
* a client WebSocket connection and a provider-specific streaming adapter.
|
|
6
6
|
* The orchestrator accepts client events (`audio`, `stop`), routes audio
|
|
7
7
|
* frames to the resolved {@link StreamingTranscriber}, and emits normalized
|
|
8
|
-
* server events
|
|
9
|
-
*
|
|
8
|
+
* server events back over the same WebSocket connection with per-session
|
|
9
|
+
* ordering guarantees: transcripts (`partial`, `final`, `finalized`),
|
|
10
|
+
* turn boundaries from providers that detect them (`turn-start`,
|
|
11
|
+
* `eager-turn-end`, `turn-resumed`, `turn-end`), and termination
|
|
12
|
+
* (`error`, `closed`).
|
|
10
13
|
*
|
|
11
14
|
* Session lifecycle:
|
|
12
15
|
* 1. Client opens a WebSocket to `/v1/stt/stream` with required `mimeType`
|
|
@@ -17,8 +20,9 @@
|
|
|
17
20
|
* `resolveStreamingTranscriber()` and starts the provider session.
|
|
18
21
|
* 3. The client sends `audio` frames (binary or base64-encoded JSON) and
|
|
19
22
|
* a `stop` event when recording is complete.
|
|
20
|
-
* 4. The provider emits
|
|
21
|
-
* orchestrator forwards to the client as
|
|
23
|
+
* 4. The provider emits transcript events, plus turn-boundary events when it
|
|
24
|
+
* detects turns, and the orchestrator forwards each to the client as a
|
|
25
|
+
* JSON frame.
|
|
22
26
|
* 5. The session closes deterministically on client disconnect, `stop`
|
|
23
27
|
* event, idle timeout, or runtime shutdown.
|
|
24
28
|
*
|
package/src/stt/types.ts
CHANGED
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
export type SttProviderId =
|
|
24
24
|
| "openai-whisper"
|
|
25
25
|
| "deepgram"
|
|
26
|
+
/**
|
|
27
|
+
* Deepgram's Flux conversational speech model. Shares the `deepgram`
|
|
28
|
+
* credential and is streaming-only: Flux has no batch endpoint.
|
|
29
|
+
*/
|
|
30
|
+
| "deepgram-flux"
|
|
26
31
|
| "google-gemini"
|
|
27
32
|
| "xai"
|
|
28
33
|
| "vellum";
|
|
@@ -65,6 +70,26 @@ export type ConversationStreamingMode =
|
|
|
65
70
|
| "incremental-batch"
|
|
66
71
|
| "none";
|
|
67
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Turn-detection support mode.
|
|
75
|
+
*
|
|
76
|
+
* Describes whether a provider decides end-of-turn itself, in-band with the
|
|
77
|
+
* audio it is already transcribing, rather than leaving the boundary to the
|
|
78
|
+
* session's local silence timer and front-door hold verdict.
|
|
79
|
+
*
|
|
80
|
+
* - `"provider"`: the provider emits turn lifecycle events (`turn-start`,
|
|
81
|
+
* `turn-end`) on its transcript stream, and a live-voice session may let
|
|
82
|
+
* those events commit the turn.
|
|
83
|
+
* - `"none"`: the provider transcribes only; the local silence boundary owns
|
|
84
|
+
* end-of-turn.
|
|
85
|
+
*
|
|
86
|
+
* A provider declaring `"provider"` should number its turns: the staleness
|
|
87
|
+
* check prefers the turn index carried on the event, and degrades to the
|
|
88
|
+
* session's local VAD generation counter when one is absent, which understates
|
|
89
|
+
* the recorded latency for a turn the caller resumed and stopped again.
|
|
90
|
+
*/
|
|
91
|
+
export type SttTurnDetectionMode = "provider" | "none";
|
|
92
|
+
|
|
68
93
|
// ---------------------------------------------------------------------------
|
|
69
94
|
// Boundary identifier
|
|
70
95
|
// ---------------------------------------------------------------------------
|
|
@@ -245,6 +270,10 @@ export type SttStreamServerEvent =
|
|
|
245
270
|
| SttStreamServerPartialEvent
|
|
246
271
|
| SttStreamServerFinalEvent
|
|
247
272
|
| SttStreamServerFinalizedEvent
|
|
273
|
+
| SttStreamServerTurnStartEvent
|
|
274
|
+
| SttStreamServerEagerTurnEndEvent
|
|
275
|
+
| SttStreamServerTurnResumedEvent
|
|
276
|
+
| SttStreamServerTurnEndEvent
|
|
248
277
|
| SttStreamServerErrorEvent
|
|
249
278
|
| SttStreamServerClosedEvent;
|
|
250
279
|
|
|
@@ -314,6 +343,80 @@ export interface SttStreamServerFinalizedEvent {
|
|
|
314
343
|
readonly type: "finalized";
|
|
315
344
|
}
|
|
316
345
|
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
// Turn-detection events
|
|
348
|
+
//
|
|
349
|
+
// Consumers that ignore these keep committing transcripts from `final`
|
|
350
|
+
// exactly as they do without a turn-detecting provider.
|
|
351
|
+
// ---------------------------------------------------------------------------
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* The provider's turn model detected the start of a user turn.
|
|
355
|
+
*
|
|
356
|
+
* Emitted only by providers with model-integrated turn detection; providers
|
|
357
|
+
* without it never emit these.
|
|
358
|
+
*/
|
|
359
|
+
export interface SttStreamServerTurnStartEvent {
|
|
360
|
+
readonly type: "turn-start";
|
|
361
|
+
/**
|
|
362
|
+
* Provider's index for the turn being opened, monotonically increasing
|
|
363
|
+
* within a stream. Undefined when the provider does not number its turns.
|
|
364
|
+
*/
|
|
365
|
+
readonly turnIndex?: number;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* The provider's turn model speculates the user has finished, ahead of its
|
|
370
|
+
* committed end-of-turn decision. A {@link SttStreamServerTurnResumedEvent}
|
|
371
|
+
* retracts the speculation; a {@link SttStreamServerTurnEndEvent} confirms it.
|
|
372
|
+
*
|
|
373
|
+
* Emitted only by providers with model-integrated turn detection; providers
|
|
374
|
+
* without it never emit these.
|
|
375
|
+
*/
|
|
376
|
+
export interface SttStreamServerEagerTurnEndEvent {
|
|
377
|
+
readonly type: "eager-turn-end";
|
|
378
|
+
/** Transcript text as of the speculated turn end. */
|
|
379
|
+
readonly text: string;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* The user kept speaking after an {@link SttStreamServerEagerTurnEndEvent},
|
|
384
|
+
* retracting the speculated turn end.
|
|
385
|
+
*
|
|
386
|
+
* Emitted only by providers with model-integrated turn detection; providers
|
|
387
|
+
* without it never emit these.
|
|
388
|
+
*/
|
|
389
|
+
export interface SttStreamServerTurnResumedEvent {
|
|
390
|
+
readonly type: "turn-resumed";
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* The provider's turn model committed an end of turn: the user has finished
|
|
395
|
+
* speaking and a reply can be dispatched.
|
|
396
|
+
*
|
|
397
|
+
* Emitted only by providers with model-integrated turn detection; providers
|
|
398
|
+
* without it never emit these.
|
|
399
|
+
*/
|
|
400
|
+
export interface SttStreamServerTurnEndEvent {
|
|
401
|
+
readonly type: "turn-end";
|
|
402
|
+
/** Committed transcript text for the completed turn. */
|
|
403
|
+
readonly text: string;
|
|
404
|
+
/**
|
|
405
|
+
* Provider-emitted end-of-turn confidence in [0, 1]. Undefined when the
|
|
406
|
+
* provider does not surface a score.
|
|
407
|
+
*/
|
|
408
|
+
readonly confidence?: number;
|
|
409
|
+
/**
|
|
410
|
+
* Provider's index for the turn being closed, matching the
|
|
411
|
+
* {@link SttStreamServerTurnStartEvent} that opened it. A consumer can
|
|
412
|
+
* compare it against the newest turn it has seen opened to tell an
|
|
413
|
+
* end-of-turn for the turn still in progress from one the provider has
|
|
414
|
+
* already superseded. Undefined when the provider does not number its
|
|
415
|
+
* turns.
|
|
416
|
+
*/
|
|
417
|
+
readonly turnIndex?: number;
|
|
418
|
+
}
|
|
419
|
+
|
|
317
420
|
/** An error occurred during streaming transcription. */
|
|
318
421
|
export interface SttStreamServerErrorEvent {
|
|
319
422
|
readonly type: "error";
|
package/src/subagent/manager.ts
CHANGED
|
@@ -705,9 +705,7 @@ export class SubagentManager {
|
|
|
705
705
|
// Inherit the parent's *turn* trust ahead of its conversation-level slot:
|
|
706
706
|
// the slot holds whichever actor sent most recently, so a spawn during one
|
|
707
707
|
// actor's turn would otherwise run under another's privileges.
|
|
708
|
-
const parentTurnTrust =
|
|
709
|
-
parentConversation?.currentTurnTrustContext ??
|
|
710
|
-
parentConversation?.trustContext;
|
|
708
|
+
const parentTurnTrust = parentConversation?.getTurnOrRestingTrust();
|
|
711
709
|
if (config.trustContext) {
|
|
712
710
|
conversation.setTrustContext({ ...config.trustContext });
|
|
713
711
|
} else if (parentTurnTrust) {
|
package/src/subagent/types.ts
CHANGED
|
@@ -483,12 +483,13 @@ export const SUBAGENT_ROLE_REGISTRY: Record<SubagentRole, SubagentRoleConfig> =
|
|
|
483
483
|
skillIds: [],
|
|
484
484
|
systemPromptPreamble: [
|
|
485
485
|
"You are a research subagent with read-only access: search the web, read and search files, and recall memories. There is no shell, and you cannot write or edit files.",
|
|
486
|
-
// `file_read`
|
|
487
|
-
//
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
486
|
+
// `file_read` returns the first DEFAULT_READ_LINE_LIMIT (2000) lines
|
|
487
|
+
// unless a limit is passed, with a truncation notice naming the resume
|
|
488
|
+
// offset, and oversized results spool to .tool-results/ like any other
|
|
489
|
+
// tool's (only re-reads of spooled content stay inline). So a ranged
|
|
490
|
+
// read is both the cheap shape and the one that avoids a spool
|
|
491
|
+
// round-trip. The anti-slicing intent stays: one pass over the range
|
|
492
|
+
// that is needed rather than many small ones.
|
|
492
493
|
"Working method: use code_search to search file contents across directories, file_list to enumerate paths, and file_read to read files and logs. Prefer broad code_search queries across a directory over one-symbol-at-a-time queries, and read the range you need in one pass rather than many small slices.",
|
|
493
494
|
"Send notify_parent (urgency 'important') as soon as each finding is confirmed, so progress survives interruption.",
|
|
494
495
|
"Your final message is the deliverable: a compact report that answers the objective, gives the evidence behind each claim (file:line references, URLs, or quotes), and names what you could not determine. For a root-cause investigation, use the sections Symptom, Root cause, Evidence, Suggested fix, Open questions.",
|