@vellumai/assistant 0.9.0 → 0.10.0-staging.2

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 (572) hide show
  1. package/ARCHITECTURE.md +18 -34
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +28 -22
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  12. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  13. package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
  14. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  15. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  16. package/openapi.yaml +976 -63
  17. package/package.json +2 -1
  18. package/scripts/sync-llm-catalog.ts +6 -15
  19. package/scripts/sync-web-search-catalog.ts +3 -11
  20. package/src/__tests__/access-request-card-view.test.ts +98 -0
  21. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  22. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
  23. package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
  24. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  25. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  26. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  27. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  28. package/src/__tests__/app-compiler.test.ts +15 -1
  29. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  32. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  33. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  34. package/src/__tests__/btw-routes.test.ts +6 -8
  35. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  36. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  37. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  38. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  39. package/src/__tests__/checker.test.ts +0 -3
  40. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  41. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  43. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  44. package/src/__tests__/config-schema.test.ts +35 -0
  45. package/src/__tests__/config-watcher.test.ts +0 -18
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  47. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  48. package/src/__tests__/contacts-tools.test.ts +29 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  52. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  53. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  54. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  55. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  56. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  57. package/src/__tests__/conversation-title-service.test.ts +62 -0
  58. package/src/__tests__/credential-broker.test.ts +449 -1
  59. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  60. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  61. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  62. package/src/__tests__/credential-routes.test.ts +360 -0
  63. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  64. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  65. package/src/__tests__/disk-usage.test.ts +65 -0
  66. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  67. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  68. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  69. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  71. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  72. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  73. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  74. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  75. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  76. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  77. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  78. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  79. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  80. package/src/__tests__/identity-routes.test.ts +0 -189
  81. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  82. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  83. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  84. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  85. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  86. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  87. package/src/__tests__/llm-resolver.test.ts +70 -24
  88. package/src/__tests__/llm-schema.test.ts +1 -0
  89. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  90. package/src/__tests__/mcp-health-check.test.ts +6 -7
  91. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  92. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  93. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  94. package/src/__tests__/path-policy.test.ts +34 -0
  95. package/src/__tests__/persona-resolver.test.ts +49 -14
  96. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  97. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  98. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  99. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  100. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  101. package/src/__tests__/reaction-persistence.test.ts +150 -29
  102. package/src/__tests__/registry.test.ts +2 -7
  103. package/src/__tests__/relay-server.test.ts +285 -0
  104. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  105. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  106. package/src/__tests__/schedule-routes.test.ts +0 -30
  107. package/src/__tests__/schedule-tools.test.ts +2 -18
  108. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  109. package/src/__tests__/skill-execute-input.test.ts +51 -1
  110. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  111. package/src/__tests__/skills.test.ts +51 -0
  112. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  113. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  114. package/src/__tests__/subagent-tools.test.ts +266 -0
  115. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  116. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  117. package/src/__tests__/title-generate-hook.test.ts +100 -3
  118. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  119. package/src/__tests__/token-manager.test.ts +519 -0
  120. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  121. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  122. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  123. package/src/__tests__/tool-executor.test.ts +0 -79
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  126. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  127. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  128. package/src/__tests__/twilio-routes.test.ts +81 -1
  129. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  130. package/src/__tests__/weak-open-model.test.ts +30 -0
  131. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  132. package/src/__tests__/workspace-greetings.test.ts +152 -0
  133. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  134. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  136. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  137. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  138. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  139. package/src/agent/loop.ts +49 -29
  140. package/src/api/README.md +6 -6
  141. package/src/api/events/tool-result.ts +6 -0
  142. package/src/api/events/workflow-completed.ts +53 -0
  143. package/src/api/events/workflow-leaf-finished.ts +38 -0
  144. package/src/api/events/workflow-leaf-started.ts +35 -0
  145. package/src/api/events/workflow-progress.ts +32 -0
  146. package/src/api/events/workflow-started.ts +31 -0
  147. package/src/api/index.ts +40 -0
  148. package/src/api/responses/conversation-message.ts +28 -4
  149. package/src/api/responses/home.ts +26 -4
  150. package/src/api/responses/workflow-journal.ts +53 -0
  151. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  152. package/src/approvals/guardian-decision-primitive.ts +26 -3
  153. package/src/approvals/guardian-request-resolvers.ts +183 -80
  154. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  155. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  156. package/src/calls/call-pointer-messages.ts +10 -4
  157. package/src/calls/channel-admission-reader.ts +104 -0
  158. package/src/calls/guardian-dispatch.ts +17 -45
  159. package/src/calls/media-stream-server.ts +84 -2
  160. package/src/calls/relay-access-wait.ts +1 -1
  161. package/src/calls/relay-server.ts +66 -0
  162. package/src/calls/relay-setup-router.ts +82 -1
  163. package/src/calls/twilio-routes.ts +17 -8
  164. package/src/calls/voice-session-bridge.ts +2 -2
  165. package/src/cli/commands/clients.ts +3 -0
  166. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  167. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  168. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  169. package/src/cli/commands/memory/index.ts +30 -0
  170. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  171. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  172. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  173. package/src/cli/commands/oauth/status.test.ts +36 -0
  174. package/src/cli/commands/oauth/status.ts +23 -3
  175. package/src/cli/commands/plugins.ts +197 -4
  176. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  177. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  178. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  179. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  180. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  181. package/src/cli/lib/diff-plugin.ts +346 -0
  182. package/src/cli/lib/inspect-plugin.ts +12 -1
  183. package/src/cli/lib/install-from-github.ts +105 -17
  184. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  185. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  186. package/src/cli/lib/plugin-surfaces.ts +104 -0
  187. package/src/cli/lib/upgrade-plugin.ts +298 -10
  188. package/src/cli/program.ts +2 -6
  189. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  190. package/src/config/assistant-feature-flags.ts +22 -7
  191. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  192. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  193. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  194. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  195. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  196. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  197. package/src/config/bundled-tool-registry.ts +2 -7
  198. package/src/config/call-site-defaults.ts +15 -2
  199. package/src/config/feature-flag-registry.json +46 -31
  200. package/src/config/inference-profile-validation.ts +26 -0
  201. package/src/config/llm-resolver.ts +3 -0
  202. package/src/config/loader.ts +4 -0
  203. package/src/config/memory-v3-gate.ts +11 -0
  204. package/src/config/profile-order.ts +28 -0
  205. package/src/config/schema.ts +8 -6
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  207. package/src/config/schemas/call-site-catalog.ts +7 -0
  208. package/src/config/schemas/channels.ts +11 -0
  209. package/src/config/schemas/elevenlabs.ts +0 -1
  210. package/src/config/schemas/llm.ts +31 -0
  211. package/src/config/schemas/memory-lifecycle.ts +3 -7
  212. package/src/config/schemas/memory-v3.ts +6 -0
  213. package/src/config/schemas/platform.ts +0 -8
  214. package/src/config/schemas/services.ts +18 -0
  215. package/src/config/seed-inference-profiles.ts +109 -44
  216. package/src/config/skills.ts +21 -0
  217. package/src/config/sync-gated-profiles.ts +220 -0
  218. package/src/contacts/contact-store.ts +89 -106
  219. package/src/contacts/contacts-write.ts +5 -22
  220. package/src/contacts/types.ts +0 -1
  221. package/src/context/compactor.ts +88 -54
  222. package/src/context/strip-injections.ts +58 -10
  223. package/src/context/token-estimator.ts +1 -1
  224. package/src/credential-execution/process-manager.ts +55 -14
  225. package/src/credential-execution/prompted-credential.ts +2 -3
  226. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  227. package/src/daemon/config-watcher.ts +0 -4
  228. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  229. package/src/daemon/conversation-agent-loop.ts +114 -22
  230. package/src/daemon/conversation-history.ts +1 -1
  231. package/src/daemon/conversation-lifecycle.ts +3 -5
  232. package/src/daemon/conversation-process.ts +13 -5
  233. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  234. package/src/daemon/conversation-slash.ts +2 -23
  235. package/src/daemon/conversation-surfaces.ts +26 -0
  236. package/src/daemon/conversation-tool-setup.ts +27 -14
  237. package/src/daemon/conversation.ts +66 -14
  238. package/src/daemon/disk-pressure-policy.ts +5 -3
  239. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  240. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  241. package/src/daemon/handlers/config-a2a.ts +0 -2
  242. package/src/daemon/handlers/config-channels.ts +15 -16
  243. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  244. package/src/daemon/handlers/conversations.ts +107 -0
  245. package/src/daemon/host-browser-proxy.ts +41 -0
  246. package/src/daemon/lifecycle.ts +55 -27
  247. package/src/daemon/message-provenance.ts +2 -0
  248. package/src/daemon/message-types/contacts.ts +0 -1
  249. package/src/daemon/message-types/conversations.ts +3 -3
  250. package/src/daemon/message-types/sync.ts +0 -1
  251. package/src/daemon/message-types/web-activity.ts +7 -1
  252. package/src/daemon/message-types/workflows.ts +83 -1
  253. package/src/daemon/orphan-reaper.test.ts +0 -19
  254. package/src/daemon/orphan-reaper.ts +2 -24
  255. package/src/daemon/server.ts +0 -10
  256. package/src/daemon/tool-setup-types.ts +4 -0
  257. package/src/daemon/trust-context.ts +1 -1
  258. package/src/events/tool-audit-listener.ts +2 -2
  259. package/src/home/feed-source-enrichment.test.ts +151 -0
  260. package/src/home/feed-source-enrichment.ts +176 -0
  261. package/src/home/relationship-state.ts +2 -4
  262. package/src/instrument.ts +18 -6
  263. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  264. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  265. package/src/ipc/assistant-server.ts +37 -4
  266. package/src/ipc/gateway-flag-listener.ts +18 -2
  267. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  268. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  269. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  270. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  271. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  272. package/src/memory/auth-fallback-events-store.ts +2 -2
  273. package/src/memory/auto-analysis-enqueue.ts +3 -5
  274. package/src/memory/bookmark-crud.ts +1 -2
  275. package/src/memory/canonical-guardian-store.ts +39 -1
  276. package/src/memory/conversation-crud.ts +9 -4
  277. package/src/memory/conversation-key-store.ts +17 -2
  278. package/src/memory/conversation-title-service.ts +64 -7
  279. package/src/memory/db-init.ts +17 -17
  280. package/src/memory/embedding-backend.ts +38 -1
  281. package/src/memory/embedding-billing-breaker.ts +96 -0
  282. package/src/memory/jobs-store.ts +25 -13
  283. package/src/memory/jobs-worker.ts +54 -1
  284. package/src/memory/lifecycle-events-store.ts +2 -2
  285. package/src/memory/memory-retrospective-constants.ts +4 -4
  286. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  287. package/src/memory/memory-retrospective-job.ts +28 -227
  288. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  289. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  290. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  291. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  292. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  293. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  294. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  295. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  296. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  297. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  298. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  299. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  300. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  301. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  302. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  303. package/src/memory/migrations/index.ts +6 -0
  304. package/src/memory/migrations/run-migrations.ts +41 -0
  305. package/src/memory/migrations/validate-migration-state.ts +1 -1
  306. package/src/memory/onboarding-events-store.ts +3 -3
  307. package/src/memory/schema/contacts.ts +0 -5
  308. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  309. package/src/memory/skill-loaded-events-store.ts +2 -2
  310. package/src/memory/tool-executed-events-store.test.ts +7 -7
  311. package/src/memory/turn-trace-store.test.ts +736 -0
  312. package/src/memory/turn-trace-store.ts +364 -0
  313. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  314. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  315. package/src/memory/v2/consolidation-job.ts +2 -2
  316. package/src/memory/v2/skill-content.ts +25 -7
  317. package/src/memory/v2/skill-store.ts +7 -1
  318. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  319. package/src/memory/v3-eval/eval-packets.ts +546 -0
  320. package/src/messaging/providers/slack/adapter.ts +1 -1
  321. package/src/messaging/providers/slack/api.ts +31 -0
  322. package/src/messaging/providers/slack/send.test.ts +114 -2
  323. package/src/messaging/providers/slack/send.ts +30 -7
  324. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  325. package/src/messaging/providers/slack/withdraw.ts +161 -0
  326. package/src/notifications/AGENTS.md +2 -0
  327. package/src/notifications/access-request-copy.ts +72 -59
  328. package/src/notifications/adapters/shared.ts +29 -0
  329. package/src/notifications/adapters/slack.ts +58 -103
  330. package/src/notifications/adapters/telegram.ts +2 -20
  331. package/src/notifications/approval-card-data.ts +333 -0
  332. package/src/notifications/broadcaster.ts +16 -3
  333. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  334. package/src/notifications/copy-composer.ts +3 -3
  335. package/src/notifications/decision-engine.ts +4 -2
  336. package/src/notifications/destination-resolver.ts +4 -6
  337. package/src/notifications/guardian-question-mode.ts +10 -0
  338. package/src/notifications/home-feed-side-effect.ts +7 -16
  339. package/src/notifications/notification-utils.ts +19 -20
  340. package/src/notifications/signal.ts +79 -43
  341. package/src/notifications/types.ts +98 -121
  342. package/src/oauth/AGENTS.md +5 -24
  343. package/src/permissions/checker.test.ts +51 -0
  344. package/src/permissions/checker.ts +185 -26
  345. package/src/permissions/ipc-risk-types.ts +24 -0
  346. package/src/permissions/question-prompter.test.ts +27 -0
  347. package/src/permissions/question-prompter.ts +4 -0
  348. package/src/platform/client.test.ts +119 -0
  349. package/src/platform/client.ts +66 -0
  350. package/src/platform/consent-cache.test.ts +267 -0
  351. package/src/platform/consent-cache.ts +174 -0
  352. package/src/plugin-api/constants.ts +1 -1
  353. package/src/plugin-api/index.ts +33 -1
  354. package/src/plugin-api/model-profiles.ts +33 -0
  355. package/src/plugin-api/types.ts +50 -2
  356. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  357. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  358. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  359. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  360. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  361. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  362. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  363. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  364. package/src/plugins/defaults/advisor/config.ts +21 -0
  365. package/src/plugins/defaults/advisor/consult.ts +93 -0
  366. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  367. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  368. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  369. package/src/plugins/defaults/advisor/package.json +14 -0
  370. package/src/plugins/defaults/advisor/steering.ts +67 -0
  371. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  372. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  373. package/src/plugins/defaults/index.ts +60 -0
  374. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  375. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  376. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  377. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  378. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  379. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  380. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  381. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  382. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  384. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  385. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  386. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  387. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  388. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  389. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  390. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  391. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  392. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  393. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  394. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  395. package/src/prompts/persona-resolver.ts +14 -4
  396. package/src/prompts/templates/system-sections.ts +7 -2
  397. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  398. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  399. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  400. package/src/providers/atlascloud/client.ts +85 -0
  401. package/src/providers/fetch-provider-catalog.ts +85 -0
  402. package/src/providers/inference/adapter-factory.ts +3 -0
  403. package/src/providers/model-catalog.ts +58 -0
  404. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  405. package/src/providers/openai/chat-completions-provider.ts +7 -0
  406. package/src/providers/openai/responses-provider.ts +10 -0
  407. package/src/providers/provider-send-message.ts +11 -3
  408. package/src/providers/retry.ts +53 -12
  409. package/src/providers/search-provider-catalog.ts +10 -0
  410. package/src/providers/weak-open-model.ts +22 -0
  411. package/src/runtime/AGENTS.md +0 -1
  412. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  413. package/src/runtime/__tests__/client-health.test.ts +44 -0
  414. package/src/runtime/access-request-helper.ts +21 -53
  415. package/src/runtime/actor-trust-resolver.ts +59 -63
  416. package/src/runtime/agent-wake.ts +52 -0
  417. package/src/runtime/assistant-event-hub.ts +18 -4
  418. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  419. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  420. package/src/runtime/btw-sidechain.ts +3 -6
  421. package/src/runtime/capabilities.test.ts +120 -0
  422. package/src/runtime/capabilities.ts +197 -0
  423. package/src/runtime/channel-approval-types.ts +22 -45
  424. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  425. package/src/runtime/channel-retry-sweep.ts +1 -0
  426. package/src/runtime/channel-verification-service.ts +3 -3
  427. package/src/runtime/client-health.ts +26 -0
  428. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  429. package/src/runtime/effective-capabilities.test.ts +128 -0
  430. package/src/runtime/effective-capabilities.ts +84 -0
  431. package/src/runtime/guardian-reply-router.ts +106 -21
  432. package/src/runtime/invite-redemption-service.ts +9 -25
  433. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  434. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  435. package/src/runtime/pending-interactions.ts +15 -0
  436. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  437. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  438. package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
  439. package/src/runtime/routes/app-routes.ts +1 -1
  440. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  441. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  442. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  443. package/src/runtime/routes/btw-routes.ts +1 -27
  444. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  445. package/src/runtime/routes/client-routes.ts +10 -0
  446. package/src/runtime/routes/contact-routes.ts +31 -8
  447. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  448. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  449. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  450. package/src/runtime/routes/conversation-routes.ts +39 -14
  451. package/src/runtime/routes/credential-routes.ts +40 -16
  452. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  453. package/src/runtime/routes/events-routes.ts +1 -3
  454. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  455. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  456. package/src/runtime/routes/home-feed-routes.ts +8 -3
  457. package/src/runtime/routes/identity-routes.ts +1 -296
  458. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  459. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  460. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  461. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  462. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  463. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  464. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  465. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  466. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  467. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  468. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  469. package/src/runtime/routes/index.ts +2 -0
  470. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  471. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  472. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  473. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  474. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  475. package/src/runtime/routes/notification-routes.ts +122 -133
  476. package/src/runtime/routes/platform-routes.ts +2 -2
  477. package/src/runtime/routes/plugins-routes.ts +202 -3
  478. package/src/runtime/routes/schedule-routes.ts +0 -22
  479. package/src/runtime/routes/secret-routes.ts +10 -0
  480. package/src/runtime/routes/surface-action-routes.ts +2 -1
  481. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  482. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  483. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  484. package/src/runtime/routes/workflow-routes.ts +131 -29
  485. package/src/runtime/routes/workspace-greetings.ts +55 -0
  486. package/src/runtime/sync/resource-sync-events.ts +1 -11
  487. package/src/runtime/tool-grant-request-helper.ts +18 -16
  488. package/src/runtime/trust-context-resolver.ts +8 -5
  489. package/src/schedule/inference-profile.ts +2 -14
  490. package/src/schedule/schedule-store.ts +1 -1
  491. package/src/schedule/scheduler-types.ts +5 -1
  492. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  493. package/src/security/secret-patterns.ts +3 -0
  494. package/src/subagent/manager.ts +17 -4
  495. package/src/subagent/types.ts +6 -0
  496. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  497. package/src/telemetry/trace-collection-policy.ts +30 -0
  498. package/src/telemetry/types.ts +89 -0
  499. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  500. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  501. package/src/tools/AGENTS.md +3 -3
  502. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  503. package/src/tools/browser/browser-execution.ts +30 -19
  504. package/src/tools/document/document-tool.ts +2 -3
  505. package/src/tools/executor.ts +5 -3
  506. package/src/tools/host-terminal/host-shell.ts +5 -4
  507. package/src/tools/memory/register.ts +2 -2
  508. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  509. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  510. package/src/tools/network/web-fetch.ts +372 -1
  511. package/src/tools/network/web-search-error.ts +1 -1
  512. package/src/tools/network/web-search.ts +213 -10
  513. package/src/tools/permission-checker.ts +4 -3
  514. package/src/tools/registry.ts +20 -0
  515. package/src/tools/schedule/create.ts +7 -12
  516. package/src/tools/schedule/update.ts +4 -11
  517. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  518. package/src/tools/side-effects.ts +2 -17
  519. package/src/tools/skills/execute.ts +33 -0
  520. package/src/tools/subagent/spawn.ts +61 -12
  521. package/src/tools/terminal/shell.ts +10 -4
  522. package/src/tools/tool-approval-handler.ts +18 -13
  523. package/src/tools/tool-manifest.ts +0 -2
  524. package/src/tools/types.ts +9 -0
  525. package/src/tools/ui-surface/definitions.ts +64 -3
  526. package/src/tools/verification-control-plane-policy.ts +3 -1
  527. package/src/tools/workflows/run-workflow.test.ts +8 -18
  528. package/src/tools/workflows/run-workflow.ts +1 -0
  529. package/src/util/disk-usage.ts +78 -23
  530. package/src/util/platform.ts +10 -3
  531. package/src/watcher/telemetry.ts +2 -2
  532. package/src/workflows/capabilities.ts +2 -3
  533. package/src/workflows/engine.test.ts +175 -1
  534. package/src/workflows/engine.ts +82 -0
  535. package/src/workflows/journal-store.test.ts +70 -0
  536. package/src/workflows/journal-store.ts +18 -3
  537. package/src/workflows/run-manager.test.ts +171 -28
  538. package/src/workflows/run-manager.ts +66 -24
  539. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  540. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  541. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  542. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  543. package/src/workspace/migrations/registry.ts +8 -0
  544. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  545. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  546. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  547. package/src/__tests__/credential-vault.test.ts +0 -1706
  548. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  549. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  550. package/src/cli/commands/__tests__/task.test.ts +0 -914
  551. package/src/cli/commands/task.ts +0 -771
  552. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  553. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  554. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  555. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  556. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  557. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  558. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  559. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  560. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  561. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  562. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  563. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  564. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  565. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  566. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  567. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  568. package/src/memory/preloaded-apps.ts +0 -116
  569. package/src/notifications/tool-approval-copy.ts +0 -142
  570. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  571. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  572. package/src/tools/credentials/vault.ts +0 -712
