@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
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
9
9
|
|
|
10
|
+
import type { ConsentState } from "../platform/consent-cache.js";
|
|
11
|
+
|
|
10
12
|
// ---------------------------------------------------------------------------
|
|
11
13
|
// Module mocks (must precede production imports)
|
|
12
14
|
// ---------------------------------------------------------------------------
|
|
@@ -91,7 +93,10 @@ mock.module("../version.js", () => ({
|
|
|
91
93
|
APP_VERSION: "1.2.3-test",
|
|
92
94
|
}));
|
|
93
95
|
|
|
94
|
-
|
|
96
|
+
// Tri-state `share_analytics` consent (the reporter's flush gate). The
|
|
97
|
+
// boolean accessor used by record-time gates derives from the same knob so a
|
|
98
|
+
// test toggling consent flips both views consistently.
|
|
99
|
+
const mockGetRawShareAnalytics = mock<() => ConsentState>(() => true);
|
|
95
100
|
// Owner's `share_diagnostics` consent — one part of the trace-collection gate.
|
|
96
101
|
const mockGetCachedShareDiagnostics = mock(() => false);
|
|
97
102
|
// Owner's accepted diagnostics-consent version — the disclosing-version part of
|
|
@@ -101,7 +106,7 @@ const mockGetCachedShareDiagnostics = mock(() => false);
|
|
|
101
106
|
const mockGetCachedShareDiagnosticsVersion = mock(() => "2999-01-01");
|
|
102
107
|
|
|
103
108
|
mock.module("../platform/consent-cache.js", () => ({
|
|
104
|
-
|
|
109
|
+
getRawShareAnalytics: mockGetRawShareAnalytics,
|
|
105
110
|
getCachedShareDiagnostics: mockGetCachedShareDiagnostics,
|
|
106
111
|
getCachedShareDiagnosticsVersion: mockGetCachedShareDiagnosticsVersion,
|
|
107
112
|
}));
|
|
@@ -215,6 +220,7 @@ import {
|
|
|
215
220
|
} from "./telemetry-event-sources.js";
|
|
216
221
|
import {
|
|
217
222
|
insertTelemetryOutboxEvent,
|
|
223
|
+
OUTBOX_MAX_ROW_AGE_MS,
|
|
218
224
|
queryTelemetryOutboxBatch,
|
|
219
225
|
} from "./telemetry-events-outbox.js";
|
|
220
226
|
import type { OutboxTelemetryEventName, TelemetryEvent } from "./types.js";
|
|
@@ -324,8 +330,8 @@ let mockFetch: ReturnType<typeof mock>;
|
|
|
324
330
|
beforeEach(() => {
|
|
325
331
|
eventIdCounter = 0;
|
|
326
332
|
// Default consent ON so the happy-path send tests exercise the flush.
|
|
327
|
-
|
|
328
|
-
|
|
333
|
+
mockGetRawShareAnalytics.mockReset();
|
|
334
|
+
mockGetRawShareAnalytics.mockReturnValue(true);
|
|
329
335
|
// Default `share_diagnostics` consent OFF — most tests don't expect a trace;
|
|
330
336
|
// the trace-specific tests opt in explicitly.
|
|
331
337
|
mockGetCachedShareDiagnostics.mockReset();
|
|
@@ -564,6 +570,126 @@ describe("UsageTelemetryReporter", () => {
|
|
|
564
570
|
expect(mockFetch).toHaveBeenCalledTimes(10);
|
|
565
571
|
});
|
|
566
572
|
|
|
573
|
+
test("flush summary reports sent/persisted/dropped from the ingest response", async () => {
|
|
574
|
+
mockQueryUnreportedUsageEvents.mockReturnValue([
|
|
575
|
+
makeUsageEvent(),
|
|
576
|
+
makeUsageEvent(),
|
|
577
|
+
makeUsageEvent(),
|
|
578
|
+
]);
|
|
579
|
+
mockFetch.mockImplementation(() =>
|
|
580
|
+
Promise.resolve(
|
|
581
|
+
new Response(
|
|
582
|
+
'{"accepted":3,"persisted":2,"dropped":{"analytics_opt_out":1}}',
|
|
583
|
+
{ status: 200 },
|
|
584
|
+
),
|
|
585
|
+
),
|
|
586
|
+
);
|
|
587
|
+
|
|
588
|
+
const reporter = makeReporter();
|
|
589
|
+
const summary = await reporter.flush();
|
|
590
|
+
|
|
591
|
+
expect(summary).toEqual({
|
|
592
|
+
flushed: true,
|
|
593
|
+
sent: 3,
|
|
594
|
+
persisted: 2,
|
|
595
|
+
dropped: 1,
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
test("flush summary falls back to persisted=sent when the body omits persisted", async () => {
|
|
600
|
+
// Older platform returns only `accepted` — assume everything landed.
|
|
601
|
+
mockQueryUnreportedUsageEvents.mockReturnValue([
|
|
602
|
+
makeUsageEvent(),
|
|
603
|
+
makeUsageEvent(),
|
|
604
|
+
]);
|
|
605
|
+
mockFetch.mockImplementation(() =>
|
|
606
|
+
Promise.resolve(new Response('{"accepted":2}', { status: 200 })),
|
|
607
|
+
);
|
|
608
|
+
|
|
609
|
+
const reporter = makeReporter();
|
|
610
|
+
const summary = await reporter.flush();
|
|
611
|
+
|
|
612
|
+
expect(summary).toEqual({
|
|
613
|
+
flushed: true,
|
|
614
|
+
sent: 2,
|
|
615
|
+
persisted: 2,
|
|
616
|
+
dropped: 0,
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
test("flush summary accumulates counts across the batch recursion", async () => {
|
|
621
|
+
const fullBatch = Array.from({ length: 500 }, (_, i) =>
|
|
622
|
+
makeUsageEvent({ id: `evt-acc-${i}`, createdAt: 1700000000000 + i }),
|
|
623
|
+
);
|
|
624
|
+
// First collect fills a batch (recurse); second returns a short batch (stop).
|
|
625
|
+
mockQueryUnreportedUsageEvents
|
|
626
|
+
.mockReturnValueOnce(fullBatch)
|
|
627
|
+
.mockReturnValue([makeUsageEvent({ id: "evt-acc-tail" })]);
|
|
628
|
+
// First POST drops 10; second persists its single event.
|
|
629
|
+
mockFetch
|
|
630
|
+
.mockImplementationOnce(() =>
|
|
631
|
+
Promise.resolve(
|
|
632
|
+
new Response('{"accepted":500,"persisted":490}', { status: 200 }),
|
|
633
|
+
),
|
|
634
|
+
)
|
|
635
|
+
.mockImplementation(() =>
|
|
636
|
+
Promise.resolve(new Response('{"persisted":1}', { status: 200 })),
|
|
637
|
+
);
|
|
638
|
+
|
|
639
|
+
const reporter = makeReporter();
|
|
640
|
+
const summary = await reporter.flush();
|
|
641
|
+
|
|
642
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
643
|
+
expect(summary).toEqual({
|
|
644
|
+
flushed: true,
|
|
645
|
+
sent: 501,
|
|
646
|
+
persisted: 491,
|
|
647
|
+
dropped: 10,
|
|
648
|
+
});
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
test("flush summary reports skip reasons without shipping", async () => {
|
|
652
|
+
const reporter = makeReporter();
|
|
653
|
+
|
|
654
|
+
// nothing-pending: no events across any source.
|
|
655
|
+
mockQueryUnreportedUsageEvents.mockReturnValue([]);
|
|
656
|
+
expect(await reporter.flush()).toEqual({
|
|
657
|
+
flushed: false,
|
|
658
|
+
reason: "nothing-pending",
|
|
659
|
+
});
|
|
660
|
+
|
|
661
|
+
// opted-out: confirmed share_analytics opt-out.
|
|
662
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
663
|
+
mockQueryUnreportedUsageEvents.mockReturnValue([makeUsageEvent()]);
|
|
664
|
+
expect(await reporter.flush()).toEqual({
|
|
665
|
+
flushed: false,
|
|
666
|
+
reason: "opted-out",
|
|
667
|
+
});
|
|
668
|
+
mockGetRawShareAnalytics.mockReturnValue(true);
|
|
669
|
+
|
|
670
|
+
// no-credentials: authenticated-only, no platform client resolved.
|
|
671
|
+
mockPlatformClient = null;
|
|
672
|
+
expect(await reporter.flush()).toEqual({
|
|
673
|
+
flushed: false,
|
|
674
|
+
reason: "no-credentials",
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
678
|
+
});
|
|
679
|
+
|
|
680
|
+
test("flush summary reports post-failed on a non-2xx response", async () => {
|
|
681
|
+
mockQueryUnreportedUsageEvents.mockReturnValue([makeUsageEvent()]);
|
|
682
|
+
mockFetch.mockImplementation(() =>
|
|
683
|
+
Promise.resolve(new Response("error", { status: 500 })),
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
const reporter = makeReporter();
|
|
687
|
+
expect(await reporter.flush()).toEqual({
|
|
688
|
+
flushed: false,
|
|
689
|
+
reason: "post-failed",
|
|
690
|
+
});
|
|
691
|
+
});
|
|
692
|
+
|
|
567
693
|
test("stop() performs final flush", async () => {
|
|
568
694
|
const events = [makeUsageEvent()];
|
|
569
695
|
mockQueryUnreportedUsageEvents.mockReturnValue(events);
|
|
@@ -1164,7 +1290,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
1164
1290
|
// The analytics gate short-circuits the entire flush; trace assembly must
|
|
1165
1291
|
// never run (and nothing is sent) even when the trace gate is fully on
|
|
1166
1292
|
// (share_diagnostics true at an eligible version).
|
|
1167
|
-
|
|
1293
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
1168
1294
|
mockGetCachedShareDiagnostics.mockReturnValue(true);
|
|
1169
1295
|
mockQueryUnreportedTurnEvents.mockReturnValue(singleTurnEvent());
|
|
1170
1296
|
|
|
@@ -1565,7 +1691,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
1565
1691
|
});
|
|
1566
1692
|
|
|
1567
1693
|
test("flush is skipped and watermarks advanced when share_analytics consent is off", async () => {
|
|
1568
|
-
|
|
1694
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
1569
1695
|
const events = [makeUsageEvent()];
|
|
1570
1696
|
mockQueryUnreportedUsageEvents.mockReturnValue(events);
|
|
1571
1697
|
mockFetch.mockImplementation(() =>
|
|
@@ -1608,6 +1734,37 @@ describe("UsageTelemetryReporter", () => {
|
|
|
1608
1734
|
expect(keys).not.toContain("telemetry:onboarding:last_reported_id");
|
|
1609
1735
|
});
|
|
1610
1736
|
|
|
1737
|
+
test("warm-up: unknown-consent flushes defer the backlog until consent resolves to true", async () => {
|
|
1738
|
+
// An unresolved consent cache (boot before the first refresh, no platform
|
|
1739
|
+
// session) is not an opt-out: unlike the confirmed-false branch, nothing
|
|
1740
|
+
// is sent, nothing is purged, and no watermark moves, so the backlog
|
|
1741
|
+
// survives the cold-cache window.
|
|
1742
|
+
mockGetRawShareAnalytics.mockReturnValue("unknown");
|
|
1743
|
+
const events = [makeUsageEvent({ id: "evt-warmup-backlog" })];
|
|
1744
|
+
mockQueryUnreportedUsageEvents.mockReturnValue(events);
|
|
1745
|
+
|
|
1746
|
+
const reporter = makeReporter();
|
|
1747
|
+
// Construction initializes the absent tool_executed watermark; clear that
|
|
1748
|
+
// call so the assertions below cover only the flushes.
|
|
1749
|
+
mockSetFlushCheckpoint.mockClear();
|
|
1750
|
+
await reporter.flush();
|
|
1751
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
1752
|
+
expect(mockSetFlushCheckpoint).not.toHaveBeenCalled();
|
|
1753
|
+
|
|
1754
|
+
// The refresh loop resolves the cache to a confirmed opt-in: the same
|
|
1755
|
+
// backlog now ships.
|
|
1756
|
+
mockGetRawShareAnalytics.mockReturnValue(true);
|
|
1757
|
+
await reporter.flush();
|
|
1758
|
+
|
|
1759
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
1760
|
+
const body = JSON.parse(
|
|
1761
|
+
(mockFetch.mock.calls[0] as [string, RequestInit])[1].body as string,
|
|
1762
|
+
);
|
|
1763
|
+
expect(
|
|
1764
|
+
body.events.map((e: { daemon_event_id: string }) => e.daemon_event_id),
|
|
1765
|
+
).toEqual(["evt-warmup-backlog"]);
|
|
1766
|
+
});
|
|
1767
|
+
|
|
1611
1768
|
test("platform disabled takes precedence over consent off — watermarks NOT advanced", async () => {
|
|
1612
1769
|
// VELLUM_DISABLE_PLATFORM keeps the consent cache false (the consent
|
|
1613
1770
|
// refresh can't create a platform client), so both gates would fire. The
|
|
@@ -1615,7 +1772,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
1615
1772
|
// watermarks, preserving the backlog until the flag is cleared — a
|
|
1616
1773
|
// deployment toggle must not be treated as a privacy opt-out.
|
|
1617
1774
|
process.env.VELLUM_DISABLE_PLATFORM = "true";
|
|
1618
|
-
|
|
1775
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
1619
1776
|
const events = [makeUsageEvent()];
|
|
1620
1777
|
mockQueryUnreportedUsageEvents.mockReturnValue(events);
|
|
1621
1778
|
|
|
@@ -1652,14 +1809,14 @@ describe("UsageTelemetryReporter", () => {
|
|
|
1652
1809
|
|
|
1653
1810
|
test("events sent normally after re-granting share_analytics consent", async () => {
|
|
1654
1811
|
// First flush with opt-out — watermarks advance, nothing sent
|
|
1655
|
-
|
|
1812
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
1656
1813
|
const reporter = makeReporter();
|
|
1657
1814
|
await reporter.flush();
|
|
1658
1815
|
expect(mockFetch).not.toHaveBeenCalled();
|
|
1659
1816
|
mockSetFlushCheckpoint.mockReset();
|
|
1660
1817
|
|
|
1661
1818
|
// Re-grant consent and flush with new events
|
|
1662
|
-
|
|
1819
|
+
mockGetRawShareAnalytics.mockReturnValue(true);
|
|
1663
1820
|
const events = [makeUsageEvent({ id: "evt-after-reenable" })];
|
|
1664
1821
|
mockQueryUnreportedUsageEvents.mockReturnValue(events);
|
|
1665
1822
|
mockFetch.mockImplementation(() =>
|
|
@@ -1969,7 +2126,8 @@ describe("UsageTelemetryReporter", () => {
|
|
|
1969
2126
|
insertTelemetryOutboxEvent({
|
|
1970
2127
|
id: "row-activation-old",
|
|
1971
2128
|
name: "onboarding",
|
|
1972
|
-
|
|
2129
|
+
// Row age drives the flush-cycle prune; only the payload is frozen-old.
|
|
2130
|
+
createdAt: Date.now(),
|
|
1973
2131
|
event: frozen,
|
|
1974
2132
|
});
|
|
1975
2133
|
mockFetch.mockImplementation(() =>
|
|
@@ -2280,7 +2438,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2280
2438
|
// constructs and runs the reporter; the always-on audit listener keeps
|
|
2281
2439
|
// writing rows. Every opted-out flush (5-minute cycle plus the final
|
|
2282
2440
|
// flush in stop()) advances the watermark past them without sending.
|
|
2283
|
-
|
|
2441
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
2284
2442
|
const optOutRowCreatedAt = Date.now() - 5_000;
|
|
2285
2443
|
seedToolInvocation({
|
|
2286
2444
|
id: "ti-opt-out-window",
|
|
@@ -2296,7 +2454,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2296
2454
|
|
|
2297
2455
|
// Session 2: the user opts back in and restarts. Only rows recorded
|
|
2298
2456
|
// after the opt-out epoch ship — the opt-out-window row never does.
|
|
2299
|
-
|
|
2457
|
+
mockGetRawShareAnalytics.mockReturnValue(true);
|
|
2300
2458
|
seedToolInvocation({ id: "ti-after-opt-in", createdAt: advanced + 1000 });
|
|
2301
2459
|
const reporter = makeReporter();
|
|
2302
2460
|
await reporter.flush();
|
|
@@ -2321,7 +2479,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2321
2479
|
|
|
2322
2480
|
// Opted-out flush: advances the timestamp watermark to Date.now() and
|
|
2323
2481
|
// must also pin the ID watermark to the high-sorting sentinel.
|
|
2324
|
-
|
|
2482
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
2325
2483
|
const optedOutReporter = makeReporter();
|
|
2326
2484
|
await optedOutReporter.flush();
|
|
2327
2485
|
expect(mockFetch).not.toHaveBeenCalled();
|
|
@@ -2339,7 +2497,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2339
2497
|
});
|
|
2340
2498
|
|
|
2341
2499
|
// Re-opt-in: only rows strictly after the opt-out epoch ship.
|
|
2342
|
-
|
|
2500
|
+
mockGetRawShareAnalytics.mockReturnValue(true);
|
|
2343
2501
|
seedToolInvocation({ id: "ti-after-opt-in", createdAt: watermark + 1000 });
|
|
2344
2502
|
const reporter = makeReporter();
|
|
2345
2503
|
await reporter.flush();
|
|
@@ -2779,7 +2937,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2779
2937
|
});
|
|
2780
2938
|
|
|
2781
2939
|
test("opt-out: discardPending drops ack-mode rows while watermark sources get the sentinel pin", async () => {
|
|
2782
|
-
|
|
2940
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
2783
2941
|
const {
|
|
2784
2942
|
source: ackSource,
|
|
2785
2943
|
discardPending,
|
|
@@ -2805,6 +2963,28 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2805
2963
|
expect(keys.some((k) => k.includes("fake_ack"))).toBe(false);
|
|
2806
2964
|
});
|
|
2807
2965
|
|
|
2966
|
+
test("unknown consent: nothing is discarded, acked, or advanced — rows wait for a confirmed state", async () => {
|
|
2967
|
+
mockGetRawShareAnalytics.mockReturnValue("unknown");
|
|
2968
|
+
const {
|
|
2969
|
+
source: ackSource,
|
|
2970
|
+
acknowledge,
|
|
2971
|
+
discardPending,
|
|
2972
|
+
pending,
|
|
2973
|
+
} = makeAckSource("fake_ack", ["row-1"]);
|
|
2974
|
+
const wmSource = makeWatermarkSource("fake_wm", ["wm-1"]);
|
|
2975
|
+
const reporter = new UsageTelemetryReporter(
|
|
2976
|
+
[wmSource, ackSource],
|
|
2977
|
+
fakeFlushCheckpointStore,
|
|
2978
|
+
);
|
|
2979
|
+
await reporter.flush();
|
|
2980
|
+
|
|
2981
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
2982
|
+
expect(discardPending).not.toHaveBeenCalled();
|
|
2983
|
+
expect(acknowledge).not.toHaveBeenCalled();
|
|
2984
|
+
expect(pending()).toBe(1);
|
|
2985
|
+
expect(mockSetFlushCheckpoint).not.toHaveBeenCalled();
|
|
2986
|
+
});
|
|
2987
|
+
|
|
2808
2988
|
test("mixed watermark+ack sources flush in construction order, each acknowledged in its own mode", async () => {
|
|
2809
2989
|
const wmSource = makeWatermarkSource("fake_wm", ["wm-1", "wm-2"]);
|
|
2810
2990
|
const { source: ackSource, acknowledge } = makeAckSource("fake_ack", [
|
|
@@ -2937,6 +3117,10 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2937
3117
|
// shipped event type.
|
|
2938
3118
|
const OUTBOX_NAME = "test_outbox" as OutboxTelemetryEventName;
|
|
2939
3119
|
|
|
3120
|
+
// Recent base for row timestamps: the flush cycle prunes outbox rows older
|
|
3121
|
+
// than OUTBOX_MAX_ROW_AGE_MS, so rows that must ship are seeded fresh.
|
|
3122
|
+
const OUTBOX_BASE = Date.now() - 60_000;
|
|
3123
|
+
|
|
2940
3124
|
function seedOutboxRow(id: string, createdAt: number): void {
|
|
2941
3125
|
insertTelemetryOutboxEvent({
|
|
2942
3126
|
id,
|
|
@@ -2951,8 +3135,8 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2951
3135
|
}
|
|
2952
3136
|
|
|
2953
3137
|
test("outboxSource ships pending rows and deletes them after a 2xx", async () => {
|
|
2954
|
-
seedOutboxRow("ob-1",
|
|
2955
|
-
seedOutboxRow("ob-2",
|
|
3138
|
+
seedOutboxRow("ob-1", OUTBOX_BASE + 1000);
|
|
3139
|
+
seedOutboxRow("ob-2", OUTBOX_BASE + 2000);
|
|
2956
3140
|
const reporter = new UsageTelemetryReporter(
|
|
2957
3141
|
[outboxSource(OUTBOX_NAME)],
|
|
2958
3142
|
fakeFlushCheckpointStore,
|
|
@@ -2975,7 +3159,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2975
3159
|
mockFetch.mockImplementation(() =>
|
|
2976
3160
|
Promise.resolve(new Response("error", { status: 500 })),
|
|
2977
3161
|
);
|
|
2978
|
-
seedOutboxRow("ob-1",
|
|
3162
|
+
seedOutboxRow("ob-1", OUTBOX_BASE + 1000);
|
|
2979
3163
|
const reporter = new UsageTelemetryReporter(
|
|
2980
3164
|
[outboxSource(OUTBOX_NAME)],
|
|
2981
3165
|
fakeFlushCheckpointStore,
|
|
@@ -2987,9 +3171,9 @@ describe("UsageTelemetryReporter", () => {
|
|
|
2987
3171
|
});
|
|
2988
3172
|
|
|
2989
3173
|
test("outboxSource opt-out discards all pending rows for the name", async () => {
|
|
2990
|
-
|
|
2991
|
-
seedOutboxRow("ob-1",
|
|
2992
|
-
seedOutboxRow("ob-2",
|
|
3174
|
+
mockGetRawShareAnalytics.mockReturnValue(false);
|
|
3175
|
+
seedOutboxRow("ob-1", OUTBOX_BASE + 1000);
|
|
3176
|
+
seedOutboxRow("ob-2", OUTBOX_BASE + 2000);
|
|
2993
3177
|
const reporter = new UsageTelemetryReporter(
|
|
2994
3178
|
[outboxSource(OUTBOX_NAME)],
|
|
2995
3179
|
fakeFlushCheckpointStore,
|
|
@@ -3001,6 +3185,91 @@ describe("UsageTelemetryReporter", () => {
|
|
|
3001
3185
|
expect(mockSetFlushCheckpoint).not.toHaveBeenCalled();
|
|
3002
3186
|
});
|
|
3003
3187
|
|
|
3188
|
+
test("outboxSource unknown consent retains all pending rows", async () => {
|
|
3189
|
+
mockGetRawShareAnalytics.mockReturnValue("unknown");
|
|
3190
|
+
seedOutboxRow("ob-1", OUTBOX_BASE + 1000);
|
|
3191
|
+
seedOutboxRow("ob-2", OUTBOX_BASE + 2000);
|
|
3192
|
+
const reporter = new UsageTelemetryReporter(
|
|
3193
|
+
[outboxSource(OUTBOX_NAME)],
|
|
3194
|
+
fakeFlushCheckpointStore,
|
|
3195
|
+
);
|
|
3196
|
+
await reporter.flush();
|
|
3197
|
+
|
|
3198
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
3199
|
+
expect(pendingOutboxIds()).toEqual(["ob-1", "ob-2"]);
|
|
3200
|
+
expect(mockSetFlushCheckpoint).not.toHaveBeenCalled();
|
|
3201
|
+
});
|
|
3202
|
+
|
|
3203
|
+
// -------------------------------------------------------------------------
|
|
3204
|
+
// Flush-cycle age prune (bounds unknown-consent buffering)
|
|
3205
|
+
// -------------------------------------------------------------------------
|
|
3206
|
+
|
|
3207
|
+
function seedExpiredAndFreshRows(): void {
|
|
3208
|
+
seedOutboxRow("ob-expired", Date.now() - OUTBOX_MAX_ROW_AGE_MS - 60_000);
|
|
3209
|
+
seedOutboxRow("ob-fresh", OUTBOX_BASE);
|
|
3210
|
+
}
|
|
3211
|
+
|
|
3212
|
+
test("opted in: expired rows are pruned rather than shipped; fresh rows ship", async () => {
|
|
3213
|
+
seedExpiredAndFreshRows();
|
|
3214
|
+
const reporter = new UsageTelemetryReporter(
|
|
3215
|
+
[outboxSource(OUTBOX_NAME)],
|
|
3216
|
+
fakeFlushCheckpointStore,
|
|
3217
|
+
);
|
|
3218
|
+
await reporter.flush();
|
|
3219
|
+
|
|
3220
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
3221
|
+
const body = JSON.parse(
|
|
3222
|
+
(mockFetch.mock.calls[0] as [string, RequestInit])[1].body as string,
|
|
3223
|
+
);
|
|
3224
|
+
expect(
|
|
3225
|
+
body.events.map((e: { daemon_event_id: string }) => e.daemon_event_id),
|
|
3226
|
+
).toEqual(["wire-ob-fresh"]);
|
|
3227
|
+
expect(pendingOutboxIds()).toEqual([]);
|
|
3228
|
+
});
|
|
3229
|
+
|
|
3230
|
+
test("unknown consent: expired rows are pruned before the deferral; fresh rows are retained", async () => {
|
|
3231
|
+
mockGetRawShareAnalytics.mockReturnValue("unknown");
|
|
3232
|
+
seedExpiredAndFreshRows();
|
|
3233
|
+
const reporter = new UsageTelemetryReporter(
|
|
3234
|
+
[outboxSource(OUTBOX_NAME)],
|
|
3235
|
+
fakeFlushCheckpointStore,
|
|
3236
|
+
);
|
|
3237
|
+
await reporter.flush();
|
|
3238
|
+
|
|
3239
|
+
// The unknown-defer branch still sends and acks nothing, but the age
|
|
3240
|
+
// prune has already bounded the backlog — a permanently-unknown state
|
|
3241
|
+
// cannot accumulate rows forever.
|
|
3242
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
3243
|
+
expect(pendingOutboxIds()).toEqual(["ob-fresh"]);
|
|
3244
|
+
expect(mockSetFlushCheckpoint).not.toHaveBeenCalled();
|
|
3245
|
+
});
|
|
3246
|
+
|
|
3247
|
+
test("expired rows are pruned even when the flush skips for platform-disabled", async () => {
|
|
3248
|
+
process.env.VELLUM_DISABLE_PLATFORM = "true";
|
|
3249
|
+
seedExpiredAndFreshRows();
|
|
3250
|
+
const reporter = new UsageTelemetryReporter(
|
|
3251
|
+
[outboxSource(OUTBOX_NAME)],
|
|
3252
|
+
fakeFlushCheckpointStore,
|
|
3253
|
+
);
|
|
3254
|
+
await reporter.flush();
|
|
3255
|
+
|
|
3256
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
3257
|
+
expect(pendingOutboxIds()).toEqual(["ob-fresh"]);
|
|
3258
|
+
});
|
|
3259
|
+
|
|
3260
|
+
test("a reporter without ack-mode sources never prunes the outbox", async () => {
|
|
3261
|
+
// The daemon partition (watermark-only) does not own the outbox; the
|
|
3262
|
+
// prune belongs to the reporter instance that flushes ack-mode sources.
|
|
3263
|
+
seedExpiredAndFreshRows();
|
|
3264
|
+
const reporter = new UsageTelemetryReporter(
|
|
3265
|
+
[makeWatermarkSource("fake_wm")],
|
|
3266
|
+
fakeFlushCheckpointStore,
|
|
3267
|
+
);
|
|
3268
|
+
await reporter.flush();
|
|
3269
|
+
|
|
3270
|
+
expect(pendingOutboxIds()).toEqual(["ob-expired", "ob-fresh"]);
|
|
3271
|
+
});
|
|
3272
|
+
|
|
3004
3273
|
test("outboxSource purges corrupt-payload rows at collect while valid siblings ship", async () => {
|
|
3005
3274
|
// Raw inserts: the store API only writes JSON.stringify-ed events, so
|
|
3006
3275
|
// corrupt payloads (invalid JSON, non-object JSON) go in directly.
|
|
@@ -3010,20 +3279,20 @@ describe("UsageTelemetryReporter", () => {
|
|
|
3010
3279
|
{
|
|
3011
3280
|
id: "ob-corrupt-json",
|
|
3012
3281
|
name: OUTBOX_NAME,
|
|
3013
|
-
createdAt:
|
|
3282
|
+
createdAt: OUTBOX_BASE + 1000,
|
|
3014
3283
|
conversationId: null,
|
|
3015
3284
|
payload: "{not json",
|
|
3016
3285
|
},
|
|
3017
3286
|
{
|
|
3018
3287
|
id: "ob-non-object",
|
|
3019
3288
|
name: OUTBOX_NAME,
|
|
3020
|
-
createdAt:
|
|
3289
|
+
createdAt: OUTBOX_BASE + 2000,
|
|
3021
3290
|
conversationId: null,
|
|
3022
3291
|
payload: "42",
|
|
3023
3292
|
},
|
|
3024
3293
|
])
|
|
3025
3294
|
.run();
|
|
3026
|
-
seedOutboxRow("ob-valid",
|
|
3295
|
+
seedOutboxRow("ob-valid", OUTBOX_BASE + 3000);
|
|
3027
3296
|
const reporter = new UsageTelemetryReporter(
|
|
3028
3297
|
[outboxSource(OUTBOX_NAME)],
|
|
3029
3298
|
fakeFlushCheckpointStore,
|
|
@@ -3045,7 +3314,7 @@ describe("UsageTelemetryReporter", () => {
|
|
|
3045
3314
|
// 501 rows: the first collect fills BATCH_SIZE (500) and the fullBatch
|
|
3046
3315
|
// recursion ships the remaining row.
|
|
3047
3316
|
for (let i = 0; i < 501; i++) {
|
|
3048
|
-
seedOutboxRow(`ob-${String(i).padStart(3, "0")}`,
|
|
3317
|
+
seedOutboxRow(`ob-${String(i).padStart(3, "0")}`, OUTBOX_BASE + i);
|
|
3049
3318
|
}
|
|
3050
3319
|
const reporter = new UsageTelemetryReporter(
|
|
3051
3320
|
[outboxSource(OUTBOX_NAME)],
|