@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e
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 +1 -1
- package/Dockerfile +8 -0
- package/docs/activation-funnel-telemetry.md +13 -7
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/openapi.yaml +530 -107
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +8 -0
- package/src/__tests__/activation-early-marking.test.ts +6 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
- package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
- package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
- package/src/__tests__/app-bundler.test.ts +4 -12
- package/src/__tests__/app-executors.test.ts +2 -49
- package/src/__tests__/app-routes-csp.test.ts +114 -146
- package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
- package/src/__tests__/build-persisted-content.test.ts +96 -0
- package/src/__tests__/bundle-scanner.test.ts +27 -1
- package/src/__tests__/call-controller.test.ts +291 -0
- package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
- package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
- package/src/__tests__/config-loader-backfill.test.ts +9 -4
- package/src/__tests__/config-schema-cmd.test.ts +10 -11
- package/src/__tests__/config-schema.test.ts +190 -257
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
- package/src/__tests__/conversation-agent-loop.test.ts +22 -9
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
- package/src/__tests__/conversation-process-callsite.test.ts +12 -19
- package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
- package/src/__tests__/conversation-summarize-route.test.ts +36 -44
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
- package/src/__tests__/conversation-usage.test.ts +4 -12
- package/src/__tests__/credential-routes.test.ts +250 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/db-migration-rollback.test.ts +22 -0
- package/src/__tests__/empty-response-hook.test.ts +186 -1
- package/src/__tests__/external-plugin-loader.test.ts +11 -5
- package/src/__tests__/heartbeat-service.test.ts +0 -28
- package/src/__tests__/host-shell-tool.test.ts +2 -0
- package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
- package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -16
- package/src/__tests__/list-all-apps.test.ts +0 -4
- package/src/__tests__/llm-context-resolution.test.ts +32 -56
- package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
- package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
- package/src/__tests__/llm-resolver.test.ts +342 -602
- package/src/__tests__/llm-schema.test.ts +79 -37
- package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
- package/src/__tests__/media-stream-output.test.ts +259 -3
- package/src/__tests__/media-stream-server-integration.test.ts +22 -1
- package/src/__tests__/media-stream-stt-session.test.ts +47 -0
- package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
- package/src/__tests__/memory-recall-log-store.test.ts +47 -13
- package/src/__tests__/mock-gateway-ipc.ts +46 -1
- package/src/__tests__/mtime-cache.test.ts +61 -0
- package/src/__tests__/navigate-settings-tab.test.ts +2 -0
- package/src/__tests__/normalize-onboarding.test.ts +33 -0
- package/src/__tests__/onboarding-persona-write.test.ts +26 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
- package/src/__tests__/provider-connections-backfill.test.ts +138 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
- package/src/__tests__/provider-registry-ollama.test.ts +8 -18
- package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
- package/src/__tests__/provider-usage-tracking.test.ts +9 -19
- package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
- package/src/__tests__/published-app-updater.test.ts +22 -16
- package/src/__tests__/registry.test.ts +5 -16
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
- package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
- package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
- package/src/__tests__/reveal-success-registry.test.ts +123 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
- package/src/__tests__/secret-fixtures.ts +9 -0
- package/src/__tests__/server-history-render.test.ts +28 -0
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/slack-share-routes.test.ts +0 -1
- package/src/__tests__/stt-stream-session.test.ts +6 -5
- package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
- package/src/__tests__/subagent-disposal.test.ts +2 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +37 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
- package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
- package/src/__tests__/telemetry-routes.test.ts +99 -19
- package/src/__tests__/tool-audit.test.ts +34 -4
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-profiler.test.ts +72 -1
- package/src/__tests__/tool-result-spool.test.ts +49 -4
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
- package/src/__tests__/ui-channel-variants.test.ts +108 -0
- package/src/__tests__/ui-shape-teaching.test.ts +255 -0
- package/src/__tests__/usage-attribution.test.ts +18 -41
- package/src/__tests__/user-plugin-loader.test.ts +4 -4
- package/src/__tests__/voice-config-update.test.ts +46 -0
- package/src/__tests__/voice-session-bridge.test.ts +216 -84
- package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
- package/src/agent/loop.ts +4 -3
- package/src/api/events/open-conversation.test.ts +64 -0
- package/src/api/events/open-conversation.ts +33 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +25 -35
- package/src/bundler/app-bundler.ts +34 -48
- package/src/bundler/app-compiler.ts +39 -4
- package/src/bundler/bundle-scanner.ts +13 -0
- package/src/bundler/manifest.ts +1 -1
- package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
- package/src/calls/call-constants.ts +5 -0
- package/src/calls/call-controller.ts +100 -32
- package/src/calls/call-transport.ts +9 -0
- package/src/calls/media-stream-output.ts +107 -4
- package/src/calls/media-stream-server.ts +29 -9
- package/src/calls/media-stream-stt-session.ts +7 -1
- package/src/calls/media-turn-detector.ts +11 -1
- package/src/calls/voice-session-bridge.ts +84 -63
- package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
- package/src/cli/commands/__tests__/notifications.test.ts +24 -3
- package/src/cli/commands/config.help.ts +6 -6
- package/src/cli/commands/credentials.help.ts +13 -0
- package/src/cli/commands/credentials.ts +6 -1
- package/src/cli/commands/email.help.ts +7 -0
- package/src/cli/commands/email.ts +35 -1
- package/src/cli/commands/inference-providers.ts +168 -107
- package/src/cli/commands/inference.help.ts +118 -46
- package/src/cli/commands/memory/index.help.ts +23 -0
- package/src/cli/commands/memory/nodes.ts +146 -0
- package/src/cli/commands/notifications.help.ts +10 -10
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
- package/src/cli/commands/oauth/connect.test.ts +126 -0
- package/src/cli/commands/oauth/connect.ts +29 -6
- package/src/cli/commands/oauth/index.help.ts +7 -1
- package/src/cli/commands/oauth/status.test.ts +69 -3
- package/src/cli/commands/oauth/status.ts +50 -12
- package/src/cli/commands/plugins.help.ts +13 -2
- package/src/cli/commands/plugins.ts +61 -7
- package/src/cli/commands/telemetry.help.ts +13 -0
- package/src/cli/commands/telemetry.ts +45 -5
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
- package/src/cli/lib/bundled-marketplace.json +93 -36
- package/src/cli/lib/inspect-plugin.ts +5 -1
- package/src/cli/lib/install-from-github.ts +68 -4
- package/src/cli/lib/plugin-catalog-local.ts +6 -2
- package/src/cli/lib/plugin-fingerprint.ts +3 -3
- package/src/cli/lib/upgrade-plugin.ts +236 -35
- package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
- package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
- package/src/config/__tests__/profile-materialization.test.ts +1 -88
- package/src/config/bundled-skills/AGENTS.md +3 -30
- package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
- package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
- package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
- package/src/config/bundled-skills/subagent/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/call-site-defaults.ts +3 -3
- package/src/config/feature-flag-registry.json +24 -39
- package/src/config/llm-resolver.ts +88 -475
- package/src/config/profile-materialization.ts +11 -11
- package/src/config/schema.ts +93 -127
- package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
- package/src/config/schemas/__tests__/stt.test.ts +31 -3
- package/src/config/schemas/live-voice.ts +10 -4
- package/src/config/schemas/llm.ts +52 -15
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +8 -0
- package/src/config/schemas/memory-v2.ts +2 -2
- package/src/config/schemas/memory-v3.ts +1 -1
- package/src/config/schemas/services.ts +6 -3
- package/src/config/schemas/stt.ts +38 -21
- package/src/config/schemas/tts.ts +13 -18
- package/src/config/skills.ts +31 -21
- package/src/context/compactor.ts +44 -7
- package/src/context/post-turn-tool-result-truncation.ts +4 -2
- package/src/context/tool-result-spool.ts +26 -5
- package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
- package/src/conversations/message-consolidation.ts +22 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
- package/src/daemon/app-source-watcher.ts +17 -23
- package/src/daemon/chat-credential-redaction.ts +1365 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
- package/src/daemon/conversation-agent-loop.ts +71 -31
- package/src/daemon/conversation-error.ts +36 -14
- package/src/daemon/conversation-process.ts +22 -0
- package/src/daemon/conversation-store.ts +35 -0
- package/src/daemon/conversation-surfaces.ts +25 -25
- package/src/daemon/conversation-tool-setup.ts +33 -7
- package/src/daemon/conversation.ts +55 -5
- package/src/daemon/handlers/shared.ts +33 -2
- package/src/daemon/lifecycle.ts +4 -4
- package/src/daemon/message-types/conversations.ts +6 -17
- package/src/daemon/providers-setup.ts +8 -0
- package/src/daemon/tool-setup-types.ts +7 -1
- package/src/daemon/wake-conversation-ops.ts +10 -1
- package/src/hooks/types.ts +9 -0
- package/src/ipc/__tests__/email-ipc.test.ts +90 -0
- package/src/ipc/gateway-client.test.ts +59 -0
- package/src/ipc/gateway-client.ts +70 -27
- package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
- package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
- package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
- package/src/live-voice/__tests__/protocol.test.ts +122 -0
- package/src/live-voice/live-voice-session.ts +443 -40
- package/src/live-voice/protocol.ts +143 -1
- package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
- package/src/monitoring/plugin-source-watch.ts +3 -62
- package/src/notifications/README.md +1 -1
- package/src/permissions/checker.ts +8 -4
- package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
- package/src/persistence/conversation-crud.ts +104 -2
- package/src/persistence/db-init.ts +14 -3
- package/src/persistence/job-handlers/cleanup.ts +15 -7
- package/src/persistence/llm-request-log-store.ts +88 -52
- package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
- package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
- package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
- package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
- package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
- package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
- package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
- package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
- package/src/persistence/migrations/helpers/relocation.ts +44 -1
- package/src/persistence/migrations/run-migrations.ts +25 -1
- package/src/persistence/schema/infrastructure.ts +9 -0
- package/src/persistence/schema/memory-core.ts +3 -0
- package/src/persistence/schema/memory-injection.ts +2 -0
- package/src/persistence/steps.ts +36 -0
- package/src/platform/client.test.ts +1 -44
- package/src/platform/client.ts +10 -20
- package/src/platform/consent-cache.test.ts +89 -23
- package/src/platform/consent-cache.ts +71 -33
- package/src/plugin-api/constants.ts +12 -0
- package/src/plugin-api/conversation-turn.ts +37 -14
- package/src/plugin-api/index.ts +11 -1
- package/src/plugin-api/resolve-credential.ts +75 -0
- package/src/plugin-api/vision-support.test.ts +8 -19
- package/src/plugin-api/vision-support.ts +26 -17
- package/src/plugins/collect-source-versions.ts +77 -0
- package/src/plugins/defaults/compaction/compact.ts +6 -0
- package/src/plugins/defaults/compaction/window-manager.ts +9 -0
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
- package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
- package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
- package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
- package/src/plugins/defaults/index.ts +8 -1
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
- package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
- package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
- package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
- package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
- package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
- package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
- package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
- package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
- package/src/plugins/defaults/memory/graph/store.ts +114 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
- package/src/plugins/defaults/memory/graph/tools.ts +47 -13
- package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
- package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
- package/src/plugins/defaults/memory/memory-db.ts +3 -2
- package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
- package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
- package/src/plugins/defaults/memory/path-containment.ts +21 -0
- package/src/plugins/defaults/memory/prompt-override.ts +51 -13
- package/src/plugins/defaults/memory/tools.test.ts +34 -0
- package/src/plugins/defaults/memory/tools.ts +12 -2
- package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
- package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
- package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
- package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
- package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
- package/src/plugins/defaults/memory/v2/migration.ts +46 -16
- package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
- package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
- package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
- package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
- package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
- package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
- package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
- package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
- package/src/plugins/defaults/memory/v3/prune.ts +17 -11
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
- package/src/plugins/external-plugin-loader.ts +15 -15
- package/src/plugins/mtime-cache.ts +47 -0
- package/src/plugins/pipeline.ts +9 -1
- package/src/plugins/plugin-execution-context.ts +44 -0
- package/src/plugins/plugin-tree-walk.ts +31 -24
- package/src/plugins/source-fingerprint.ts +3 -4
- package/src/prompts/normalize-onboarding.ts +12 -0
- package/src/prompts/persona-resolver.ts +8 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
- package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
- package/src/providers/__tests__/retry-callsite.test.ts +235 -215
- package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
- package/src/providers/atlascloud/client.ts +10 -49
- package/src/providers/baseten/client.ts +43 -0
- package/src/providers/call-site-routing.ts +26 -13
- package/src/providers/connection-resolution.ts +9 -11
- package/src/providers/fetch-provider-catalog.ts +4 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
- package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
- package/src/providers/inference/adapter-factory.ts +27 -6
- package/src/providers/inference/auth.ts +29 -1
- package/src/providers/inference/backfill.ts +61 -10
- package/src/providers/inference/connection-availability.ts +3 -2
- package/src/providers/inference/resolve-auth.ts +9 -1
- package/src/providers/model-catalog.ts +37 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
- package/src/providers/openai/api-key-validation.ts +70 -0
- package/src/providers/retry.ts +2 -0
- package/src/providers/types.ts +11 -4
- package/src/providers/vellum-model-routing.test.ts +26 -0
- package/src/providers/vellum-model-routing.ts +30 -0
- package/src/providers/voice-error-copy.ts +47 -0
- package/src/runtime/agent-wake.ts +40 -24
- package/src/runtime/for-chat-mint-registry.ts +118 -0
- package/src/runtime/reveal-nonce.ts +49 -0
- package/src/runtime/reveal-success-registry.ts +306 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
- package/src/runtime/routes/app-management-routes.ts +152 -45
- package/src/runtime/routes/app-routes.ts +16 -77
- package/src/runtime/routes/canned-message-complete.ts +16 -16
- package/src/runtime/routes/conversation-management-routes.ts +17 -14
- package/src/runtime/routes/conversation-query-routes.ts +54 -6
- package/src/runtime/routes/conversation-routes.ts +48 -2
- package/src/runtime/routes/credential-routes.ts +116 -3
- package/src/runtime/routes/email-routes.ts +17 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
- package/src/runtime/routes/inference-send-routes.ts +1 -1
- package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
- package/src/runtime/routes/plugins-routes.ts +37 -6
- package/src/runtime/routes/publish-routes.ts +15 -18
- package/src/runtime/routes/schedule-worker-routes.ts +9 -0
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/telemetry-routes.ts +149 -45
- package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
- package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
- package/src/schedule/schedule-store.ts +12 -1
- package/src/schedule/schedule-timezone.ts +63 -0
- package/src/schedule/scheduler.ts +100 -6
- package/src/schedule/worker-control.ts +19 -0
- package/src/security/auth-fallback-events-store.ts +7 -6
- package/src/security/secret-scanner.ts +26 -1
- package/src/services/published-app-updater.ts +6 -11
- package/src/stt/stt-stream-session.ts +36 -16
- package/src/subagent/manager.ts +34 -8
- package/src/telemetry/AGENTS.md +30 -1
- package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
- package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
- package/src/telemetry/config-setting-snapshot.ts +44 -10
- package/src/telemetry/telemetry-event-sources.test.ts +124 -30
- package/src/telemetry/telemetry-event-sources.ts +137 -83
- package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
- package/src/telemetry/telemetry-events-outbox.ts +60 -9
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire-validation.ts +39 -2
- package/src/telemetry/telemetry-wire.generated.ts +8 -0
- package/src/telemetry/tool-audit.ts +15 -9
- package/src/telemetry/tool-executed-events-store.test.ts +1 -1
- package/src/telemetry/turn-events-store.ts +20 -0
- package/src/telemetry/types.ts +45 -12
- package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
- package/src/telemetry/usage-telemetry-reporter.ts +117 -18
- package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
- package/src/telemetry/watchdog-direct-emit.ts +11 -6
- package/src/tools/apps/executors.ts +11 -36
- package/src/tools/executor.ts +14 -1
- package/src/tools/host-terminal/host-shell.ts +6 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
- package/src/tools/network/web-fetch.ts +6 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/subagent/spawn.ts +7 -11
- package/src/tools/terminal/shell.ts +5 -2
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/tool-profiler.ts +37 -6
- package/src/tools/ui-surface/channel-variants.ts +101 -0
- package/src/tools/ui-surface/definitions.ts +23 -67
- package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
- package/src/tts/__tests__/provider-adapters.test.ts +18 -0
- package/src/tts/provider-catalog.ts +2 -4
- package/src/tts/providers/deepgram-provider.ts +10 -1
- package/src/types/onboarding-context.ts +8 -0
- package/src/usage/attribution.ts +18 -112
- package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
- package/src/util/provider-error-patterns.ts +7 -1
- package/src/util/worker-process.ts +105 -0
- package/src/watcher/__tests__/telemetry.test.ts +17 -4
- package/src/watcher/telemetry.ts +5 -5
- package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/provider-commit-message-generator.ts +6 -4
- package/src/__tests__/app-open-proxy.test.ts +0 -67
- package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
- package/src/onboarding/onboarding-research-events-store.ts +0 -104
- package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
- package/src/tools/apps/definitions.ts +0 -73
- package/src/tools/apps/open-proxy.ts +0 -43
|
@@ -13,6 +13,10 @@ import {
|
|
|
13
13
|
isValidScheduleExpression,
|
|
14
14
|
} from "./recurrence-engine.js";
|
|
15
15
|
import type { ScheduleSyntax } from "./recurrence-types.js";
|
|
16
|
+
import {
|
|
17
|
+
expressionCarriesOwnTimezone,
|
|
18
|
+
resolveScheduleTimezone,
|
|
19
|
+
} from "./schedule-timezone.js";
|
|
16
20
|
|
|
17
21
|
const logger = getLogger("schedule-store");
|
|
18
22
|
|
|
@@ -154,7 +158,14 @@ export async function createSchedule(params: {
|
|
|
154
158
|
const id = uuid();
|
|
155
159
|
const now = Date.now();
|
|
156
160
|
const enabled = params.enabled ?? true;
|
|
157
|
-
|
|
161
|
+
// A recurring wall-clock schedule with no zone otherwise evaluates in the host
|
|
162
|
+
// clock (UTC on managed containers). Default it to the user's configured/detected
|
|
163
|
+
// zone so it fires at the intended local hour. One-shot schedules (absolute epoch)
|
|
164
|
+
// and expressions that carry their own zone keep the caller's value verbatim.
|
|
165
|
+
const timezone =
|
|
166
|
+
isOneShot || expressionCarriesOwnTimezone(syntax, expression)
|
|
167
|
+
? (params.timezone ?? null)
|
|
168
|
+
: resolveScheduleTimezone(params.timezone);
|
|
158
169
|
const mode = params.mode ?? "execute";
|
|
159
170
|
const routingIntent = params.routingIntent ?? "all_channels";
|
|
160
171
|
const routingHints = params.routingHints ?? {};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve the timezone a recurring schedule should be evaluated in.
|
|
3
|
+
*
|
|
4
|
+
* A cron/RRULE wall-clock recurrence needs a zone to disambiguate; when none is
|
|
5
|
+
* supplied the recurrence engine falls back to the daemon host clock (UTC on a
|
|
6
|
+
* managed container), firing at the wrong hour. The rest of the assistant grounds
|
|
7
|
+
* time via the user's configured/detected zone (see `resolveTurnTimezoneContext`
|
|
8
|
+
* in `daemon/date-context.ts`); this brings recurring schedules in line.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { getConfigReadOnly } from "../config/loader.js";
|
|
12
|
+
import { canonicalizeTimeZone } from "../daemon/date-context.js";
|
|
13
|
+
import { hasSetConstructs } from "./recurrence-engine.js";
|
|
14
|
+
import type { ScheduleSyntax } from "./recurrence-types.js";
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* True when the expression already determines its own evaluation zone, so the
|
|
18
|
+
* `timezone` field must not be resolved/overridden:
|
|
19
|
+
* - an RRULE with an embedded `DTSTART;TZID=…` or a Z-anchored UTC `DTSTART`; or
|
|
20
|
+
* - an RRULE set-construct (RDATE/EXDATE/multi-RRULE). `rrulestr` does not thread
|
|
21
|
+
* a caller-supplied tzid into a constructed `RRuleSet`, so a resolved zone would
|
|
22
|
+
* have no effect on firing — persisting one would be misleading.
|
|
23
|
+
*
|
|
24
|
+
* Cron never carries its own zone.
|
|
25
|
+
*/
|
|
26
|
+
export function expressionCarriesOwnTimezone(
|
|
27
|
+
syntax: ScheduleSyntax,
|
|
28
|
+
expression: string | null | undefined,
|
|
29
|
+
): boolean {
|
|
30
|
+
if (syntax !== "rrule" || !expression) {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
if (
|
|
34
|
+
/TZID=/i.test(expression) ||
|
|
35
|
+
/DTSTART[^:]*:\d{8}T\d{6}Z/i.test(expression)
|
|
36
|
+
) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
return hasSetConstructs(expression);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the effective zone for a recurring schedule. An explicit caller value
|
|
44
|
+
* wins (canonicalized); otherwise the user's configured zone, then the
|
|
45
|
+
* client-detected zone. Returns null only when nothing is known — preserving
|
|
46
|
+
* host-local evaluation for local installs where the host clock is the user's.
|
|
47
|
+
*
|
|
48
|
+
* Precedence mirrors `resolveTurnTimezoneContext` (configured beats detected).
|
|
49
|
+
*/
|
|
50
|
+
export function resolveScheduleTimezone(
|
|
51
|
+
explicit: string | null | undefined,
|
|
52
|
+
): string | null {
|
|
53
|
+
const explicitTz = canonicalizeTimeZone(explicit);
|
|
54
|
+
if (explicitTz) {
|
|
55
|
+
return explicitTz;
|
|
56
|
+
}
|
|
57
|
+
const ui = getConfigReadOnly().ui;
|
|
58
|
+
return (
|
|
59
|
+
canonicalizeTimeZone(ui.userTimezone) ??
|
|
60
|
+
canonicalizeTimeZone(ui.detectedTimezone) ??
|
|
61
|
+
null
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -16,7 +16,12 @@ import { runBackgroundJob } from "../runtime/background-job-runner.js";
|
|
|
16
16
|
import { publishConversationListChanged } from "../runtime/sync/resource-sync-events.js";
|
|
17
17
|
import { runSequencesOnce } from "../sequence/engine.js";
|
|
18
18
|
import type { TurnFailure } from "../telemetry/turn-outcome.js";
|
|
19
|
+
import { recordWatchdogEvent } from "../telemetry/watchdog-events-store.js";
|
|
19
20
|
import { getLogger } from "../util/logger.js";
|
|
21
|
+
import {
|
|
22
|
+
createWorkerSupervisor,
|
|
23
|
+
type WorkerSupervisor,
|
|
24
|
+
} from "../util/worker-process.js";
|
|
20
25
|
import { runWatchersOnce } from "../watcher/engine.js";
|
|
21
26
|
import { normalizeCapabilityManifest } from "../workflows/capabilities.js";
|
|
22
27
|
import { getWorkflowRunManager } from "../workflows/run-manager.js";
|
|
@@ -37,7 +42,13 @@ import {
|
|
|
37
42
|
scheduleRetry,
|
|
38
43
|
setScheduleRunConversationId,
|
|
39
44
|
} from "./schedule-store.js";
|
|
40
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
isScheduleWorkerAdministrativelyStopped,
|
|
47
|
+
probeScheduleWorker,
|
|
48
|
+
spawnScheduleWorkerProcess,
|
|
49
|
+
startScheduleWorker,
|
|
50
|
+
stopScheduleWorker,
|
|
51
|
+
} from "./worker-control.js";
|
|
41
52
|
|
|
42
53
|
const log = getLogger("scheduler");
|
|
43
54
|
|
|
@@ -227,6 +238,42 @@ async function handleExecutionFailure(params: {
|
|
|
227
238
|
/** The running scheduler, retained so shutdown can stop it. */
|
|
228
239
|
let instance: SchedulerHandle | null = null;
|
|
229
240
|
|
|
241
|
+
/** The schedule worker liveness watchdog, disposed by {@link stopScheduler}. */
|
|
242
|
+
let scheduleWorkerSupervisor: WorkerSupervisor | null = null;
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Notify the user once per outage when the worker cannot be respawned after
|
|
246
|
+
* repeated attempts, so schedules being paused is not silent. A stable
|
|
247
|
+
* hourly dedupe key keeps it to one notification per outage window rather than
|
|
248
|
+
* one per tick.
|
|
249
|
+
*/
|
|
250
|
+
function emitScheduleWorkerDownNotification(consecutiveFailures: number): void {
|
|
251
|
+
void emitNotificationSignal({
|
|
252
|
+
sourceEventName: "activity.failed",
|
|
253
|
+
sourceChannel: "scheduler",
|
|
254
|
+
sourceContextId: "schedule-worker",
|
|
255
|
+
dedupeKey: `schedule-worker-down:${Math.floor(Date.now() / 3_600_000)}`,
|
|
256
|
+
contextPayload: {
|
|
257
|
+
jobName: "Scheduled tasks",
|
|
258
|
+
errorMessage:
|
|
259
|
+
"The assistant could not restart its schedule runner, so scheduled tasks are paused. They resume automatically once it recovers.",
|
|
260
|
+
errorKind: "exception",
|
|
261
|
+
consecutiveFailures,
|
|
262
|
+
},
|
|
263
|
+
attentionHints: {
|
|
264
|
+
requiresAction: false,
|
|
265
|
+
urgency: "medium",
|
|
266
|
+
isAsyncBackground: true,
|
|
267
|
+
visibleInSourceNow: false,
|
|
268
|
+
},
|
|
269
|
+
}).catch((emitErr) => {
|
|
270
|
+
log.warn(
|
|
271
|
+
{ err: emitErr },
|
|
272
|
+
"Failed to emit schedule-worker-down notification",
|
|
273
|
+
);
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
|
|
230
277
|
export function startScheduler(): SchedulerHandle {
|
|
231
278
|
// Schedule execution is owned by the schedule worker process; spawn it now as
|
|
232
279
|
// a child of the daemon so it is running immediately. Fire-and-forget — a
|
|
@@ -234,6 +281,46 @@ export function startScheduler(): SchedulerHandle {
|
|
|
234
281
|
// watchers and sequences.
|
|
235
282
|
startScheduleWorker();
|
|
236
283
|
|
|
284
|
+
// Liveness watchdog: the worker calls process.exit() on any uncaught error
|
|
285
|
+
// and nothing respawned it, so schedules could stop for days silently. Probe
|
|
286
|
+
// and respawn off the existing tick — recovery latency drops from days to
|
|
287
|
+
// ~one tick. Detects process death, not a wedged-but-alive worker.
|
|
288
|
+
scheduleWorkerSupervisor = createWorkerSupervisor({
|
|
289
|
+
label: "Schedule worker",
|
|
290
|
+
probe: probeScheduleWorker,
|
|
291
|
+
respawn: () => spawnScheduleWorkerProcess({ detached: false }),
|
|
292
|
+
isSuppressed: isScheduleWorkerAdministrativelyStopped,
|
|
293
|
+
killChild: (pid) => {
|
|
294
|
+
try {
|
|
295
|
+
process.kill(pid, "SIGTERM");
|
|
296
|
+
} catch {
|
|
297
|
+
// Worker already gone — nothing to kill.
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
onRespawn: (pid) => {
|
|
301
|
+
log.warn(
|
|
302
|
+
{ pid },
|
|
303
|
+
"Schedule worker was not running — respawned by watchdog",
|
|
304
|
+
);
|
|
305
|
+
recordWatchdogEvent({
|
|
306
|
+
checkName: "schedule_worker_respawn",
|
|
307
|
+
detail: { pid },
|
|
308
|
+
});
|
|
309
|
+
},
|
|
310
|
+
onPersistentFailure: (consecutiveFailures, err) => {
|
|
311
|
+
log.error(
|
|
312
|
+
{ err, consecutiveFailures },
|
|
313
|
+
"Schedule worker repeatedly failed to respawn — schedules are paused",
|
|
314
|
+
);
|
|
315
|
+
recordWatchdogEvent({
|
|
316
|
+
checkName: "schedule_worker_down",
|
|
317
|
+
value: consecutiveFailures,
|
|
318
|
+
detail: { consecutiveFailures },
|
|
319
|
+
});
|
|
320
|
+
emitScheduleWorkerDownNotification(consecutiveFailures);
|
|
321
|
+
},
|
|
322
|
+
});
|
|
323
|
+
|
|
237
324
|
let stopped = false;
|
|
238
325
|
let tickRunning = false;
|
|
239
326
|
|
|
@@ -243,6 +330,9 @@ export function startScheduler(): SchedulerHandle {
|
|
|
243
330
|
}
|
|
244
331
|
tickRunning = true;
|
|
245
332
|
try {
|
|
333
|
+
// Respawn the schedule worker if it has died (fire-and-forget; never
|
|
334
|
+
// throws). Idempotent — a live PID file short-circuits to alreadyRunning.
|
|
335
|
+
void scheduleWorkerSupervisor?.ensureAlive();
|
|
246
336
|
await runScheduleOnce();
|
|
247
337
|
} catch (err) {
|
|
248
338
|
log.error({ err }, "Schedule tick failed");
|
|
@@ -284,12 +374,16 @@ export function startScheduler(): SchedulerHandle {
|
|
|
284
374
|
* worker process if one is running.
|
|
285
375
|
*/
|
|
286
376
|
export function stopScheduler(): void {
|
|
287
|
-
|
|
288
|
-
if (
|
|
289
|
-
|
|
377
|
+
// Stop the tick FIRST so no tick can respawn the worker we are about to kill.
|
|
378
|
+
if (instance) {
|
|
379
|
+
instance.stop();
|
|
380
|
+
instance = null;
|
|
290
381
|
}
|
|
291
|
-
|
|
292
|
-
|
|
382
|
+
// No further respawns; a respawn already in flight kills its child on resolve.
|
|
383
|
+
scheduleWorkerSupervisor?.dispose();
|
|
384
|
+
scheduleWorkerSupervisor = null;
|
|
385
|
+
// Then SIGTERM the running worker via its PID file.
|
|
386
|
+
stopScheduleWorker();
|
|
293
387
|
}
|
|
294
388
|
|
|
295
389
|
/** The running scheduler, or null if one was never started. */
|
|
@@ -21,6 +21,25 @@ import {
|
|
|
21
21
|
|
|
22
22
|
const log = getLogger("schedule-worker-control");
|
|
23
23
|
|
|
24
|
+
/**
|
|
25
|
+
* True when an operator explicitly stopped the worker (via
|
|
26
|
+
* `assistant schedules worker stop`). The liveness watchdog must not respawn it
|
|
27
|
+
* while this is set, or a manual stop would be silently undone within one tick.
|
|
28
|
+
* Process-local: it resets to false on daemon restart and is cleared by the
|
|
29
|
+
* `schedules worker start` route — a restart is treated as a fresh start.
|
|
30
|
+
*/
|
|
31
|
+
let administrativelyStopped = false;
|
|
32
|
+
|
|
33
|
+
/** Whether the schedule worker has been administratively stopped by an operator. */
|
|
34
|
+
export function isScheduleWorkerAdministrativelyStopped(): boolean {
|
|
35
|
+
return administrativelyStopped;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Set/clear the administratively-stopped flag (set by stop, cleared by start). */
|
|
39
|
+
export function setScheduleWorkerAdministrativelyStopped(value: boolean): void {
|
|
40
|
+
administrativelyStopped = value;
|
|
41
|
+
}
|
|
42
|
+
|
|
24
43
|
/**
|
|
25
44
|
* Inspect the PID file to determine whether the schedule worker process is
|
|
26
45
|
* alive. A stale PID file (pointing at a dead process) is cleaned up and
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { v4 as uuid } from "uuid";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { getRawShareAnalytics } from "../platform/consent-cache.js";
|
|
4
4
|
import { insertTelemetryOutboxEvents } from "../telemetry/telemetry-events-outbox.js";
|
|
5
5
|
import type { AuthFallbackTelemetryEvent } from "../telemetry/types.js";
|
|
6
6
|
import { APP_VERSION } from "../version.js";
|
|
@@ -19,17 +19,18 @@ export interface AuthFallbackCount {
|
|
|
19
19
|
* flush window, each carrying its full wire event built at record time. The
|
|
20
20
|
* whole batch inserts atomically, so a failure never leaves a partial batch
|
|
21
21
|
* committed (the gateway retries the full batch on `recorded === 0`). Returns
|
|
22
|
-
* the number of rows recorded, or 0 when
|
|
23
|
-
* (the counts are dropped to honor
|
|
24
|
-
* telemetry) or the telemetry database is
|
|
25
|
-
* that must tell those apart check
|
|
22
|
+
* the number of rows recorded, or 0 when `share_analytics` is a confirmed
|
|
23
|
+
* opt-out (the counts are dropped to honor it, matching the rest of
|
|
24
|
+
* telemetry — an unknown consent state records) or the telemetry database is
|
|
25
|
+
* unavailable (degraded mode). Callers that must tell those apart check
|
|
26
|
+
* `getRawShareAnalytics()` themselves.
|
|
26
27
|
*/
|
|
27
28
|
export function recordAuthFallbackCounts(
|
|
28
29
|
windowStart: number,
|
|
29
30
|
windowEnd: number,
|
|
30
31
|
counts: AuthFallbackCount[],
|
|
31
32
|
): number {
|
|
32
|
-
if (
|
|
33
|
+
if (getRawShareAnalytics() === false) {
|
|
33
34
|
return 0;
|
|
34
35
|
}
|
|
35
36
|
if (counts.length === 0) {
|
|
@@ -274,6 +274,31 @@ export function scanText(text: string): SecretMatch[] {
|
|
|
274
274
|
* Returns the modified text.
|
|
275
275
|
*/
|
|
276
276
|
export function redactSecrets(text: string): string {
|
|
277
|
+
return redactSecretsWith(
|
|
278
|
+
text,
|
|
279
|
+
(match) => `<redacted type="${match.type}" />`,
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Core redaction walk with a caller-supplied marker builder.
|
|
285
|
+
*
|
|
286
|
+
* `markerFor` receives the {@link SecretMatch} plus the raw matched value so
|
|
287
|
+
* a caller can vary the marker per match (e.g. the chat-persist path emits a
|
|
288
|
+
* revealable sentinel when the value byte-matches a known vault credential).
|
|
289
|
+
* The default `<redacted type="…" />` marker produced via
|
|
290
|
+
* {@link redactSecrets} is consumed by ingress blocking, tool-output
|
|
291
|
+
* scanning, and log redaction — its shape must not change (see
|
|
292
|
+
* `security/AGENTS.md`), which is why this variant exists as a separate
|
|
293
|
+
* additive entry point instead of an option on `redactSecrets`.
|
|
294
|
+
*
|
|
295
|
+
* This module stays pure: `markerFor` is a plain callback, and no credential
|
|
296
|
+
* store types leak in here.
|
|
297
|
+
*/
|
|
298
|
+
export function redactSecretsWith(
|
|
299
|
+
text: string,
|
|
300
|
+
markerFor: (match: SecretMatch, rawValue: string) => string,
|
|
301
|
+
): string {
|
|
277
302
|
const matches = scanText(text);
|
|
278
303
|
if (matches.length === 0) return text;
|
|
279
304
|
|
|
@@ -289,7 +314,7 @@ export function redactSecrets(text: string): string {
|
|
|
289
314
|
continue;
|
|
290
315
|
}
|
|
291
316
|
result += text.slice(lastIndex, match.startIndex);
|
|
292
|
-
result +=
|
|
317
|
+
result += markerFor(match, text.slice(match.startIndex, match.endIndex));
|
|
293
318
|
lastIndex = match.endIndex;
|
|
294
319
|
}
|
|
295
320
|
result += text.slice(lastIndex);
|
|
@@ -9,7 +9,6 @@ import { join } from "node:path";
|
|
|
9
9
|
import {
|
|
10
10
|
getApp,
|
|
11
11
|
getAppDirPath,
|
|
12
|
-
isMultifileApp,
|
|
13
12
|
resolveEffectiveAppHtml,
|
|
14
13
|
} from "../apps/app-store.js";
|
|
15
14
|
import {
|
|
@@ -30,23 +29,19 @@ export async function updatePublishedAppDeployment(
|
|
|
30
29
|
const publishedPage = getActivePublishedPageByAppId(appId);
|
|
31
30
|
if (!publishedPage) return;
|
|
32
31
|
|
|
33
|
-
// 2. Load the app and resolve its deployable HTML
|
|
34
|
-
//
|
|
35
|
-
//
|
|
36
|
-
// would deploy a blank page.
|
|
32
|
+
// 2. Load the app and resolve its deployable HTML: the real content
|
|
33
|
+
// lives in dist/index.html (compiled from src/), inlined into a
|
|
34
|
+
// self-contained page.
|
|
37
35
|
const app = getApp(appId);
|
|
38
36
|
if (!app) {
|
|
39
37
|
log.warn({ appId }, "Published app not found");
|
|
40
38
|
return;
|
|
41
39
|
}
|
|
42
40
|
|
|
43
|
-
// Skip rather than deploy the compile-failure fallback when
|
|
44
|
-
//
|
|
41
|
+
// Skip rather than deploy the compile-failure fallback when the app has
|
|
42
|
+
// no compiled output (e.g. a concurrent compile failed); a later
|
|
45
43
|
// successful compile re-triggers this path.
|
|
46
|
-
if (
|
|
47
|
-
isMultifileApp(app) &&
|
|
48
|
-
!existsSync(join(getAppDirPath(app.id), "dist", "index.html"))
|
|
49
|
-
) {
|
|
44
|
+
if (!existsSync(join(getAppDirPath(app.id), "dist", "index.html"))) {
|
|
50
45
|
log.warn({ appId }, "Skipping auto-redeploy: compiled output missing");
|
|
51
46
|
return;
|
|
52
47
|
}
|
|
@@ -34,10 +34,12 @@ import {
|
|
|
34
34
|
listProviderIds,
|
|
35
35
|
supportsBoundary,
|
|
36
36
|
} from "../providers/speech-to-text/provider-catalog.js";
|
|
37
|
+
import { describeSttFailure } from "../providers/voice-error-copy.js";
|
|
37
38
|
import { getLogger } from "../util/logger.js";
|
|
39
|
+
import { normalizeSttError } from "./daemon-batch-transcriber.js";
|
|
38
40
|
import {
|
|
39
41
|
type StreamingTranscriber,
|
|
40
|
-
|
|
42
|
+
type SttProviderId,
|
|
41
43
|
type SttStreamServerEvent,
|
|
42
44
|
} from "./types.js";
|
|
43
45
|
|
|
@@ -228,17 +230,17 @@ export class SttStreamSession {
|
|
|
228
230
|
"STT stream session started",
|
|
229
231
|
);
|
|
230
232
|
} catch (err) {
|
|
231
|
-
|
|
233
|
+
// normalizeSttError preserves an adapter-thrown SttError's category
|
|
234
|
+
// (e.g. a relay auth rejection at dial time) and classifies raw errors.
|
|
235
|
+
const sttErr = normalizeSttError(err);
|
|
232
236
|
log.error(
|
|
233
|
-
{ provider: this.provider, error: message },
|
|
237
|
+
{ provider: this.provider, error: sttErr.message },
|
|
234
238
|
"Failed to start STT stream session",
|
|
235
239
|
);
|
|
236
240
|
this.sendEvent({
|
|
237
241
|
type: "error",
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
category: err instanceof SttError ? err.category : "provider-error",
|
|
241
|
-
message: `Failed to start streaming session: ${message}`,
|
|
242
|
+
category: sttErr.category,
|
|
243
|
+
message: describeSttFailure(sttErr, this.provider as SttProviderId),
|
|
242
244
|
});
|
|
243
245
|
this.sendEvent({ type: "closed" });
|
|
244
246
|
this.state = "closed";
|
|
@@ -254,7 +256,9 @@ export class SttStreamSession {
|
|
|
254
256
|
* {@link handleBinaryAudio}.
|
|
255
257
|
*/
|
|
256
258
|
handleMessage(raw: string): void {
|
|
257
|
-
if (this.state === "closed") {
|
|
259
|
+
if (this.state === "closed") {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
258
262
|
|
|
259
263
|
this.resetIdleTimer();
|
|
260
264
|
|
|
@@ -267,7 +271,9 @@ export class SttStreamSession {
|
|
|
267
271
|
return;
|
|
268
272
|
}
|
|
269
273
|
|
|
270
|
-
if (!parsed || typeof parsed !== "object") {
|
|
274
|
+
if (!parsed || typeof parsed !== "object") {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
271
277
|
|
|
272
278
|
const event = parsed as {
|
|
273
279
|
type?: string;
|
|
@@ -312,7 +318,9 @@ export class SttStreamSession {
|
|
|
312
318
|
* frames rather than base64-encoded JSON.
|
|
313
319
|
*/
|
|
314
320
|
handleBinaryAudio(data: Buffer | ArrayBuffer | Uint8Array): void {
|
|
315
|
-
if (this.state !== "active") {
|
|
321
|
+
if (this.state !== "active") {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
316
324
|
|
|
317
325
|
this.resetIdleTimer();
|
|
318
326
|
|
|
@@ -328,7 +336,9 @@ export class SttStreamSession {
|
|
|
328
336
|
* Tears down the provider session and cleans up resources.
|
|
329
337
|
*/
|
|
330
338
|
handleClose(code: number, reason?: string): void {
|
|
331
|
-
if (this.state === "closed") {
|
|
339
|
+
if (this.state === "closed") {
|
|
340
|
+
return;
|
|
341
|
+
}
|
|
332
342
|
|
|
333
343
|
log.info(
|
|
334
344
|
{ provider: this.provider, code, reason },
|
|
@@ -343,7 +353,9 @@ export class SttStreamSession {
|
|
|
343
353
|
* ensure deterministic cleanup of all active sessions.
|
|
344
354
|
*/
|
|
345
355
|
destroy(): void {
|
|
346
|
-
if (this.state === "closed") {
|
|
356
|
+
if (this.state === "closed") {
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
347
359
|
|
|
348
360
|
log.info({ provider: this.provider }, "STT stream session destroyed");
|
|
349
361
|
this.teardown();
|
|
@@ -395,7 +407,9 @@ export class SttStreamSession {
|
|
|
395
407
|
* Handle events emitted by the streaming transcriber.
|
|
396
408
|
*/
|
|
397
409
|
private handleTranscriberEvent(event: SttStreamServerEvent): void {
|
|
398
|
-
if (this.state === "closed") {
|
|
410
|
+
if (this.state === "closed") {
|
|
411
|
+
return;
|
|
412
|
+
}
|
|
399
413
|
|
|
400
414
|
this.sendEvent(event);
|
|
401
415
|
|
|
@@ -436,10 +450,14 @@ export class SttStreamSession {
|
|
|
436
450
|
private resetIdleTimer(): void {
|
|
437
451
|
this.clearIdleTimer();
|
|
438
452
|
|
|
439
|
-
if (this.state === "closed" || this.state === "stopping") {
|
|
453
|
+
if (this.state === "closed" || this.state === "stopping") {
|
|
454
|
+
return;
|
|
455
|
+
}
|
|
440
456
|
|
|
441
457
|
this.idleTimer = setTimeout(() => {
|
|
442
|
-
if (this.state === "closed") {
|
|
458
|
+
if (this.state === "closed") {
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
443
461
|
|
|
444
462
|
log.warn({ provider: this.provider }, "STT stream session idle timeout");
|
|
445
463
|
this.sendEvent({
|
|
@@ -469,7 +487,9 @@ export class SttStreamSession {
|
|
|
469
487
|
* Idempotent — safe to call multiple times.
|
|
470
488
|
*/
|
|
471
489
|
private teardown(): void {
|
|
472
|
-
if (this.state === "closed") {
|
|
490
|
+
if (this.state === "closed") {
|
|
491
|
+
return;
|
|
492
|
+
}
|
|
473
493
|
this.state = "closed";
|
|
474
494
|
|
|
475
495
|
this.clearIdleTimer();
|
package/src/subagent/manager.ts
CHANGED
|
@@ -107,7 +107,7 @@ function extractDeltaText(msg: ServerMessage): string | null {
|
|
|
107
107
|
|
|
108
108
|
// ── Default subagent system prompt ──────────────────────────────────────
|
|
109
109
|
|
|
110
|
-
function buildSubagentSystemPrompt(
|
|
110
|
+
export function buildSubagentSystemPrompt(
|
|
111
111
|
config: SubagentConfig,
|
|
112
112
|
role: SubagentRole,
|
|
113
113
|
): string {
|
|
@@ -126,7 +126,8 @@ function buildSubagentSystemPrompt(
|
|
|
126
126
|
"## Constraints",
|
|
127
127
|
`- Role: ${role}`,
|
|
128
128
|
"- You cannot spawn nested subagents.",
|
|
129
|
-
"- Use notify_parent to report important findings or if you are blocked.",
|
|
129
|
+
"- Use notify_parent to report important findings, or if you are blocked.",
|
|
130
|
+
'- If the objective needs a capability your role\'s tools do not provide (for example, writing or editing a file, or running a command, with a read-only role), do NOT fabricate a completed result — call notify_parent with urgency "blocked", name the capability you lack (e.g. file_write), and stop.',
|
|
130
131
|
);
|
|
131
132
|
return sections.join("\n");
|
|
132
133
|
}
|
|
@@ -152,6 +153,8 @@ export function buildSubagentTerminalMessage(opts: {
|
|
|
152
153
|
error?: string;
|
|
153
154
|
/** A follow-up turn is still queued, so the current synthesis is a snapshot. */
|
|
154
155
|
deferred?: boolean;
|
|
156
|
+
/** Tools the subagent attempted but that its role allowlist denied. */
|
|
157
|
+
deniedTools?: string[];
|
|
155
158
|
}): string {
|
|
156
159
|
const {
|
|
157
160
|
label,
|
|
@@ -162,9 +165,19 @@ export function buildSubagentTerminalMessage(opts: {
|
|
|
162
165
|
finalText,
|
|
163
166
|
error,
|
|
164
167
|
deferred,
|
|
168
|
+
deniedTools,
|
|
165
169
|
} = opts;
|
|
166
170
|
const prefix = isFork ? "Fork" : "Subagent";
|
|
167
171
|
|
|
172
|
+
// When the subagent reached for tools its role does not permit, tell the
|
|
173
|
+
// parent so it re-spawns with a capable role instead of blindly retrying (a
|
|
174
|
+
// read-only role produces nothing). Only attached to completed outcomes.
|
|
175
|
+
const many = (deniedTools?.length ?? 0) > 1;
|
|
176
|
+
const deniedNote =
|
|
177
|
+
deniedTools && deniedTools.length > 0
|
|
178
|
+
? `\n\nNote: this ${prefix.toLowerCase()} attempted ${deniedTools.join(", ")} but its role does not permit ${many ? "them" : "it"}. If the objective requires ${many ? "those" : "that"}, re-spawn with a role that includes ${many ? "them" : "it"} (e.g. \`coder\`).`
|
|
179
|
+
: "";
|
|
180
|
+
|
|
168
181
|
if (outcome === "failed") {
|
|
169
182
|
return (
|
|
170
183
|
`[${prefix} "${label}" failed]\n\n` +
|
|
@@ -180,7 +193,8 @@ export function buildSubagentTerminalMessage(opts: {
|
|
|
180
193
|
`${trimmed}\n\n` +
|
|
181
194
|
(silent
|
|
182
195
|
? `(Use these findings internally; do not relay the raw ${prefix.toLowerCase()} output to the user.)`
|
|
183
|
-
: `(Incorporate this into your reply to the user as appropriate.)`)
|
|
196
|
+
: `(Incorporate this into your reply to the user as appropriate.)`) +
|
|
197
|
+
deniedNote
|
|
184
198
|
);
|
|
185
199
|
}
|
|
186
200
|
|
|
@@ -194,7 +208,8 @@ export function buildSubagentTerminalMessage(opts: {
|
|
|
194
208
|
return (
|
|
195
209
|
`[${prefix} "${label}" completed]\n\n` +
|
|
196
210
|
`${reason}. Use subagent_read with subagent_id "${subagentId}"${lastN} for the latest output.` +
|
|
197
|
-
(silent ? ` Keep the result internal.` : ``)
|
|
211
|
+
(silent ? ` Keep the result internal.` : ``) +
|
|
212
|
+
deniedNote
|
|
198
213
|
);
|
|
199
214
|
}
|
|
200
215
|
|
|
@@ -367,8 +382,9 @@ export class SubagentManager {
|
|
|
367
382
|
// ── Build conversation dependencies ─────────────────────────────
|
|
368
383
|
const appConfig = getConfig();
|
|
369
384
|
// Connection-aware default-provider resolution. Throws
|
|
370
|
-
// `ConnectionResolutionError` if
|
|
371
|
-
//
|
|
385
|
+
// `ConnectionResolutionError` if the resolved default config carries no
|
|
386
|
+
// provider_connection or the connection row is missing/mismatched
|
|
387
|
+
// (config bugs).
|
|
372
388
|
// Returns null on soft credential failures (missing credential,
|
|
373
389
|
// platform auth unavailable).
|
|
374
390
|
const baseProvider = await resolveDefaultProvider(appConfig);
|
|
@@ -376,7 +392,7 @@ export class SubagentManager {
|
|
|
376
392
|
throw await mainAgentResolutionError(appConfig.llm, listProviders());
|
|
377
393
|
}
|
|
378
394
|
// Per-call `options.config.callSite` (e.g. `subagentSpawn`) can resolve
|
|
379
|
-
// to a profile that differs from
|
|
395
|
+
// to a profile that differs from the default's. The shared wrapper
|
|
380
396
|
// threads `appConfig` through so per-call alternate-profile routing is
|
|
381
397
|
// also connection-aware (matches the canonical dispatch path).
|
|
382
398
|
let provider = wrapWithCallSiteRouting(baseProvider, appConfig);
|
|
@@ -771,6 +787,9 @@ export class SubagentManager {
|
|
|
771
787
|
// Capture the trailing assistant text before any release nulls the
|
|
772
788
|
// conversation reference. The fire-and-forget caller ignores the return.
|
|
773
789
|
finalText = extractFinalAssistantText(conversation.messages);
|
|
790
|
+
// Capture any tools the subagent reached for but its role denied, before a
|
|
791
|
+
// release nulls the conversation reference, so we can tell the parent.
|
|
792
|
+
const deniedTools = [...conversation.subagentDeniedToolNames];
|
|
774
793
|
// Copy usage stats from the conversation before sending status (which includes usage).
|
|
775
794
|
managed.state.usage = { ...conversation.usageStats };
|
|
776
795
|
// Only update state + notify if still non-terminal (guards against abort race).
|
|
@@ -785,7 +804,12 @@ export class SubagentManager {
|
|
|
785
804
|
// round-trip. Skipped on the synchronous path — the awaiting caller
|
|
786
805
|
// receives the final text directly.
|
|
787
806
|
if (!managed.synchronous) {
|
|
788
|
-
this.notifyParentTerminal(
|
|
807
|
+
this.notifyParentTerminal(
|
|
808
|
+
managed,
|
|
809
|
+
"completed",
|
|
810
|
+
finalText,
|
|
811
|
+
deniedTools,
|
|
812
|
+
);
|
|
789
813
|
}
|
|
790
814
|
}
|
|
791
815
|
} catch (err) {
|
|
@@ -1396,6 +1420,7 @@ export class SubagentManager {
|
|
|
1396
1420
|
managed: ManagedSubagent,
|
|
1397
1421
|
outcome: "completed" | "failed",
|
|
1398
1422
|
finalText?: string,
|
|
1423
|
+
deniedTools?: string[],
|
|
1399
1424
|
): void {
|
|
1400
1425
|
const { config } = managed.state;
|
|
1401
1426
|
const isFork = managed.state.isFork;
|
|
@@ -1416,6 +1441,7 @@ export class SubagentManager {
|
|
|
1416
1441
|
// A queued follow-up turn means the snapshot we hold is stale; defer to a
|
|
1417
1442
|
// read pointer so the parent picks up the queued turn's output instead.
|
|
1418
1443
|
deferred: managed.hadEnqueuedMessages === true,
|
|
1444
|
+
deniedTools,
|
|
1419
1445
|
});
|
|
1420
1446
|
|
|
1421
1447
|
const notification: SubagentNotificationInfo = {
|
package/src/telemetry/AGENTS.md
CHANGED
|
@@ -12,4 +12,33 @@ Pre-flush validation (`telemetry-wire-validation.ts`) checks outgoing events aga
|
|
|
12
12
|
|
|
13
13
|
The ingest endpoint silently skips events whose type has no registered serializer (the batch still 2xxes and the daemon acks away its outbox rows), so an emitter shipped before its platform serializer loses every event it records — pre-flush validation logs the drop but does not prevent it.
|
|
14
14
|
|
|
15
|
-
The full cross-repo checklist lives with the serializer registry it governs, in `vellum-assistant-platform` at `django/app/assistant/self_hosted_local/AGENTS.md` ("Adding a new telemetry ingest event type"). The daemon emitter is the **last** step — added only after the platform serializer has merged and the wire sync PR has landed here.
|
|
15
|
+
The full cross-repo checklist lives with the serializer registry it governs, in `vellum-assistant-platform` at `django/app/assistant/self_hosted_local/AGENTS.md` ("Adding a new telemetry ingest event type"). The daemon emitter is the **last** step — added only after the platform serializer has merged and the wire sync PR has landed here.
|
|
16
|
+
|
|
17
|
+
### Emitting an outbox-backed event
|
|
18
|
+
|
|
19
|
+
For a normal (outbox-backed) event, the sync PR is all the daemon needs — **no manual edits in this directory**. `OUTBOX_TELEMETRY_EVENT_NAMES` (in `types.ts`) and its flush source (in `telemetry-event-sources.ts`) are both **derived from the wire contract**, so a newly-synced type is outbox-backed and flushed automatically. Just call the generic, fully-typed recorder from your feature code:
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { recordTelemetryEvent } from "./telemetry-events-outbox.js";
|
|
23
|
+
|
|
24
|
+
recordTelemetryEvent(
|
|
25
|
+
"my_new_event",
|
|
26
|
+
{ some_field: value, conversation_id: conversationId ?? null },
|
|
27
|
+
{ conversationId },
|
|
28
|
+
);
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`recordTelemetryEvent` stamps the base fields (`type`, `daemon_event_id`, `recorded_at`, `assistant_version`) and gates on consent — dropping only on a confirmed `share_analytics` opt-out; an unknown consent state (cold cache) records, because consent is enforced again at flush time and a record-time drop would destroy the event permanently. Unknown-state buffering is bounded: the reporter prunes outbox rows older than `OUTBOX_MAX_ROW_AGE_MS` (30 days) at the start of every flush cycle, in every consent state, so a permanently-unknown state cannot grow the outbox forever. The `fields` argument is typed as everything except the base fields.
|
|
32
|
+
|
|
33
|
+
Manual edits in this directory are needed **only** to override a default:
|
|
34
|
+
|
|
35
|
+
- **Watermark-flushed** (the type has its own high-volume table, not the outbox): add it to `WATERMARK_TELEMETRY_EVENT_NAMES` in `types.ts` and add its source to `WATERMARK_TELEMETRY_EVENT_SOURCES`.
|
|
36
|
+
- **Custom flush behavior** (a non-default per-type source — e.g. re-checking consent at flush time): map the type to a custom source factory in `OUTBOX_SOURCE_FACTORY` in `telemetry-event-sources.ts`. (No type currently needs one — every outbox event, `onboarding_research` included, flushes through the default `outboxSource` under `share_analytics`.)
|
|
37
|
+
- **Daemon-partition flush** (needs live in-process state, like `turn`): add its source to `DAEMON_TELEMETRY_EVENT_SOURCES`.
|
|
38
|
+
- **Richer daemon type** than the wire: add an `Overrides` entry in `types.ts` with the drift guards (see the wire-contract section above).
|
|
39
|
+
|
|
40
|
+
If the type previously existed daemon-only, also move it out of `Extensions` in `types.ts`.
|
|
41
|
+
|
|
42
|
+
**For a new event type, prefer calling `recordTelemetryEvent` directly** rather than adding a `record<Thing>Event` store wrapper. A wrapper that only renames camelCase fields to the snake-case wire shape is gratuitous indirection now that `recordTelemetryEvent`'s `fields` argument is fully typed from the wire. Add a store module only when it earns its keep: clamp values to server bounds (`config-setting-events-store.ts`), stamp a custom deterministic `daemon_event_id` via `recordTelemetryOutboxEvent` (`onboarding-research-events-store.ts`), aggregate multiple events (`auth-fallback-events-store.ts`), centralize a non-trivial mapping reused across many call sites (`watchdog-events-store.ts`), or provide a stable, mockable seam for tests (`skill-loaded-events-store.ts`).
|
|
43
|
+
|
|
44
|
+
The generic outbox also self-heals: rows for a type the platform later removes or renames are drained by the synthetic orphan-drain source (`orphanOutboxDrainSource` in `telemetry-event-sources.ts`), so a retired type needs no cleanup step here.
|