@@ -0,0 +1,341 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { describe, expect, test } from "bun:test";
3
+
4
+ import { drizzle } from "drizzle-orm/bun-sqlite";
5
+
6
+ import { getSqliteFrom } from "../../db-connection.js";
7
+ import * as schema from "../../schema.js";
8
+ import { migrateContactChannelsRenormalizeAddresses } from "../291-contact-channels-renormalize-addresses.js";
9
+
10
+ interface ChannelRow {
11
+ id: string;
12
+ contact_id: string;
13
+ type: string;
14
+ address: string;
15
+ external_user_id: string | null;
16
+ status: string;
17
+ }
18
+
19
+ function createTestDb() {
20
+ const sqlite = new Database(":memory:");
21
+ sqlite.exec("PRAGMA journal_mode=WAL");
22
+ sqlite.exec("PRAGMA foreign_keys = ON");
23
+ return drizzle(sqlite, { schema });
24
+ }
25
+
26
+ function bootstrap(db: ReturnType<typeof createTestDb>): void {
27
+ const raw = getSqliteFrom(db);
28
+ raw.exec(/*sql*/ `
29
+ CREATE TABLE contacts (
30
+ id TEXT PRIMARY KEY,
31
+ display_name TEXT NOT NULL,
32
+ created_at INTEGER NOT NULL,
33
+ updated_at INTEGER NOT NULL,
34
+ role TEXT NOT NULL DEFAULT 'contact',
35
+ contact_type TEXT NOT NULL DEFAULT 'human'
36
+ )
37
+ `);
38
+ raw.exec(/*sql*/ `
39
+ CREATE TABLE contact_channels (
40
+ id TEXT PRIMARY KEY,
41
+ contact_id TEXT NOT NULL REFERENCES contacts(id) ON DELETE CASCADE,
42
+ type TEXT NOT NULL,
43
+ address TEXT NOT NULL,
44
+ is_primary INTEGER NOT NULL DEFAULT 0,
45
+ external_user_id TEXT,
46
+ external_chat_id TEXT,
47
+ status TEXT NOT NULL DEFAULT 'unverified',
48
+ policy TEXT NOT NULL DEFAULT 'allow',
49
+ interaction_count INTEGER NOT NULL DEFAULT 0,
50
+ updated_at INTEGER,
51
+ created_at INTEGER NOT NULL
52
+ )
53
+ `);
54
+ raw.exec(/*sql*/ `
55
+ CREATE UNIQUE INDEX idx_contact_channels_type_address
56
+ ON contact_channels(type, address)
57
+ `);
58
+ }
59
+
60
+ function insertContact(raw: Database, id: string): void {
61
+ const now = Date.now();
62
+ raw
63
+ .query(
64
+ `INSERT INTO contacts (id, display_name, created_at, updated_at) VALUES (?, ?, ?, ?)`,
65
+ )
66
+ .run(id, `Contact ${id}`, now, now);
67
+ }
68
+
69
+ function insertChannel(
70
+ raw: Database,
71
+ opts: {
72
+ id: string;
73
+ contactId: string;
74
+ type: string;
75
+ address: string;
76
+ externalUserId: string | null;
77
+ status: string;
78
+ },
79
+ ): void {
80
+ const now = Date.now();
81
+ raw
82
+ .query(
83
+ `INSERT INTO contact_channels (id, contact_id, type, address, external_user_id, status, updated_at, created_at)
84
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
85
+ )
86
+ .run(
87
+ opts.id,
88
+ opts.contactId,
89
+ opts.type,
90
+ opts.address,
91
+ opts.externalUserId,
92
+ opts.status,
93
+ now,
94
+ now,
95
+ );
96
+ }
97
+
98
+ describe("migration 291 — renormalize addresses", () => {
99
+ test("restores Slack address casing from external_user_id", () => {
100
+ const db = createTestDb();
101
+ bootstrap(db);
102
+ const raw = getSqliteFrom(db);
103
+
104
+ insertContact(raw, "c1");
105
+ insertChannel(raw, {
106
+ id: "ch1",
107
+ contactId: "c1",
108
+ type: "slack",
109
+ address: "u12345abc",
110
+ externalUserId: "U12345ABC",
111
+ status: "active",
112
+ });
113
+
114
+ migrateContactChannelsRenormalizeAddresses(db);
115
+
116
+ const rows = raw
117
+ .prepare("SELECT * FROM contact_channels")
118
+ .all() as ChannelRow[];
119
+ expect(rows).toHaveLength(1);
120
+ expect(rows[0].address).toBe("U12345ABC");
121
+ });
122
+
123
+ test("preserves lowercase email addresses (does not overwrite with mixed-case external_user_id)", () => {
124
+ const db = createTestDb();
125
+ bootstrap(db);
126
+ const raw = getSqliteFrom(db);
127
+
128
+ insertContact(raw, "c1");
129
+ insertChannel(raw, {
130
+ id: "ch1",
131
+ contactId: "c1",
132
+ type: "email",
133
+ address: "user@example.com",
134
+ // generic-examples:ignore-next-line — reason: testing mixed-case email normalization
135
+ externalUserId: "User@Example.com",
136
+ status: "active",
137
+ });
138
+
139
+ migrateContactChannelsRenormalizeAddresses(db);
140
+
141
+ const rows = raw
142
+ .prepare("SELECT * FROM contact_channels")
143
+ .all() as ChannelRow[];
144
+ expect(rows).toHaveLength(1);
145
+ expect(rows[0].address).toBe("user@example.com");
146
+ });
147
+
148
+ test("lowercases email address if it was stored incorrectly", () => {
149
+ const db = createTestDb();
150
+ bootstrap(db);
151
+ const raw = getSqliteFrom(db);
152
+
153
+ insertContact(raw, "c1");
154
+ insertChannel(raw, {
155
+ id: "ch1",
156
+ contactId: "c1",
157
+ type: "email",
158
+ // generic-examples:ignore-next-line — reason: testing mixed-case email stored incorrectly
159
+ address: "User@Example.com",
160
+ // generic-examples:ignore-next-line — reason: testing mixed-case email stored incorrectly
161
+ externalUserId: "User@Example.com",
162
+ status: "active",
163
+ });
164
+
165
+ migrateContactChannelsRenormalizeAddresses(db);
166
+
167
+ const rows = raw
168
+ .prepare("SELECT * FROM contact_channels")
169
+ .all() as ChannelRow[];
170
+ expect(rows).toHaveLength(1);
171
+ expect(rows[0].address).toBe("user@example.com");
172
+ });
173
+
174
+ test("removes cross-column blocker before normalization", () => {
175
+ const db = createTestDb();
176
+ bootstrap(db);
177
+ const raw = getSqliteFrom(db);
178
+
179
+ insertContact(raw, "c1");
180
+ insertContact(raw, "c2");
181
+ // Blocker: NULL external_user_id, address matches normalizer's external_user_id
182
+ insertChannel(raw, {
183
+ id: "blocker",
184
+ contactId: "c1",
185
+ type: "slack",
186
+ address: "u12345abc",
187
+ externalUserId: null,
188
+ status: "unverified",
189
+ });
190
+ // Normalizer: has external_user_id that will become its address
191
+ insertChannel(raw, {
192
+ id: "normalizer",
193
+ contactId: "c2",
194
+ type: "slack",
195
+ address: "old-address",
196
+ externalUserId: "U12345ABC",
197
+ status: "active",
198
+ });
199
+
200
+ migrateContactChannelsRenormalizeAddresses(db);
201
+
202
+ const rows = raw
203
+ .prepare("SELECT * FROM contact_channels ORDER BY id")
204
+ .all() as ChannelRow[];
205
+ expect(rows).toHaveLength(1);
206
+ expect(rows[0].id).toBe("normalizer");
207
+ expect(rows[0].address).toBe("U12345ABC");
208
+ });
209
+
210
+ test("idempotent — safe to run twice", () => {
211
+ const db = createTestDb();
212
+ bootstrap(db);
213
+ const raw = getSqliteFrom(db);
214
+
215
+ insertContact(raw, "c1");
216
+ insertChannel(raw, {
217
+ id: "ch1",
218
+ contactId: "c1",
219
+ type: "slack",
220
+ address: "u12345abc",
221
+ externalUserId: "U12345ABC",
222
+ status: "active",
223
+ });
224
+
225
+ migrateContactChannelsRenormalizeAddresses(db);
226
+ migrateContactChannelsRenormalizeAddresses(db);
227
+
228
+ const rows = raw
229
+ .prepare("SELECT * FROM contact_channels")
230
+ .all() as ChannelRow[];
231
+ expect(rows).toHaveLength(1);
232
+ expect(rows[0].address).toBe("U12345ABC");
233
+ });
234
+
235
+ test("does not overwrite canonical E.164 phone address with raw external_user_id", () => {
236
+ const db = createTestDb();
237
+ bootstrap(db);
238
+ const raw = getSqliteFrom(db);
239
+
240
+ insertContact(raw, "c1");
241
+ // Phone channel whose address was canonicalized to E.164 (+1 prefix)
242
+ // but external_user_id still holds the raw 10-digit number.
243
+ insertChannel(raw, {
244
+ id: "ch1",
245
+ contactId: "c1",
246
+ type: "phone",
247
+ // generic-examples:ignore-next-line — reason: testing E.164 canonicalization preservation
248
+ address: "+15550101234",
249
+ // generic-examples:ignore-next-line — reason: testing raw 10-digit phone before E.164 normalization
250
+ externalUserId: "5550101234",
251
+ status: "active",
252
+ });
253
+
254
+ migrateContactChannelsRenormalizeAddresses(db);
255
+
256
+ const rows = raw
257
+ .prepare("SELECT * FROM contact_channels")
258
+ .all() as ChannelRow[];
259
+ expect(rows).toHaveLength(1);
260
+ // generic-examples:ignore-next-line — reason: verifying E.164 address preserved
261
+ expect(rows[0].address).toBe("+15550101234");
262
+ });
263
+
264
+ test("does not overwrite canonical WhatsApp address with raw external_user_id", () => {
265
+ const db = createTestDb();
266
+ bootstrap(db);
267
+ const raw = getSqliteFrom(db);
268
+
269
+ insertContact(raw, "c1");
270
+ insertChannel(raw, {
271
+ id: "ch1",
272
+ contactId: "c1",
273
+ type: "whatsapp",
274
+ address: "+447911123456",
275
+ externalUserId: "447911123456",
276
+ status: "active",
277
+ });
278
+
279
+ migrateContactChannelsRenormalizeAddresses(db);
280
+
281
+ const rows = raw
282
+ .prepare("SELECT * FROM contact_channels")
283
+ .all() as ChannelRow[];
284
+ expect(rows).toHaveLength(1);
285
+ expect(rows[0].address).toBe("+447911123456");
286
+ });
287
+
288
+ test("no-op when address already matches external_user_id", () => {
289
+ const db = createTestDb();
290
+ bootstrap(db);
291
+ const raw = getSqliteFrom(db);
292
+
293
+ insertContact(raw, "c1");
294
+ insertChannel(raw, {
295
+ id: "ch1",
296
+ contactId: "c1",
297
+ type: "slack",
298
+ address: "U12345ABC",
299
+ externalUserId: "U12345ABC",
300
+ status: "active",
301
+ });
302
+
303
+ migrateContactChannelsRenormalizeAddresses(db);
304
+
305
+ const rows = raw
306
+ .prepare("SELECT * FROM contact_channels")
307
+ .all() as ChannelRow[];
308
+ expect(rows).toHaveLength(1);
309
+ expect(rows[0].address).toBe("U12345ABC");
310
+ });
311
+
312
+ test("no-op when external_user_id column is absent (re-run after migration 294)", () => {
313
+ const db = createTestDb();
314
+ bootstrap(db);
315
+ const raw = getSqliteFrom(db);
316
+
317
+ insertContact(raw, "c1");
318
+ insertChannel(raw, {
319
+ id: "ch1",
320
+ contactId: "c1",
321
+ type: "slack",
322
+ address: "u12345abc",
323
+ externalUserId: "U12345ABC",
324
+ status: "active",
325
+ });
326
+
327
+ // Simulate a later startup where migration 294 has already dropped the
328
+ // index and column. Migration steps re-run on every startup, so this must
329
+ // tolerate the dropped column rather than throwing "no such column".
330
+ raw.run("DROP INDEX IF EXISTS idx_contact_channels_type_ext_user");
331
+ raw.run("ALTER TABLE contact_channels DROP COLUMN external_user_id");
332
+
333
+ expect(() => migrateContactChannelsRenormalizeAddresses(db)).not.toThrow();
334
+
335
+ const rows = raw
336
+ .prepare("SELECT id, address FROM contact_channels")
337
+ .all() as { id: string; address: string }[];
338
+ expect(rows).toHaveLength(1);
339
+ expect(rows[0].address).toBe("u12345abc");
340
+ });
341
+ });
@@ -0,0 +1,52 @@
1
+ import { Database } from "bun:sqlite";
2
+ import { describe, expect, test } from "bun:test";
3
+
4
+ import { drizzle } from "drizzle-orm/bun-sqlite";
5
+
6
+ import * as schema from "../../schema.js";
7
+ import { type MigrationStep, runMigrationSteps } from "../run-migrations.js";
8
+
9
+ function createTestDb() {
10
+ const sqlite = new Database(":memory:");
11
+ sqlite.exec("PRAGMA journal_mode=WAL");
12
+ sqlite.exec("PRAGMA foreign_keys = ON");
13
+ return drizzle(sqlite, { schema });
14
+ }
15
+
16
+ describe("runMigrationSteps — checkpointing", () => {
17
+ // Skipped: documents the post-checkpointing contract and fails against the
18
+ // current behavior, which re-runs every forward step on every boot. Promote
19
+ // to `test` when true checkpointing of initial migrations lands.
20
+ test.skip("does not re-execute a step that was already applied on a prior run", () => {
21
+ /**
22
+ * Booting an already-migrated database must not re-run forward migration
23
+ * steps. With true checkpointing each step's body executes at most once
24
+ * across boots; without it every step re-runs on every startup — the
25
+ * ~200-step re-probe that floors daemon startup at ~30s.
26
+ */
27
+
28
+ // GIVEN three dummy migration steps that count how often their body runs
29
+ const calls = { a: 0, b: 0, c: 0 };
30
+ const steps: MigrationStep[] = [
31
+ function dummyMigrationA() {
32
+ calls.a++;
33
+ },
34
+ function dummyMigrationB() {
35
+ calls.b++;
36
+ },
37
+ function dummyMigrationC() {
38
+ calls.c++;
39
+ },
40
+ ];
41
+
42
+ // AND a database that has already been migrated once
43
+ const db = createTestDb();
44
+ runMigrationSteps(db, steps);
45
+
46
+ // WHEN the same steps run again against the already-migrated database
47
+ runMigrationSteps(db, steps);
48
+
49
+ // THEN no step body executes a second time
50
+ expect(calls).toEqual({ a: 1, b: 1, c: 1 });
51
+ });
52
+ });
@@ -282,6 +282,12 @@ export { migrateConversationOriginChannelIndex } from "./287-conversation-origin
282
282
  export { migrateBackfillOriginChannelFromBindings } from "./288-backfill-origin-channel-from-bindings.js";
283
283
  export { migrateContactChannelsUniqueExtUser } from "./289-contact-channels-unique-ext-user.js";
284
284
  export { migrateScheduleCapabilities } from "./290-schedule-capabilities.js";
285
+ export { migrateContactChannelsRenormalizeAddresses } from "./291-contact-channels-renormalize-addresses.js";
286
+ export { migrateScheduleDefaultNoReuseConversation } from "./292-schedule-default-no-reuse-conversation.js";
287
+ export { migrateWorkflowJournalLeafTokens } from "./293-workflow-journal-leaf-tokens.js";
288
+ export { migrateDropExternalUserId } from "./294-drop-external-user-id.js";
289
+ export { dropApprovalPromptTsTrackerTable } from "./295-drop-approval-prompt-ts-tracker.js";
290
+ export { migrateRewriteBalancedEconomyProfilePins } from "./296-rewrite-balanced-economy-profile-pins.js";
285
291
  export {
286
292
  MIGRATION_REGISTRY,
287
293
  type MigrationRegistryEntry,
@@ -0,0 +1,41 @@
1
+ import { getLogger } from "../../util/logger.js";
2
+ import type { DrizzleDb } from "../db-connection.js";
3
+
4
+ const log = getLogger("db-init");
5
+
6
+ /** A single forward migration step, identified for logging by its `.name`. */
7
+ export type MigrationStep = (database: DrizzleDb) => void;
8
+
9
+ export interface MigrationRunResult {
10
+ /** Steps whose body threw. */
11
+ failed: string[];
12
+ }
13
+
14
+ /**
15
+ * Run the ordered list of forward migration steps against the database.
16
+ *
17
+ * Individual step failures are caught and logged so one broken migration does
18
+ * not prevent independent later ones from succeeding.
19
+ */
20
+ export function runMigrationSteps(
21
+ database: DrizzleDb,
22
+ steps: MigrationStep[],
23
+ ): MigrationRunResult {
24
+ const failed: string[] = [];
25
+
26
+ for (const step of steps) {
27
+ try {
28
+ log.debug({ migration: step.name }, `Starting migration: ${step.name}`);
29
+ step(database);
30
+ log.debug({ migration: step.name }, `Migration succeeded: ${step.name}`);
31
+ } catch (err) {
32
+ failed.push(step.name);
33
+ log.error(
34
+ { err, migration: step.name },
35
+ `Migration failed: ${step.name}`,
36
+ );
37
+ }
38
+ }
39
+
40
+ return { failed };
41
+ }
@@ -220,7 +220,7 @@ export function validateMigrationState(
220
220
  // registry entry — these are from a newer version of the daemon.
221
221
  //
222
222
  // The memory_checkpoints table is a general-purpose key-value store also
223
- // used by non-migration subsystems (e.g., "identity:intro:text",
223
+ // used by non-migration subsystems (e.g., "empty_state:greeting:text",
224
224
  // "conversation_starters:item_count_at_last_gen"). Filter to only keys
225
225
  // that follow migration naming conventions before comparing against the
226
226
  // registry to avoid false-positive warnings.
@@ -1,7 +1,7 @@
1
1
  import { and, asc, eq, gt, or } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
- import { getConfig } from "../config/loader.js";
4
+ import { getCachedShareAnalytics } from "../platform/consent-cache.js";
5
5
  import {
6
6
  ACTIVATION_AB_VARIANT,
7
7
  ACTIVATION_FUNNEL_VERSION,
@@ -58,7 +58,7 @@ function insertOnboardingEvent(event: OnboardingEvent): OnboardingEvent {
58
58
  export function recordOnboardingEvent(
59
59
  params: RecordOnboardingEventParams,
60
60
  ): OnboardingEvent | null {
61
- if (!getConfig().collectUsageData) return null;
61
+ if (!getCachedShareAnalytics()) return null;
62
62
  return insertOnboardingEvent({
63
63
  id: uuid(),
64
64
  createdAt: Date.now(),
@@ -93,7 +93,7 @@ export function recordActivationEvent(params: {
93
93
  userId?: string | null;
94
94
  abVariant?: string;
95
95
  }): OnboardingEvent | null {
96
- if (!getConfig().collectUsageData) return null;
96
+ if (!getCachedShareAnalytics()) return null;
97
97
  const createdAt = Date.now();
98
98
  return insertOnboardingEvent({
99
99
  id: uuid(),
@@ -26,7 +26,6 @@ export const contactChannels = sqliteTable(
26
26
  isPrimary: integer("is_primary", { mode: "boolean" })
27
27
  .notNull()
28
28
  .default(false),
29
- externalUserId: text("external_user_id"), // channel-native user ID (e.g., Telegram numeric ID, E.164 phone)
30
29
  externalChatId: text("external_chat_id"), // delivery/notification routing address (e.g., Telegram chat ID)
31
30
  status: text("status").notNull().default("unverified"), // 'active' | 'pending' | 'revoked' | 'blocked' | 'unverified'
32
31
  policy: text("policy").notNull().default("allow"), // 'allow' | 'deny' | 'escalate'
@@ -42,10 +41,6 @@ export const contactChannels = sqliteTable(
42
41
  createdAt: integer("created_at").notNull(),
43
42
  },
44
43
  (table) => [
45
- index("idx_contact_channels_type_ext_user").on(
46
- table.type,
47
- table.externalUserId,
48
- ),
49
44
  index("idx_contact_channels_type_ext_chat").on(
50
45
  table.type,
51
46
  table.externalChatId,
@@ -8,18 +8,10 @@ mock.module("../util/logger.js", () => ({
8
8
  }),
9
9
  }));
10
10
 
11
- let collectUsageData = true;
12
-
13
- mock.module("../config/loader.js", () => ({
14
- getConfig: () => ({
15
- ui: {},
16
- model: "test",
17
- provider: "test",
18
- memory: { enabled: false },
19
- rateLimit: { maxRequestsPerMinute: 0 },
20
- secretDetection: { enabled: false },
21
- collectUsageData,
22
- }),
11
+ let shareAnalytics = true;
12
+
13
+ mock.module("../platform/consent-cache.js", () => ({
14
+ getCachedShareAnalytics: () => shareAnalytics,
23
15
  }));
24
16
 
25
17
  import { getDb } from "./db-connection.js";
@@ -42,12 +34,12 @@ function insertEvent(
42
34
 
43
35
  describe("skill-loaded-events-store", () => {
44
36
  beforeEach(() => {
45
- collectUsageData = true;
37
+ shareAnalytics = true;
46
38
  getDb().delete(skillLoadedEvents).run();
47
39
  });
48
40
 
49
- test("honors the collectUsageData opt-out (records nothing)", () => {
50
- collectUsageData = false;
41
+ test("honors the share_analytics opt-out (records nothing)", () => {
42
+ shareAnalytics = false;
51
43
  recordSkillLoadedEvent({ skillName: "web-research" });
52
44
  expect(queryUnreportedSkillLoadedEvents(0, undefined, 10)).toHaveLength(0);
53
45
  });
@@ -1,7 +1,7 @@
1
1
  import { and, asc, eq, gt, or } from "drizzle-orm";
2
2
  import { v4 as uuid } from "uuid";
3
3
 
4
- import { getConfig } from "../config/loader.js";
4
+ import { getCachedShareAnalytics } from "../platform/consent-cache.js";
5
5
  import type { UsageAttributionColumns } from "../usage/attribution.js";
6
6
  import { getDb } from "./db-connection.js";
7
7
  import { skillLoadedEvents } from "./schema.js";
@@ -38,7 +38,7 @@ export interface SkillLoadedEvent {
38
38
  * opt-out, matching the rest of telemetry).
39
39
  */
40
40
  export function recordSkillLoadedEvent(record: SkillLoadedEventRecord): void {
41
- if (!getConfig().collectUsageData) return;
41
+ if (!getCachedShareAnalytics()) return;
42
42
  const db = getDb();
43
43
  db.insert(skillLoadedEvents)
44
44
  .values({
@@ -8,12 +8,12 @@ mock.module("../util/logger.js", () => ({
8
8
  }),
9
9
  }));
10
10
 
11
- // Toggle for the collectUsageData opt-out gate the audit listener consults
11
+ // Toggle for the share_analytics opt-out gate the audit listener consults
12
12
  // when populating the telemetry columns.
13
- let collectUsageData = true;
13
+ let shareAnalytics = true;
14
14
 
15
- mock.module("../config/loader.js", () => ({
16
- getConfig: () => ({ collectUsageData }),
15
+ mock.module("../platform/consent-cache.js", () => ({
16
+ getCachedShareAnalytics: () => shareAnalytics,
17
17
  }));
18
18
 
19
19
  import {
@@ -43,7 +43,7 @@ function insertInvocation(
43
43
 
44
44
  describe("tool-executed-events-store", () => {
45
45
  beforeEach(() => {
46
- collectUsageData = true;
46
+ shareAnalytics = true;
47
47
  getDb().delete(toolInvocations).run();
48
48
  });
49
49
 
@@ -136,10 +136,10 @@ describe("tool-executed-events-store", () => {
136
136
 
137
137
  listener(executedEvent("t-opted-in-before"));
138
138
 
139
- collectUsageData = false;
139
+ shareAnalytics = false;
140
140
  listener(executedEvent("t-opted-out"));
141
141
 
142
- collectUsageData = true;
142
+ shareAnalytics = true;
143
143
  listener(executedEvent("t-opted-in-after"));
144
144
 
145
145
  // Mid-session opt-out flip: only rows recorded while opted in project.