@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.
Files changed (456) hide show
  1. package/ARCHITECTURE.md +1 -1
  2. package/Dockerfile +8 -0
  3. package/docs/activation-funnel-telemetry.md +13 -7
  4. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  5. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  6. package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  7. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
  8. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  9. package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
  12. package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
  13. package/openapi.yaml +530 -107
  14. package/package.json +1 -1
  15. package/scripts/generate-openapi.ts +8 -0
  16. package/src/__tests__/activation-early-marking.test.ts +6 -5
  17. package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
  18. package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
  19. package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
  20. package/src/__tests__/app-bundler.test.ts +4 -12
  21. package/src/__tests__/app-executors.test.ts +2 -49
  22. package/src/__tests__/app-routes-csp.test.ts +114 -146
  23. package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
  24. package/src/__tests__/build-persisted-content.test.ts +96 -0
  25. package/src/__tests__/bundle-scanner.test.ts +27 -1
  26. package/src/__tests__/call-controller.test.ts +291 -0
  27. package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
  28. package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
  29. package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
  30. package/src/__tests__/compaction.benchmark.test.ts +2 -1
  31. package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
  32. package/src/__tests__/config-loader-backfill.test.ts +9 -4
  33. package/src/__tests__/config-schema-cmd.test.ts +10 -11
  34. package/src/__tests__/config-schema.test.ts +190 -257
  35. package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
  37. package/src/__tests__/conversation-agent-loop.test.ts +22 -9
  38. package/src/__tests__/conversation-error.test.ts +31 -0
  39. package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
  40. package/src/__tests__/conversation-process-callsite.test.ts +12 -19
  41. package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
  42. package/src/__tests__/conversation-summarize-route.test.ts +36 -44
  43. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
  44. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
  45. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
  46. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
  47. package/src/__tests__/conversation-usage.test.ts +4 -12
  48. package/src/__tests__/credential-routes.test.ts +250 -0
  49. package/src/__tests__/credential-security-invariants.test.ts +3 -0
  50. package/src/__tests__/db-migration-rollback.test.ts +22 -0
  51. package/src/__tests__/empty-response-hook.test.ts +186 -1
  52. package/src/__tests__/external-plugin-loader.test.ts +11 -5
  53. package/src/__tests__/heartbeat-service.test.ts +0 -28
  54. package/src/__tests__/host-shell-tool.test.ts +2 -0
  55. package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
  56. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
  57. package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
  58. package/src/__tests__/invite-routes-http.test.ts +12 -16
  59. package/src/__tests__/list-all-apps.test.ts +0 -4
  60. package/src/__tests__/llm-context-resolution.test.ts +32 -56
  61. package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
  62. package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
  63. package/src/__tests__/llm-resolver.test.ts +342 -602
  64. package/src/__tests__/llm-schema.test.ts +79 -37
  65. package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
  66. package/src/__tests__/media-stream-output.test.ts +259 -3
  67. package/src/__tests__/media-stream-server-integration.test.ts +22 -1
  68. package/src/__tests__/media-stream-stt-session.test.ts +47 -0
  69. package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
  70. package/src/__tests__/memory-recall-log-store.test.ts +47 -13
  71. package/src/__tests__/mock-gateway-ipc.ts +46 -1
  72. package/src/__tests__/mtime-cache.test.ts +61 -0
  73. package/src/__tests__/navigate-settings-tab.test.ts +2 -0
  74. package/src/__tests__/normalize-onboarding.test.ts +33 -0
  75. package/src/__tests__/onboarding-persona-write.test.ts +26 -0
  76. package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
  77. package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
  78. package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
  79. package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
  80. package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
  81. package/src/__tests__/provider-connections-backfill.test.ts +138 -0
  82. package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
  83. package/src/__tests__/provider-registry-ollama.test.ts +8 -18
  84. package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
  85. package/src/__tests__/provider-usage-tracking.test.ts +9 -19
  86. package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
  87. package/src/__tests__/published-app-updater.test.ts +22 -16
  88. package/src/__tests__/registry.test.ts +5 -16
  89. package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
  90. package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
  91. package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
  92. package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
  93. package/src/__tests__/reveal-success-registry.test.ts +123 -0
  94. package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
  95. package/src/__tests__/secret-fixtures.ts +9 -0
  96. package/src/__tests__/server-history-render.test.ts +28 -0
  97. package/src/__tests__/skills.test.ts +9 -4
  98. package/src/__tests__/slack-share-routes.test.ts +0 -1
  99. package/src/__tests__/stt-stream-session.test.ts +6 -5
  100. package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
  101. package/src/__tests__/subagent-disposal.test.ts +2 -0
  102. package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
  103. package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
  104. package/src/__tests__/subagent-manager-notify.test.ts +2 -0
  105. package/src/__tests__/subagent-role-registry.test.ts +37 -0
  106. package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
  107. package/src/__tests__/subagent-terminal-message.test.ts +50 -0
  108. package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
  109. package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
  110. package/src/__tests__/telemetry-routes.test.ts +99 -19
  111. package/src/__tests__/tool-audit.test.ts +34 -4
  112. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  113. package/src/__tests__/tool-profiler.test.ts +72 -1
  114. package/src/__tests__/tool-result-spool.test.ts +49 -4
  115. package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
  116. package/src/__tests__/ui-channel-variants.test.ts +108 -0
  117. package/src/__tests__/ui-shape-teaching.test.ts +255 -0
  118. package/src/__tests__/usage-attribution.test.ts +18 -41
  119. package/src/__tests__/user-plugin-loader.test.ts +4 -4
  120. package/src/__tests__/voice-config-update.test.ts +46 -0
  121. package/src/__tests__/voice-session-bridge.test.ts +216 -84
  122. package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
  123. package/src/agent/loop.ts +4 -3
  124. package/src/api/events/open-conversation.test.ts +64 -0
  125. package/src/api/events/open-conversation.ts +33 -0
  126. package/src/api/index.ts +6 -0
  127. package/src/api/responses/conversation-message.ts +5 -0
  128. package/src/apps/app-store.ts +25 -35
  129. package/src/bundler/app-bundler.ts +34 -48
  130. package/src/bundler/app-compiler.ts +39 -4
  131. package/src/bundler/bundle-scanner.ts +13 -0
  132. package/src/bundler/manifest.ts +1 -1
  133. package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
  134. package/src/calls/call-constants.ts +5 -0
  135. package/src/calls/call-controller.ts +100 -32
  136. package/src/calls/call-transport.ts +9 -0
  137. package/src/calls/media-stream-output.ts +107 -4
  138. package/src/calls/media-stream-server.ts +29 -9
  139. package/src/calls/media-stream-stt-session.ts +7 -1
  140. package/src/calls/media-turn-detector.ts +11 -1
  141. package/src/calls/voice-session-bridge.ts +84 -63
  142. package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
  143. package/src/cli/commands/__tests__/notifications.test.ts +24 -3
  144. package/src/cli/commands/config.help.ts +6 -6
  145. package/src/cli/commands/credentials.help.ts +13 -0
  146. package/src/cli/commands/credentials.ts +6 -1
  147. package/src/cli/commands/email.help.ts +7 -0
  148. package/src/cli/commands/email.ts +35 -1
  149. package/src/cli/commands/inference-providers.ts +168 -107
  150. package/src/cli/commands/inference.help.ts +118 -46
  151. package/src/cli/commands/memory/index.help.ts +23 -0
  152. package/src/cli/commands/memory/nodes.ts +146 -0
  153. package/src/cli/commands/notifications.help.ts +10 -10
  154. package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
  155. package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
  156. package/src/cli/commands/oauth/connect.test.ts +126 -0
  157. package/src/cli/commands/oauth/connect.ts +29 -6
  158. package/src/cli/commands/oauth/index.help.ts +7 -1
  159. package/src/cli/commands/oauth/status.test.ts +69 -3
  160. package/src/cli/commands/oauth/status.ts +50 -12
  161. package/src/cli/commands/plugins.help.ts +13 -2
  162. package/src/cli/commands/plugins.ts +61 -7
  163. package/src/cli/commands/telemetry.help.ts +13 -0
  164. package/src/cli/commands/telemetry.ts +45 -5
  165. package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
  166. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
  167. package/src/cli/lib/bundled-marketplace.json +93 -36
  168. package/src/cli/lib/inspect-plugin.ts +5 -1
  169. package/src/cli/lib/install-from-github.ts +68 -4
  170. package/src/cli/lib/plugin-catalog-local.ts +6 -2
  171. package/src/cli/lib/plugin-fingerprint.ts +3 -3
  172. package/src/cli/lib/upgrade-plugin.ts +236 -35
  173. package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
  174. package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
  175. package/src/config/__tests__/profile-materialization.test.ts +1 -88
  176. package/src/config/bundled-skills/AGENTS.md +3 -30
  177. package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
  178. package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
  179. package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
  180. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
  181. package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
  182. package/src/config/bundled-skills/schedule/SKILL.md +1 -1
  183. package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
  184. package/src/config/bundled-skills/settings/TOOLS.json +3 -1
  185. package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
  186. package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
  187. package/src/config/bundled-skills/subagent/SKILL.md +2 -0
  188. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  189. package/src/config/call-site-defaults.ts +3 -3
  190. package/src/config/feature-flag-registry.json +24 -39
  191. package/src/config/llm-resolver.ts +88 -475
  192. package/src/config/profile-materialization.ts +11 -11
  193. package/src/config/schema.ts +93 -127
  194. package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
  195. package/src/config/schemas/__tests__/stt.test.ts +31 -3
  196. package/src/config/schemas/live-voice.ts +10 -4
  197. package/src/config/schemas/llm.ts +52 -15
  198. package/src/config/schemas/memory-lifecycle.ts +1 -1
  199. package/src/config/schemas/memory-retrospective.ts +8 -0
  200. package/src/config/schemas/memory-v2.ts +2 -2
  201. package/src/config/schemas/memory-v3.ts +1 -1
  202. package/src/config/schemas/services.ts +6 -3
  203. package/src/config/schemas/stt.ts +38 -21
  204. package/src/config/schemas/tts.ts +13 -18
  205. package/src/config/skills.ts +31 -21
  206. package/src/context/compactor.ts +44 -7
  207. package/src/context/post-turn-tool-result-truncation.ts +4 -2
  208. package/src/context/tool-result-spool.ts +26 -5
  209. package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
  210. package/src/conversations/message-consolidation.ts +22 -2
  211. package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
  212. package/src/daemon/app-source-watcher.ts +17 -23
  213. package/src/daemon/chat-credential-redaction.ts +1365 -0
  214. package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
  215. package/src/daemon/conversation-agent-loop.ts +71 -31
  216. package/src/daemon/conversation-error.ts +36 -14
  217. package/src/daemon/conversation-process.ts +22 -0
  218. package/src/daemon/conversation-store.ts +35 -0
  219. package/src/daemon/conversation-surfaces.ts +25 -25
  220. package/src/daemon/conversation-tool-setup.ts +33 -7
  221. package/src/daemon/conversation.ts +55 -5
  222. package/src/daemon/handlers/shared.ts +33 -2
  223. package/src/daemon/lifecycle.ts +4 -4
  224. package/src/daemon/message-types/conversations.ts +6 -17
  225. package/src/daemon/providers-setup.ts +8 -0
  226. package/src/daemon/tool-setup-types.ts +7 -1
  227. package/src/daemon/wake-conversation-ops.ts +10 -1
  228. package/src/hooks/types.ts +9 -0
  229. package/src/ipc/__tests__/email-ipc.test.ts +90 -0
  230. package/src/ipc/gateway-client.test.ts +59 -0
  231. package/src/ipc/gateway-client.ts +70 -27
  232. package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
  233. package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
  234. package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
  235. package/src/live-voice/__tests__/protocol.test.ts +122 -0
  236. package/src/live-voice/live-voice-session.ts +443 -40
  237. package/src/live-voice/protocol.ts +143 -1
  238. package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
  239. package/src/monitoring/plugin-source-watch.ts +3 -62
  240. package/src/notifications/README.md +1 -1
  241. package/src/permissions/checker.ts +8 -4
  242. package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
  243. package/src/persistence/conversation-crud.ts +104 -2
  244. package/src/persistence/db-init.ts +14 -3
  245. package/src/persistence/job-handlers/cleanup.ts +15 -7
  246. package/src/persistence/llm-request-log-store.ts +88 -52
  247. package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
  248. package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
  249. package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
  250. package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
  251. package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
  252. package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
  253. package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
  254. package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
  255. package/src/persistence/migrations/helpers/relocation.ts +44 -1
  256. package/src/persistence/migrations/run-migrations.ts +25 -1
  257. package/src/persistence/schema/infrastructure.ts +9 -0
  258. package/src/persistence/schema/memory-core.ts +3 -0
  259. package/src/persistence/schema/memory-injection.ts +2 -0
  260. package/src/persistence/steps.ts +36 -0
  261. package/src/platform/client.test.ts +1 -44
  262. package/src/platform/client.ts +10 -20
  263. package/src/platform/consent-cache.test.ts +89 -23
  264. package/src/platform/consent-cache.ts +71 -33
  265. package/src/plugin-api/constants.ts +12 -0
  266. package/src/plugin-api/conversation-turn.ts +37 -14
  267. package/src/plugin-api/index.ts +11 -1
  268. package/src/plugin-api/resolve-credential.ts +75 -0
  269. package/src/plugin-api/vision-support.test.ts +8 -19
  270. package/src/plugin-api/vision-support.ts +26 -17
  271. package/src/plugins/collect-source-versions.ts +77 -0
  272. package/src/plugins/defaults/compaction/compact.ts +6 -0
  273. package/src/plugins/defaults/compaction/window-manager.ts +9 -0
  274. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
  275. package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
  276. package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
  277. package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
  278. package/src/plugins/defaults/index.ts +8 -1
  279. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
  280. package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
  281. package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
  282. package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
  283. package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
  284. package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
  285. package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
  286. package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
  287. package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
  288. package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
  289. package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
  290. package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
  291. package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
  292. package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
  293. package/src/plugins/defaults/memory/graph/store.ts +114 -0
  294. package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
  295. package/src/plugins/defaults/memory/graph/tools.ts +47 -13
  296. package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
  297. package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
  298. package/src/plugins/defaults/memory/memory-db.ts +3 -2
  299. package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
  300. package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
  301. package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
  302. package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
  303. package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
  304. package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
  305. package/src/plugins/defaults/memory/path-containment.ts +21 -0
  306. package/src/plugins/defaults/memory/prompt-override.ts +51 -13
  307. package/src/plugins/defaults/memory/tools.test.ts +34 -0
  308. package/src/plugins/defaults/memory/tools.ts +12 -2
  309. package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
  310. package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
  311. package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
  312. package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
  313. package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
  314. package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
  315. package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
  316. package/src/plugins/defaults/memory/v2/migration.ts +46 -16
  317. package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
  318. package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
  319. package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
  320. package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
  321. package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
  322. package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
  323. package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
  324. package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
  325. package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
  326. package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
  327. package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
  328. package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
  329. package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
  330. package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
  331. package/src/plugins/defaults/memory/v3/prune.ts +17 -11
  332. package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
  333. package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
  334. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
  335. package/src/plugins/external-plugin-loader.ts +15 -15
  336. package/src/plugins/mtime-cache.ts +47 -0
  337. package/src/plugins/pipeline.ts +9 -1
  338. package/src/plugins/plugin-execution-context.ts +44 -0
  339. package/src/plugins/plugin-tree-walk.ts +31 -24
  340. package/src/plugins/source-fingerprint.ts +3 -4
  341. package/src/prompts/normalize-onboarding.ts +12 -0
  342. package/src/prompts/persona-resolver.ts +8 -0
  343. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
  344. package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
  345. package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
  346. package/src/providers/__tests__/retry-callsite.test.ts +235 -215
  347. package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
  348. package/src/providers/atlascloud/client.ts +10 -49
  349. package/src/providers/baseten/client.ts +43 -0
  350. package/src/providers/call-site-routing.ts +26 -13
  351. package/src/providers/connection-resolution.ts +9 -11
  352. package/src/providers/fetch-provider-catalog.ts +4 -2
  353. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
  354. package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
  355. package/src/providers/inference/adapter-factory.ts +27 -6
  356. package/src/providers/inference/auth.ts +29 -1
  357. package/src/providers/inference/backfill.ts +61 -10
  358. package/src/providers/inference/connection-availability.ts +3 -2
  359. package/src/providers/inference/resolve-auth.ts +9 -1
  360. package/src/providers/model-catalog.ts +37 -0
  361. package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
  362. package/src/providers/openai/api-key-validation.ts +70 -0
  363. package/src/providers/retry.ts +2 -0
  364. package/src/providers/types.ts +11 -4
  365. package/src/providers/vellum-model-routing.test.ts +26 -0
  366. package/src/providers/vellum-model-routing.ts +30 -0
  367. package/src/providers/voice-error-copy.ts +47 -0
  368. package/src/runtime/agent-wake.ts +40 -24
  369. package/src/runtime/for-chat-mint-registry.ts +118 -0
  370. package/src/runtime/reveal-nonce.ts +49 -0
  371. package/src/runtime/reveal-success-registry.ts +306 -0
  372. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
  373. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
  374. package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
  375. package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
  376. package/src/runtime/routes/app-management-routes.ts +152 -45
  377. package/src/runtime/routes/app-routes.ts +16 -77
  378. package/src/runtime/routes/canned-message-complete.ts +16 -16
  379. package/src/runtime/routes/conversation-management-routes.ts +17 -14
  380. package/src/runtime/routes/conversation-query-routes.ts +54 -6
  381. package/src/runtime/routes/conversation-routes.ts +48 -2
  382. package/src/runtime/routes/credential-routes.ts +116 -3
  383. package/src/runtime/routes/email-routes.ts +17 -1
  384. package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
  385. package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
  386. package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
  387. package/src/runtime/routes/inference-send-routes.ts +1 -1
  388. package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
  389. package/src/runtime/routes/plugins-routes.ts +37 -6
  390. package/src/runtime/routes/publish-routes.ts +15 -18
  391. package/src/runtime/routes/schedule-worker-routes.ts +9 -0
  392. package/src/runtime/routes/secret-routes.ts +10 -0
  393. package/src/runtime/routes/telemetry-routes.ts +149 -45
  394. package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
  395. package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
  396. package/src/schedule/schedule-store.ts +12 -1
  397. package/src/schedule/schedule-timezone.ts +63 -0
  398. package/src/schedule/scheduler.ts +100 -6
  399. package/src/schedule/worker-control.ts +19 -0
  400. package/src/security/auth-fallback-events-store.ts +7 -6
  401. package/src/security/secret-scanner.ts +26 -1
  402. package/src/services/published-app-updater.ts +6 -11
  403. package/src/stt/stt-stream-session.ts +36 -16
  404. package/src/subagent/manager.ts +34 -8
  405. package/src/telemetry/AGENTS.md +30 -1
  406. package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
  407. package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
  408. package/src/telemetry/config-setting-snapshot.ts +44 -10
  409. package/src/telemetry/telemetry-event-sources.test.ts +124 -30
  410. package/src/telemetry/telemetry-event-sources.ts +137 -83
  411. package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
  412. package/src/telemetry/telemetry-events-outbox.ts +60 -9
  413. package/src/telemetry/telemetry-wire-source.json +1 -1
  414. package/src/telemetry/telemetry-wire-validation.ts +39 -2
  415. package/src/telemetry/telemetry-wire.generated.ts +8 -0
  416. package/src/telemetry/tool-audit.ts +15 -9
  417. package/src/telemetry/tool-executed-events-store.test.ts +1 -1
  418. package/src/telemetry/turn-events-store.ts +20 -0
  419. package/src/telemetry/types.ts +45 -12
  420. package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
  421. package/src/telemetry/usage-telemetry-reporter.ts +117 -18
  422. package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
  423. package/src/telemetry/watchdog-direct-emit.ts +11 -6
  424. package/src/tools/apps/executors.ts +11 -36
  425. package/src/tools/executor.ts +14 -1
  426. package/src/tools/host-terminal/host-shell.ts +6 -2
  427. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
  428. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
  429. package/src/tools/network/web-fetch.ts +6 -2
  430. package/src/tools/skills/sandbox-runner.ts +5 -2
  431. package/src/tools/subagent/spawn.ts +7 -11
  432. package/src/tools/terminal/shell.ts +5 -2
  433. package/src/tools/tool-manifest.ts +0 -2
  434. package/src/tools/tool-profiler.ts +37 -6
  435. package/src/tools/ui-surface/channel-variants.ts +101 -0
  436. package/src/tools/ui-surface/definitions.ts +23 -67
  437. package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
  438. package/src/tts/__tests__/provider-adapters.test.ts +18 -0
  439. package/src/tts/provider-catalog.ts +2 -4
  440. package/src/tts/providers/deepgram-provider.ts +10 -1
  441. package/src/types/onboarding-context.ts +8 -0
  442. package/src/usage/attribution.ts +18 -112
  443. package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
  444. package/src/util/provider-error-patterns.ts +7 -1
  445. package/src/util/worker-process.ts +105 -0
  446. package/src/watcher/__tests__/telemetry.test.ts +17 -4
  447. package/src/watcher/telemetry.ts +5 -5
  448. package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
  449. package/src/workspace/migrations/registry.ts +2 -0
  450. package/src/workspace/provider-commit-message-generator.ts +6 -4
  451. package/src/__tests__/app-open-proxy.test.ts +0 -67
  452. package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
  453. package/src/onboarding/onboarding-research-events-store.ts +0 -104
  454. package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
  455. package/src/tools/apps/definitions.ts +0 -73
  456. 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
