@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
@@ -5,21 +5,12 @@
5
5
  * tool use."
6
6
  *
7
7
  * This is the root cause of repeated failures in memory graph operations
8
- * (extraction, narrative, pattern-scan, consolidation) when the user's default
9
- * LLM config has `thinking.enabled: true` and the call site uses forced
8
+ * (extraction, narrative, pattern-scan, consolidation) when the resolved LLM
9
+ * config has `thinking.enabled: true` and the call site uses forced
10
10
  * `tool_choice` without explicitly disabling thinking.
11
11
  */
12
12
 
13
- import { beforeAll, beforeEach, describe, expect, test } from "bun:test";
14
-
15
- import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
16
-
17
- // Legacy-shaped fixtures (llm.default-centric resolution): pinned to the
18
- // flag-off cascade. Override-or-default (flag-on) semantics are pinned by
19
- // llm-resolver-override-or-default.test.ts and its companion suites.
20
- beforeAll(() => {
21
- setOverridesForTesting({ "override-or-default-resolution": false });
22
- });
13
+ import { beforeEach, describe, expect, test } from "bun:test";
23
14
 
24
15
  import { RetryProvider } from "../providers/retry.js";
25
16
  import type {
@@ -72,13 +63,13 @@ const userMessage: Message = {
72
63
  describe("retry normalization: thinking + forced tool_choice", () => {
73
64
  test("strips thinking when tool_choice forces a specific tool (type: 'tool')", async () => {
74
65
  setLlmConfig({
75
- default: {
76
- provider: "anthropic",
77
- model: "claude-opus-4-7",
78
- thinking: { enabled: true, streamThinking: true },
66
+ callSites: {
67
+ memoryExtraction: {
68
+ provider: "anthropic",
69
+ model: "claude-opus-4-7",
70
+ thinking: { enabled: true, streamThinking: true },
71
+ },
79
72
  },
80
- // Disable the catalog default so resolution lands on llm.default.
81
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
82
73
  });
83
74
  const { provider, lastConfig } = makePipeline("anthropic");
84
75
  await provider.sendMessage([userMessage], {
@@ -98,13 +89,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
98
89
 
99
90
  test("strips thinking when tool_choice type is 'any'", async () => {
100
91
  setLlmConfig({
101
- default: {
102
- provider: "anthropic",
103
- model: "claude-opus-4-7",
104
- thinking: { enabled: true },
92
+ callSites: {
93
+ memoryExtraction: {
94
+ provider: "anthropic",
95
+ model: "claude-opus-4-7",
96
+ thinking: { enabled: true },
97
+ },
105
98
  },
106
- // Disable the catalog default so resolution lands on llm.default.
107
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
108
99
  });
109
100
  const { provider, lastConfig } = makePipeline("anthropic");
110
101
  await provider.sendMessage([userMessage], {
@@ -118,13 +109,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
118
109
 
119
110
  test("preserves thinking when tool_choice type is 'auto'", async () => {
120
111
  setLlmConfig({
121
- default: {
122
- provider: "anthropic",
123
- model: "claude-opus-4-7",
124
- thinking: { enabled: true },
112
+ callSites: {
113
+ memoryExtraction: {
114
+ provider: "anthropic",
115
+ model: "claude-opus-4-7",
116
+ thinking: { enabled: true },
117
+ },
125
118
  },
126
- // Disable the catalog default so resolution lands on llm.default.
127
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
128
119
  });
129
120
  const { provider, lastConfig } = makePipeline("anthropic");
130
121
  await provider.sendMessage([userMessage], {
@@ -138,13 +129,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
138
129
 
139
130
  test("preserves thinking when no tool_choice is set", async () => {
140
131
  setLlmConfig({
141
- default: {
142
- provider: "anthropic",
143
- model: "claude-opus-4-7",
144
- thinking: { enabled: true },
132
+ callSites: {
133
+ memoryExtraction: {
134
+ provider: "anthropic",
135
+ model: "claude-opus-4-7",
136
+ thinking: { enabled: true },
137
+ },
145
138
  },
146
- // Disable the catalog default so resolution lands on llm.default.
147
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
148
139
  });
149
140
  const { provider, lastConfig } = makePipeline("anthropic");
150
141
  await provider.sendMessage([userMessage], {
@@ -184,13 +175,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
184
175
 
185
176
  test("preserves resolved thinking: disabled with forced tool_choice", async () => {
186
177
  setLlmConfig({
187
- default: {
188
- provider: "anthropic",
189
- model: "claude-opus-4-7",
190
- thinking: { enabled: false },
178
+ callSites: {
179
+ memoryExtraction: {
180
+ provider: "anthropic",
181
+ model: "claude-opus-4-7",
182
+ thinking: { enabled: false },
183
+ },
191
184
  },
192
- // Disable the catalog default so resolution lands on llm.default.
193
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
194
185
  });
195
186
  const { provider, lastConfig } = makePipeline("anthropic");
196
187
  await provider.sendMessage([userMessage], {
@@ -204,13 +195,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
204
195
 
205
196
  test("strips thinking for openrouter with anthropic model and forced tool_choice", async () => {
206
197
  setLlmConfig({
207
- default: {
208
- provider: "openrouter",
209
- model: "anthropic/claude-opus-4.7",
210
- thinking: { enabled: true },
198
+ callSites: {
199
+ memoryExtraction: {
200
+ provider: "openrouter",
201
+ model: "anthropic/claude-opus-4.7",
202
+ thinking: { enabled: true },
203
+ },
211
204
  },
212
- // Disable the catalog default so resolution lands on llm.default.
213
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
214
205
  });
215
206
  const { provider, lastConfig } = makePipeline("openrouter");
216
207
  await provider.sendMessage([userMessage], {
@@ -224,13 +215,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
224
215
 
225
216
  test("preserves thinking for openrouter with non-anthropic model and forced tool_choice", async () => {
226
217
  setLlmConfig({
227
- default: {
228
- provider: "openrouter",
229
- model: "x-ai/grok-3-mini",
230
- thinking: { enabled: true },
218
+ callSites: {
219
+ memoryExtraction: {
220
+ provider: "openrouter",
221
+ model: "x-ai/grok-3-mini",
222
+ thinking: { enabled: true },
223
+ },
231
224
  },
232
- // Disable the catalog default so resolution lands on llm.default.
233
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
234
225
  });
235
226
  const { provider, lastConfig } = makePipeline("openrouter");
236
227
  await provider.sendMessage([userMessage], {
@@ -246,13 +237,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
246
237
 
247
238
  test("strips thinking for vercel-ai-gateway with anthropic model and forced tool_choice", async () => {
248
239
  setLlmConfig({
249
- default: {
250
- provider: "vercel-ai-gateway",
251
- model: "anthropic/claude-opus-4.6",
252
- thinking: { enabled: true },
240
+ callSites: {
241
+ memoryExtraction: {
242
+ provider: "vercel-ai-gateway",
243
+ model: "anthropic/claude-opus-4.6",
244
+ thinking: { enabled: true },
245
+ },
253
246
  },
254
- // Disable the catalog default so resolution lands on llm.default.
255
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
256
247
  });
257
248
  const { provider, lastConfig } = makePipeline("vercel-ai-gateway");
258
249
  await provider.sendMessage([userMessage], {
@@ -266,13 +257,13 @@ describe("retry normalization: thinking + forced tool_choice", () => {
266
257
 
267
258
  test("preserves thinking for vercel-ai-gateway with non-anthropic model and forced tool_choice", async () => {
268
259
  setLlmConfig({
269
- default: {
270
- provider: "vercel-ai-gateway",
271
- model: "xai/grok-4.3",
272
- thinking: { enabled: true },
260
+ callSites: {
261
+ memoryExtraction: {
262
+ provider: "vercel-ai-gateway",
263
+ model: "xai/grok-4.3",
264
+ thinking: { enabled: true },
265
+ },
273
266
  },
274
- // Disable the catalog default so resolution lands on llm.default.
275
- profiles: { "cost-optimized": { source: "managed", status: "disabled" } },
276
267
  });
277
268
  const { provider, lastConfig } = makePipeline("vercel-ai-gateway");
278
269
  await provider.sendMessage([userMessage], {
@@ -5,16 +5,7 @@
5
5
  * the unknown field on the wire.
6
6
  */
7
7
 
8
- import { beforeAll, beforeEach, describe, expect, test } from "bun:test";
9
-
10
- import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
11
-
12
- // Legacy-shaped fixtures (llm.default-centric resolution): pinned to the
13
- // flag-off cascade. Override-or-default (flag-on) semantics are pinned by
14
- // llm-resolver-override-or-default.test.ts and its companion suites.
15
- beforeAll(() => {
16
- setOverridesForTesting({ "override-or-default-resolution": false });
17
- });
8
+ import { beforeEach, describe, expect, test } from "bun:test";
18
9
 
19
10
  import { RetryProvider } from "../providers/retry.js";
20
11
  import type {
@@ -67,10 +58,12 @@ const userMessage: Message = {
67
58
  describe("retry normalization for verbosity", () => {
68
59
  test("forwards verbosity on the outbound config for openai", async () => {
69
60
  setLlmConfig({
70
- default: {
71
- provider: "openai",
72
- model: "gpt-5.5",
73
- verbosity: "high",
61
+ callSites: {
62
+ mainAgent: {
63
+ provider: "openai",
64
+ model: "gpt-5.5",
65
+ verbosity: "high",
66
+ },
74
67
  },
75
68
  });
76
69
  const { provider, lastConfig } = makePipeline("openai");
@@ -96,13 +89,17 @@ describe("retry normalization for verbosity", () => {
96
89
  expect(lastConfig()?.verbosity).toBe(undefined);
97
90
  });
98
91
 
99
- test("call-site override replaces default verbosity", async () => {
92
+ test("call-site override replaces the winning profile's verbosity", async () => {
100
93
  setLlmConfig({
101
- default: {
102
- provider: "openai",
103
- model: "gpt-5.5",
104
- verbosity: "low",
94
+ profiles: {
95
+ "openai-profile": {
96
+ source: "user",
97
+ provider: "openai",
98
+ model: "gpt-5.5",
99
+ verbosity: "low",
100
+ },
105
101
  },
102
+ activeProfile: "openai-profile",
106
103
  callSites: {
107
104
  mainAgent: { verbosity: "high" },
108
105
  },
@@ -116,11 +113,15 @@ describe("retry normalization for verbosity", () => {
116
113
 
117
114
  test("per-call explicit verbosity overrides resolved call-site value", async () => {
118
115
  setLlmConfig({
119
- default: {
120
- provider: "openai",
121
- model: "gpt-5.5",
122
- verbosity: "low",
116
+ profiles: {
117
+ "openai-profile": {
118
+ source: "user",
119
+ provider: "openai",
120
+ model: "gpt-5.5",
121
+ verbosity: "low",
122
+ },
123
123
  },
124
+ activeProfile: "openai-profile",
124
125
  callSites: {
125
126
  mainAgent: { verbosity: "medium" },
126
127
  },
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Tests for the reveal-success registry's retention behavior (LUM-2768).
3
+ *
4
+ * The registry is the ground truth the chat-credential persist seams use
5
+ * to prove a reveal ran; losing a record before its tool window closes
6
+ * silently drops the candidate and the printed value persists raw. Recent
7
+ * records must therefore survive the count cap — only the age bound may
8
+ * remove them while a window could still be active.
9
+ */
10
+ import {
11
+ afterEach,
12
+ beforeEach,
13
+ describe,
14
+ expect,
15
+ setSystemTime,
16
+ test,
17
+ } from "bun:test";
18
+
19
+ import {
20
+ _recordCountForTest,
21
+ _resetRevealSuccessRegistryForTest,
22
+ closeRevealProofWindow,
23
+ currentRevealSuccessWatermark,
24
+ openRevealProofWindow,
25
+ recordRevealSuccess,
26
+ revealedValueSince,
27
+ } from "../runtime/reveal-success-registry.js";
28
+
29
+ beforeEach(() => {
30
+ _resetRevealSuccessRegistryForTest();
31
+ // Recording is gated on an open proof window (a staged tool reveal);
32
+ // these tests exercise retention semantics, so arm one for the file.
33
+ openRevealProofWindow();
34
+ });
35
+
36
+ afterEach(() => {
37
+ setSystemTime();
38
+ });
39
+
40
+ const TEST_NONCE = "nonce-registry-test";
41
+
42
+ describe("reveal success registry retention", () => {
43
+ test("recent records survive the count cap while a tool window is active", () => {
44
+ // A single command with hundreds of reveal invocations records every
45
+ // success BEFORE the tool result triggers the proof check. A hard
46
+ // count cap would evict the earliest ones mid-window, so their values
47
+ // could never become candidates.
48
+ const watermark = currentRevealSuccessWatermark();
49
+ for (let i = 0; i < 300; i++) {
50
+ recordRevealSuccess(`service-${i}`, "api_key", `value-${i}`, TEST_NONCE);
51
+ }
52
+ expect(
53
+ revealedValueSince(watermark, "service-0", "api_key", TEST_NONCE),
54
+ ).toBe("value-0");
55
+ expect(
56
+ revealedValueSince(watermark, "service-299", "api_key", TEST_NONCE),
57
+ ).toBe("value-299");
58
+ });
59
+
60
+ test("watermark gating still excludes earlier successes", () => {
61
+ recordRevealSuccess("openai", "api_key", "before-watermark", TEST_NONCE);
62
+ const watermark = currentRevealSuccessWatermark();
63
+ expect(
64
+ revealedValueSince(watermark, "openai", "api_key", TEST_NONCE),
65
+ ).toBeUndefined();
66
+ recordRevealSuccess("openai", "api_key", "after-watermark", TEST_NONCE);
67
+ expect(revealedValueSince(watermark, "openai", "api_key", TEST_NONCE)).toBe(
68
+ "after-watermark",
69
+ );
70
+ });
71
+
72
+ test("records nothing while no proof window is open", () => {
73
+ // A user's own local CLI reveal outside any assistant tool turn has no
74
+ // pending proof to satisfy — retaining its plaintext for the age bound
75
+ // would expand every CLI reveal's exposure for nothing.
76
+ _resetRevealSuccessRegistryForTest();
77
+ recordRevealSuccess("svc", "f", "hunter2-no-window", TEST_NONCE);
78
+ expect(_recordCountForTest()).toBe(0);
79
+ expect(revealedValueSince(0, "svc", "f", TEST_NONCE)).toBeUndefined();
80
+
81
+ // With a window open the same reveal records; closing the last window
82
+ // both stops further recording and drops the retained plaintext.
83
+ const token = openRevealProofWindow();
84
+ recordRevealSuccess("svc", "f", "hunter2-windowed", TEST_NONCE);
85
+ expect(revealedValueSince(0, "svc", "f", TEST_NONCE)).toBe(
86
+ "hunter2-windowed",
87
+ );
88
+ closeRevealProofWindow(token);
89
+ recordRevealSuccess("svc", "f", "hunter2-after-close", TEST_NONCE);
90
+ expect(revealedValueSince(0, "svc", "f", TEST_NONCE)).toBeUndefined();
91
+ expect(_recordCountForTest()).toBe(0);
92
+ });
93
+
94
+ test("closing the last proof window drops retained plaintext immediately", () => {
95
+ // Proof consumption reads happen BEFORE a window closes, so once the
96
+ // last window is gone nothing can ever read the records again — they
97
+ // must not sit in memory until the age bound.
98
+ _resetRevealSuccessRegistryForTest();
99
+ const first = openRevealProofWindow();
100
+ const second = openRevealProofWindow();
101
+ recordRevealSuccess("svc", "f", "hunter2-windowed", TEST_NONCE);
102
+ closeRevealProofWindow(first);
103
+ // A concurrent staging (second window) may still consume the record.
104
+ expect(_recordCountForTest()).toBe(1);
105
+ closeRevealProofWindow(second);
106
+ expect(_recordCountForTest()).toBe(0);
107
+ });
108
+
109
+ test("an expired record is EVICTED on read, not merely filtered", () => {
110
+ // Records hold credential plaintext. Filtering expired records out of
111
+ // read results while retaining them in memory would keep a lone
112
+ // reveal's secret alive indefinitely — eviction must happen on any
113
+ // registry activity (a write, a read, or the idle timer).
114
+ setSystemTime(new Date("2026-01-01T00:00:00Z"));
115
+ recordRevealSuccess("svc", "f", "hunter2-expiring", TEST_NONCE);
116
+ expect(_recordCountForTest()).toBe(1);
117
+
118
+ // Past the 6-hour age bound with NO further writes.
119
+ setSystemTime(new Date("2026-01-01T07:00:00Z"));
120
+ expect(revealedValueSince(0, "svc", "f", TEST_NONCE)).toBeUndefined();
121
+ expect(_recordCountForTest()).toBe(0);
122
+ });
123
+ });
@@ -0,0 +1,130 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import {
4
+ createConversation,
5
+ ensureConversationExists,
6
+ getConversation,
7
+ } from "../persistence/conversation-crud.js";
8
+ import { getDb } from "../persistence/db-connection.js";
9
+ import { initializeDb } from "../persistence/db-init.js";
10
+
11
+ await initializeDb();
12
+
13
+ // Capture the list-invalidation calls `runConversationTurn` fires when it
14
+ // creates a brand-new conversation row, without pulling in the real sync
15
+ // publisher (which reaches for live SSE subscribers).
16
+ const listChangedCalls: Array<{ kind: string; conversationId: string }> = [];
17
+ mock.module("../runtime/sync/resource-sync-events.js", () => ({
18
+ publishConversationListAndMetadataChanged: (
19
+ kind: string,
20
+ conversationId: string,
21
+ ) => {
22
+ listChangedCalls.push({ kind, conversationId });
23
+ },
24
+ }));
25
+
26
+ // Stub the heavy machinery: the in-memory conversation build (provider wiring,
27
+ // system prompt, history hydration) and the SSE event fan-out. The agent turn
28
+ // itself is a no-op — this test only asserts that the `conversations` row is
29
+ // persisted before the turn runs. The real `getOrCreateConversation` now
30
+ // creates the DB row before hydrating, so the mock mirrors that by calling
31
+ // `ensureConversationExists`. The persistence module is intentionally NOT
32
+ // mocked so the real `ensureConversationExists` runs against the real DB.
33
+ let lastProcessMessageConversationId: string | undefined;
34
+ mock.module("../daemon/conversation-store.js", () => ({
35
+ getOrCreateConversation: async (
36
+ conversationId: string,
37
+ options?: { conversationType?: string },
38
+ ) => {
39
+ if (!getConversation(conversationId)) {
40
+ if (options?.conversationType) {
41
+ createConversation({
42
+ id: conversationId,
43
+ conversationType: options.conversationType as "standard" | "background",
44
+ });
45
+ } else {
46
+ ensureConversationExists(conversationId);
47
+ }
48
+ }
49
+ return {
50
+ abortController: undefined,
51
+ isProcessing: () => false,
52
+ async processMessage() {
53
+ // The row must already exist by the time the turn persists its user
54
+ // message — record the id so the FK precondition can be asserted.
55
+ lastProcessMessageConversationId = conversationId;
56
+ return "user-message-id";
57
+ },
58
+ enqueueMessage: () => ({ rejected: false }),
59
+ };
60
+ },
61
+ }));
62
+
63
+ mock.module("../runtime/assistant-event-hub.js", () => ({
64
+ broadcastMessage: () => {},
65
+ }));
66
+
67
+ mock.module("../providers/media-resolve.js", () => ({
68
+ resolveMediaSourceData: () => null,
69
+ }));
70
+
71
+ // Import under test AFTER the mocks are registered so its dynamic imports
72
+ // resolve to the stubs above.
73
+ const { runConversationTurn } =
74
+ await import("../plugin-api/conversation-turn.js");
75
+
76
+ describe("runConversationTurn persistence", () => {
77
+ beforeEach(() => {
78
+ const db = getDb();
79
+ db.run("DELETE FROM messages");
80
+ db.run("DELETE FROM conversations");
81
+ listChangedCalls.length = 0;
82
+ lastProcessMessageConversationId = undefined;
83
+ });
84
+
85
+ test("persists a conversations row for a freshly-minted conversation", async () => {
86
+ const result = await runConversationTurn({
87
+ content: [{ type: "text", text: "hello" }],
88
+ });
89
+
90
+ // The row exists on disk — not just as an in-memory Conversation object —
91
+ // so the user-message persist inside the turn has its FK target.
92
+ const row = getConversation(result.conversationId);
93
+ expect(row?.id).toBe(result.conversationId);
94
+ expect(lastProcessMessageConversationId).toBe(result.conversationId);
95
+
96
+ // Siblings/sidebars are told about the new conversation, mirroring the
97
+ // send-message route.
98
+ expect(listChangedCalls).toEqual([
99
+ { kind: "created", conversationId: result.conversationId },
100
+ ]);
101
+ });
102
+
103
+ test("adopts a caller-supplied conversation id verbatim when no row exists", async () => {
104
+ const conversationId = "0f9c1e2a-3b4d-5e6f-7a8b-9c0d1e2f3a4b";
105
+
106
+ const result = await runConversationTurn({
107
+ conversationId,
108
+ content: [{ type: "text", text: "hello" }],
109
+ });
110
+
111
+ expect(result.conversationId).toBe(conversationId);
112
+ expect(getConversation(conversationId)?.id).toBe(conversationId);
113
+ expect(listChangedCalls).toEqual([{ kind: "created", conversationId }]);
114
+ });
115
+
116
+ test("is a no-op for an already-persisted conversation row", async () => {
117
+ const existing = createConversation({ title: "already here" });
118
+ listChangedCalls.length = 0;
119
+
120
+ const result = await runConversationTurn({
121
+ conversationId: existing.id,
122
+ content: [{ type: "text", text: "follow up" }],
123
+ });
124
+
125
+ expect(result.conversationId).toBe(existing.id);
126
+ // Row is untouched and no duplicate "created" invalidation fires.
127
+ expect(getConversation(existing.id)?.title).toBe("already here");
128
+ expect(listChangedCalls).toEqual([]);
129
+ });
130
+ });
@@ -18,3 +18,12 @@ export const SYNTHETIC_OPENAI_PROJECT_KEY =
18
18
  /** The marker `redactSecrets()` substitutes for the key above. */
19
19
  export const OPENAI_PROJECT_KEY_REDACTION_MARKER =
20
20
  '<redacted type="OpenAI Project Key" />';
21
+
22
+ /**
23
+ * An opaque/manual credential value with NO scanner-recognizable shape — no
24
+ * known prefix, not high-entropy enough for the AWS-secret heuristic, no
25
+ * private-key header. The scanner leaves it untouched, so only the
26
+ * reveal-candidate exact-match machinery can redact it. Used to prove the
27
+ * candidate fallback catches values the pattern scanner cannot classify.
28
+ */
29
+ export const SYNTHETIC_OPAQUE_CREDENTIAL = "hunter2-manual-token-value";
@@ -15,6 +15,34 @@ import { getDb } from "../persistence/db-connection.js";
15
15
  import { initializeDb } from "../persistence/db-init.js";
16
16
  await initializeDb();
17
17
 
18
+ describe("renderHistoryContent sentinel forgery guard", () => {
19
+ const FORGED = "\u3014redacted:GitHub Token:github-app:pem\u3015";
20
+
21
+ test("neutralizes sentinel-shaped text in blocks without the redaction rider (pre-feature rows)", () => {
22
+ const output = renderHistoryContent([
23
+ { type: "text", text: `quoted: ${FORGED}` },
24
+ ]);
25
+ // The word joiner breaks the chip regex; text is otherwise unchanged.
26
+ expect(output.textSegments[0]).toBe(
27
+ `quoted: \u3014\u2060redacted:GitHub Token:github-app:pem\u3015`,
28
+ );
29
+ });
30
+
31
+ test("passes sentinels through verbatim when the block carries the redaction rider", () => {
32
+ const output = renderHistoryContent([
33
+ { type: "text", text: `key: ${FORGED}`, _redactionVersion: 2 },
34
+ ]);
35
+ expect(output.textSegments[0]).toBe(`key: ${FORGED}`);
36
+ });
37
+
38
+ test("rider-less text without sentinels is untouched", () => {
39
+ const output = renderHistoryContent([
40
+ { type: "text", text: "ordinary history text" },
41
+ ]);
42
+ expect(output.textSegments[0]).toBe("ordinary history text");
43
+ });
44
+ });
45
+
18
46
  describe("renderHistoryContent", () => {
19
47
  test("renders text-only content unchanged", () => {
20
48
  const output = renderHistoryContent([
@@ -340,15 +340,20 @@ describe("plugin-resident skills", () => {
340
340
  expect(skill).toBeUndefined();
341
341
  });
342
342
 
343
- test("ignores plugin dirs whose package.json name mismatches the directory", () => {
344
- // Mirrors the loader's recognition gate: an un-adapted clone whose
345
- // package.json declares `caveman-installer` in a `caveman` dir is skipped.
343
+ test("surfaces plugin skills when package.json name differs from the directory", () => {
344
+ // A plugin is installed under its slug (marketplace name or GitHub path
345
+ // leaf), which routinely differs from its authored `package.json` name
346
+ // e.g. cognee installs to `cognee`/`vellum-assistant` while its package is
347
+ // named `cognee-memory`. The skill must still surface, attributed to the
348
+ // install directory (the identity every other surface uses).
346
349
  writePluginSkill("caveman", "caveman", "Caveman", "Terse mode", "body", {
347
350
  packageName: "caveman-installer",
348
351
  });
349
352
 
350
353
  const skill = loadSkillCatalog().find((s) => s.id === "caveman");
351
- expect(skill).toBeUndefined();
354
+ expect(skill).toBeDefined();
355
+ expect(skill!.source).toBe("plugin");
356
+ expect(skill!.owner).toEqual({ kind: "plugin", id: "caveman" });
352
357
  });
353
358
 
354
359
  test("warns when a plugin directory is missing package.json", () => {
@@ -42,7 +42,6 @@ let appStoreResult: unknown = null;
42
42
  mock.module("../apps/app-store.js", () => ({
43
43
  getApp: (_id: string) => appStoreResult,
44
44
  getAppsDir: () => "/tmp/apps",
45
- isMultifileApp: () => false,
46
45
  listApps: () => [],
47
46
  }));
48
47
 
@@ -321,11 +321,12 @@ describe("SttStreamSession", () => {
321
321
  const frames = ws.frames;
322
322
  expect(frames[0].type).toBe("error");
323
323
  expect(frames[0].category).toBe("provider-error");
324
- expect(
325
- (frames[0].message as string).includes(
326
- "Mock provider connection refused",
327
- ),
328
- ).toBe(true);
324
+ // Friendly copy names the provider; the raw upstream error stays in
325
+ // logs and must not leak to the client.
326
+ expect(frames[0].message as string).toContain("Deepgram");
327
+ expect(frames[0].message as string).not.toContain(
328
+ "Mock provider connection refused",
329
+ );
329
330
  expect(frames[1].type).toBe("closed");
330
331
  expect(session.isClosed).toBe(true);
331
332
  expect(ws.closed).toBe(true);