- const mockGetCachedShareAnalytics = mock(() => true);
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
- getCachedShareAnalytics: mockGetCachedShareAnalytics,
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
- mockGetCachedShareAnalytics.mockReset();
328
- mockGetCachedShareAnalytics.mockReturnValue(true);
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
- mockGetCachedShareAnalytics.mockReturnValue(false);
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
- mockGetCachedShareAnalytics.mockReturnValue(false);
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
- mockGetCachedShareAnalytics.mockReturnValue(false);
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
- mockGetCachedShareAnalytics.mockReturnValue(false);
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
- mockGetCachedShareAnalytics.mockReturnValue(true);
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
- createdAt: 1700000100000,
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
- mockGetCachedShareAnalytics.mockReturnValue(false);
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
- mockGetCachedShareAnalytics.mockReturnValue(true);
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
- mockGetCachedShareAnalytics.mockReturnValue(false);
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
- mockGetCachedShareAnalytics.mockReturnValue(true);
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
- mockGetCachedShareAnalytics.mockReturnValue(false);
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", 1700000001000);
2955
- seedOutboxRow("ob-2", 1700000002000);
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", 1700000001000);
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
- mockGetCachedShareAnalytics.mockReturnValue(false);
2991
- seedOutboxRow("ob-1", 1700000001000);
2992
- seedOutboxRow("ob-2", 1700000002000);
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: 1700000001000,
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: 1700000002000,
3289
+ createdAt: OUTBOX_BASE + 2000,
3021
3290
  conversationId: null,
3022
3291
  payload: "42",
3023
3292
  },
3024
3293
  ])
3025
3294
  .run();
3026
- seedOutboxRow("ob-valid", 1700000003000);
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")}`, 1700000000000 + i);
3317
+ seedOutboxRow(`ob-${String(i).padStart(3, "0")}`, OUTBOX_BASE + i);
3049
3318
  }
3050
3319
  const reporter = new UsageTelemetryReporter(
3051
3320
  [outboxSource(OUTBOX_NAME)],