@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
@@ -15,7 +15,6 @@ import {
15
15
  type FeedItemCategory,
16
16
  type FeedItemDetailPanelKind,
17
17
  feedItemSchema,
18
- type FeedItemUrgency,
19
18
  } from "../home/feed-types.js";
20
19
  import { appendFeedItem } from "../home/feed-writer.js";
21
20
  import { getConversation } from "../memory/conversation-crud.js";
@@ -28,13 +27,6 @@ import type { NotificationDecision, RenderedChannelCopy } from "./types.js";
28
27
 
29
28
  const log = getLogger("home-feed-side-effect");
30
29
 
31
- const FEED_ITEM_URGENCIES: ReadonlySet<string> = new Set<FeedItemUrgency>([
32
- "low",
33
- "medium",
34
- "high",
35
- "critical",
36
- ]);
37
-
38
30
  /**
39
31
  * Append a `FeedItem` for the given notification signal when the
40
32
  * filter criteria pass.
@@ -97,9 +89,7 @@ export async function writeHomeFeedItemForSignal(
97
89
  return null;
98
90
  }
99
91
 
100
- const urgency = FEED_ITEM_URGENCIES.has(signal.attentionHints.urgency)
101
- ? (signal.attentionHints.urgency as FeedItemUrgency)
102
- : undefined;
92
+ const urgency = signal.attentionHints.urgency;
103
93
  const now = new Date().toISOString();
104
94
 
105
95
  const category = deriveCategory(signal);
@@ -109,7 +99,7 @@ export async function writeHomeFeedItemForSignal(
109
99
  signal.contextPayload &&
110
100
  typeof signal.contextPayload === "object" &&
111
101
  !Array.isArray(signal.contextPayload)
112
- ? { ...(signal.contextPayload as Record<string, unknown>) }
102
+ ? { ...signal.contextPayload }
113
103
  : undefined;
114
104
 
115
105
  // Link scheduled-run notifications back to their schedule. `notify`-mode
@@ -187,7 +177,7 @@ function deriveDetailPanelKind(
187
177
  const payload = signal.contextPayload;
188
178
  const kind =
189
179
  payload && typeof payload === "object" && "requestKind" in payload
190
- ? (payload as Record<string, unknown>).requestKind
180
+ ? payload.requestKind
191
181
  : undefined;
192
182
  if (kind === "tool_approval" || kind === "tool_grant_request") {
193
183
  return "permissionChat";
@@ -217,9 +207,10 @@ function resolveHomeFeedMirror(
217
207
  sourceConversationId?: string;
218
208
  sourceScheduleJobId?: string;
219
209
  } {
220
- let sourceRow:
221
- | { conversationType?: string; scheduleJobId?: string | null }
222
- | null = null;
210
+ let sourceRow: {
211
+ conversationType?: string;
212
+ scheduleJobId?: string | null;
213
+ } | null = null;
223
214
  if (signal.sourceContextId) {
224
215
  try {
225
216
  sourceRow = getConversation(signal.sourceContextId) ?? null;
@@ -24,43 +24,42 @@ export function readPayloadString(
24
24
  payload: unknown,
25
25
  key: string,
26
26
  ): string | undefined {
27
- if (!payload || typeof payload !== "object") return undefined;
27
+ if (!payload || typeof payload !== "object" || Array.isArray(payload))
28
+ return undefined;
28
29
  const value = (payload as Record<string, unknown>)[key];
29
30
  return typeof value === "string" ? value : undefined;
30
31
  }
31
32
 
33
+ /** Truncate `text` to `maxLength`, appending "…" when exceeded. */
34
+ export function truncate(text: string, maxLength: number): string {
35
+ if (text.length <= maxLength) return text;
36
+ return text.slice(0, maxLength - 1) + "…";
37
+ }
38
+
32
39
  // ── Sanitization ────────────────────────────────────────────────────────────
33
40
 
34
- const IDENTITY_FIELD_MAX_LENGTH = 120;
41
+ /** Strip control characters and newlines, then truncate to `maxLength`. */
42
+ function sanitize(value: string, maxLength: number): string {
43
+ return truncate(
44
+ value.replace(/[\x00-\x1f\x7f-\x9f\r\n]+/g, " ").trim(),
45
+ maxLength,
46
+ );
47
+ }
35
48
 
36
49
  /**
37
50
  * Sanitize an untrusted identity field for inclusion in notification copy.
38
- *
39
- * - Strips control characters (U+0000–U+001F, U+007F–U+009F) and newlines.
40
- * - Clamps to 120 characters.
51
+ * Strips control characters and clamps to 120 characters.
41
52
  */
42
53
  export function sanitizeIdentityField(value: string): string {
43
- const stripped = value.replace(/[\x00-\x1f\x7f-\x9f\r\n]+/g, " ").trim();
44
- const clamped =
45
- stripped.length > IDENTITY_FIELD_MAX_LENGTH
46
- ? stripped.slice(0, IDENTITY_FIELD_MAX_LENGTH) + "…"
47
- : stripped;
48
- return clamped;
54
+ return sanitize(value, 120);
49
55
  }
50
56
 
51
57
  export const MESSAGE_PREVIEW_MAX_LENGTH = 200;
52
58
 
53
59
  /**
54
60
  * Sanitize an untrusted message preview for inclusion in notification copy.
55
- *
56
- * Same as {@link sanitizeIdentityField} but uses a higher length limit
57
- * (200 chars) suited for message previews.
61
+ * Strips control characters and clamps to 200 characters.
58
62
  */
59
63
  export function sanitizeMessagePreview(value: string): string {
60
- const stripped = value.replace(/[\x00-\x1f\x7f-\x9f\r\n]+/g, " ").trim();
61
- const clamped =
62
- stripped.length > MESSAGE_PREVIEW_MAX_LENGTH
63
- ? stripped.slice(0, MESSAGE_PREVIEW_MAX_LENGTH) + "…"
64
- : stripped;
65
- return clamped;
64
+ return sanitize(value, MESSAGE_PREVIEW_MAX_LENGTH);
66
65
  }
@@ -2,8 +2,13 @@
2
2
  * NotificationSignal -- the flexible input from producers.
3
3
  * Uses free-form event names and structured attention hints that let the
4
4
  * decision engine route contextually.
5
+ *
6
+ * All data shapes are defined as Zod schemas — types are derived via
7
+ * `z.infer` so runtime validation and compile-time types stay in sync.
5
8
  */
6
9
 
10
+ import { z } from "zod";
11
+
7
12
  import type { ConversationCreateType } from "../memory/conversation-crud.js";
8
13
  import type { GuardianQuestionPayload } from "./guardian-question-mode.js";
9
14
 
@@ -14,7 +19,11 @@ export const NOTIFICATION_SOURCE_CHANNELS = [
14
19
  { id: "vellum", description: "Vellum native client (macOS/iOS)" },
15
20
  { id: "phone", description: "Phone call pipeline" },
16
21
  { id: "telegram", description: "Telegram channel" },
22
+ { id: "whatsapp", description: "WhatsApp channel" },
17
23
  { id: "slack", description: "Slack channel" },
24
+ { id: "email", description: "Email channel" },
25
+ { id: "platform", description: "Platform-managed channel" },
26
+ { id: "a2a", description: "Agent-to-agent protocol channel" },
18
27
  { id: "scheduler", description: "Scheduled task runner (reminders, cron)" },
19
28
  { id: "watcher", description: "File/event watcher subsystem" },
20
29
  ] as const;
@@ -22,6 +31,18 @@ export const NOTIFICATION_SOURCE_CHANNELS = [
22
31
  export type NotificationSourceChannel =
23
32
  (typeof NOTIFICATION_SOURCE_CHANNELS)[number]["id"];
24
33
 
34
+ /** Typed tuple of all source channel IDs — usable with `z.enum()`. */
35
+ export const NOTIFICATION_SOURCE_CHANNEL_IDS = NOTIFICATION_SOURCE_CHANNELS.map(
36
+ (c) => c.id,
37
+ ) as unknown as readonly [
38
+ NotificationSourceChannel,
39
+ ...NotificationSourceChannel[],
40
+ ];
41
+
42
+ export const NotificationSourceChannelSchema = z.enum(
43
+ NOTIFICATION_SOURCE_CHANNEL_IDS,
44
+ );
45
+
25
46
  export function isNotificationSourceChannel(
26
47
  value: unknown,
27
48
  ): value is NotificationSourceChannel {
@@ -114,15 +135,24 @@ export type NotificationSourceEventName =
114
135
 
115
136
  // ── Attention hints & routing ──────────────────────────────────────────
116
137
 
117
- export interface AttentionHints {
118
- requiresAction: boolean;
119
- urgency: "low" | "medium" | "high" | "critical";
120
- deadlineAt?: number; // epoch ms
121
- isAsyncBackground: boolean;
122
- visibleInSourceNow: boolean;
123
- }
138
+ export const UrgencySchema = z.enum(["low", "medium", "high", "critical"]);
139
+ export type Urgency = z.infer<typeof UrgencySchema>;
124
140
 
125
- export type RoutingIntent = "single_channel" | "multi_channel" | "all_channels";
141
+ export const AttentionHintsSchema = z.object({
142
+ requiresAction: z.boolean(),
143
+ urgency: UrgencySchema,
144
+ deadlineAt: z.number().optional(),
145
+ isAsyncBackground: z.boolean(),
146
+ visibleInSourceNow: z.boolean(),
147
+ });
148
+ export type AttentionHints = z.infer<typeof AttentionHintsSchema>;
149
+
150
+ export const RoutingIntentSchema = z.enum([
151
+ "single_channel",
152
+ "multi_channel",
153
+ "all_channels",
154
+ ]);
155
+ export type RoutingIntent = z.infer<typeof RoutingIntentSchema>;
126
156
 
127
157
  // ── Typed context payloads ──────────────────────────────────────────────
128
158
 
@@ -140,42 +170,48 @@ export type RoutingIntent = "single_channel" | "multi_channel" | "all_channels";
140
170
  * This is channel-agnostic by design — any channel's access request that
141
171
  * resolves to a non-source-channel guardian gets the same treatment.
142
172
  */
143
- export type GuardianResolutionSource =
144
- | "source-channel-contact"
145
- | "vellum-anchor"
146
- | "none";
147
-
148
- export interface AccessRequestContextPayload {
149
- requestId: string;
150
- requestCode: string;
151
- sourceChannel: string;
152
- conversationExternalId: string;
153
- actorExternalId: string;
154
- actorDisplayName: string | null;
155
- actorUsername: string | null;
156
- senderIdentifier: string;
157
- guardianBindingChannel: string | null;
158
- guardianResolutionSource: GuardianResolutionSource;
159
- previousMemberStatus: string | null;
160
- /** Preview of the requester's original message (first ~200 chars). */
161
- messagePreview: string | null;
162
- /** Slack-specific: user is from an external workspace (Slack Connect). */
163
- isStranger?: boolean;
164
- /** Slack-specific: user is a guest / restricted account. */
165
- isRestricted?: boolean;
166
- /** Slack message timestamp (e.g. "1234567890.123456") for permalink construction. */
167
- messageTs?: string;
168
- }
173
+ export const GuardianResolutionSourceSchema = z.enum([
174
+ "source-channel-contact",
175
+ "vellum-anchor",
176
+ "none",
177
+ ]);
178
+ export type GuardianResolutionSource = z.infer<
179
+ typeof GuardianResolutionSourceSchema
180
+ >;
169
181
 
170
- export interface GuardianChannelActivationPayload {
171
- verificationCode: string;
172
- sourceChannel: string;
173
- actorExternalId: string;
174
- actorDisplayName: string | null;
175
- actorUsername: string | null;
176
- sessionId: string;
177
- expiresAt: number;
178
- }
182
+ export const AccessRequestContextPayloadSchema = z.object({
183
+ requestId: z.string(),
184
+ requestCode: z.string(),
185
+ sourceChannel: z.string(),
186
+ conversationExternalId: z.string(),
187
+ actorExternalId: z.string(),
188
+ actorDisplayName: z.string().nullable(),
189
+ actorUsername: z.string().nullable(),
190
+ senderIdentifier: z.string(),
191
+ guardianBindingChannel: z.string().nullable(),
192
+ guardianResolutionSource: GuardianResolutionSourceSchema,
193
+ previousMemberStatus: z.string().nullable(),
194
+ messagePreview: z.string().nullable(),
195
+ isStranger: z.boolean().optional(),
196
+ isRestricted: z.boolean().optional(),
197
+ messageTs: z.string().optional(),
198
+ });
199
+ export type AccessRequestContextPayload = z.infer<
200
+ typeof AccessRequestContextPayloadSchema
201
+ >;
202
+
203
+ export const GuardianChannelActivationPayloadSchema = z.object({
204
+ verificationCode: z.string(),
205
+ sourceChannel: z.string(),
206
+ actorExternalId: z.string(),
207
+ actorDisplayName: z.string().nullable(),
208
+ actorUsername: z.string().nullable(),
209
+ sessionId: z.string(),
210
+ expiresAt: z.number(),
211
+ });
212
+ export type GuardianChannelActivationPayload = z.infer<
213
+ typeof GuardianChannelActivationPayloadSchema
214
+ >;
179
215
 
180
216
  export interface NotificationEventContextPayloadMap {
181
217
  "guardian.question": GuardianQuestionPayload;
@@ -1,14 +1,19 @@
1
1
  /**
2
2
  * Core domain types for the unified notification system.
3
3
  *
4
- * Defines the channel-adapter interfaces that the broadcaster and adapters
5
- * depend on, plus the decision engine output contract.
4
+ * Data shapes are defined as Zod schemas types derived via `z.infer`
5
+ * so runtime validation and compile-time types stay in sync.
6
+ *
7
+ * Behavioral interfaces (ChannelAdapter) remain as TypeScript interfaces.
6
8
  */
7
9
 
10
+ import { ApprovalUIMetadataSchema } from "@vellumai/gateway-client";
11
+ import { z } from "zod";
12
+
8
13
  import type { ChannelPolicies } from "../channels/config.js";
9
14
  import type { ChannelId } from "../channels/types.js";
10
- import type { ApprovalUIMetadata } from "../runtime/channel-approval-types.js";
11
- import type { AttentionHints } from "./signal.js";
15
+ import { AccessRequestPayloadSchema } from "./access-request-copy.js";
16
+ import { UrgencySchema } from "./signal.js";
12
17
 
13
18
  /**
14
19
  * Derived from the channel policy registry: only channels whose
@@ -21,38 +26,39 @@ export type NotificationChannel = {
21
26
  }[keyof ChannelPolicies] &
22
27
  ChannelId;
23
28
 
24
- export type NotificationDeliveryStatus =
25
- | "pending"
26
- | "sent"
27
- | "failed"
28
- | "skipped";
29
-
30
- /** Result of attempting to deliver a notification to a single channel. */
31
- export interface NotificationDeliveryResult {
32
- channel: NotificationChannel;
33
- destination: string;
34
- status: NotificationDeliveryStatus;
35
- errorCode?: string;
36
- errorMessage?: string;
37
- sentAt?: number;
38
- conversationId?: string;
39
- messageId?: string;
40
- conversationStrategy?: string;
41
- }
42
-
43
- // -- Channel adapter interfaces -----------------------------------------------
44
-
45
- /** Result returned by a channel adapter after attempting to send. */
46
- export interface DeliveryResult {
47
- success: boolean;
48
- error?: string;
49
- /**
50
- * Channel-native message identifier captured at send time (e.g. Slack `ts`).
51
- * Persisted onto `notification_deliveries.messageId` so later edits can
52
- * target the same message via `ChannelAdapter.update()`.
53
- */
54
- messageId?: string;
55
- }
29
+ export const NotificationDeliveryStatusSchema = z.enum([
30
+ "pending",
31
+ "sent",
32
+ "failed",
33
+ "skipped",
34
+ ]);
35
+ export type NotificationDeliveryStatus = z.infer<
36
+ typeof NotificationDeliveryStatusSchema
37
+ >;
38
+
39
+ export const NotificationDeliveryResultSchema = z.object({
40
+ channel: z.string(),
41
+ destination: z.string(),
42
+ status: NotificationDeliveryStatusSchema,
43
+ errorCode: z.string().optional(),
44
+ errorMessage: z.string().optional(),
45
+ sentAt: z.number().optional(),
46
+ conversationId: z.string().optional(),
47
+ messageId: z.string().optional(),
48
+ conversationStrategy: z.string().optional(),
49
+ });
50
+ export type NotificationDeliveryResult = z.infer<
51
+ typeof NotificationDeliveryResultSchema
52
+ >;
53
+
54
+ // -- Channel adapter data shapes ----------------------------------------------
55
+
56
+ export const DeliveryResultSchema = z.object({
57
+ success: z.boolean(),
58
+ error: z.string().optional(),
59
+ messageId: z.string().optional(),
60
+ });
61
+ export type DeliveryResult = z.infer<typeof DeliveryResultSchema>;
56
62
 
57
63
  /** Resolved destination for a specific channel. */
58
64
  export interface ChannelDestination {
@@ -69,52 +75,44 @@ export interface ChannelDestination {
69
75
  * conversations keyed by (sourceChannel, externalChatId).
70
76
  */
71
77
  export interface DestinationBindingContext {
72
- /** The channel this binding belongs to (e.g. "telegram", "slack"). */
73
78
  sourceChannel: NotificationChannel;
74
- /** The channel-specific chat/conversation identifier (e.g. Telegram chat ID, phone number). */
75
79
  externalChatId: string;
76
- /** Optional external user identifier within the chat. */
77
80
  externalUserId?: string;
78
81
  }
79
82
 
80
- /**
81
- * Delivery payload assembled from the decision engine's rendered copy
82
- * plus contextual fields the adapters need for formatting and routing.
83
- */
84
- export interface ChannelDeliveryPayload {
85
- /** Delivery audit record ID — passed through to the client for ack correlation. */
86
- deliveryId?: string;
87
- sourceEventName: string;
88
- copy: RenderedChannelCopy;
89
- deepLinkTarget?: Record<string, unknown>;
90
- /** Original signal context payload — available for channel-specific structured rendering. */
91
- contextPayload?: Record<string, unknown>;
92
- /**
93
- * Forwarded from the originating signal so adapters can make
94
- * urgency-aware decisions (e.g. the vellum adapter suppresses the OS
95
- * banner for non-urgent intents while still emitting the conversation
96
- * pairing side effects).
97
- */
98
- urgency: AttentionHints["urgency"];
99
- /**
100
- * Structured approval context for notifications that require guardian
101
- * action buttons (approve/reject). Built centrally by the broadcaster
102
- * so adapters can render channel-native approval UI without re-parsing
103
- * `contextPayload`.
104
- */
105
- approvalContext?: ApprovalUIMetadata;
106
- }
83
+ // -- Rendered copy & delivery payload -----------------------------------------
84
+
85
+ export const RenderedChannelCopySchema = z.object({
86
+ title: z.string(),
87
+ body: z.string(),
88
+ deliveryText: z.string().optional(),
89
+ conversationTitle: z.string().optional(),
90
+ conversationSeedMessage: z.string().optional(),
91
+ seedContentBlocks: z.array(z.unknown()).optional(),
92
+ });
93
+ export type RenderedChannelCopy = z.infer<typeof RenderedChannelCopySchema>;
94
+
95
+ export const ChannelDeliveryPayloadSchema = z.object({
96
+ deliveryId: z.string().optional(),
97
+ sourceEventName: z.string(),
98
+ copy: RenderedChannelCopySchema,
99
+ deepLinkTarget: z.record(z.string(), z.unknown()).optional(),
100
+ contextPayload: z.record(z.string(), z.unknown()).optional(),
101
+ urgency: UrgencySchema,
102
+ approvalContext: ApprovalUIMetadataSchema.optional(),
103
+ accessRequestContext: AccessRequestPayloadSchema.optional(),
104
+ });
105
+ export type ChannelDeliveryPayload = z.infer<
106
+ typeof ChannelDeliveryPayloadSchema
107
+ >;
107
108
 
108
- /**
109
- * Patch supplied when an already-delivered notification is edited.
110
- * Adapters only need to act on `title` and `body` — feed-only fields
111
- * like `urgency` and `status` are handled by the home-feed patch.
112
- */
113
109
  export interface ChannelUpdatePayload {
114
110
  title?: string;
115
111
  body?: string;
116
112
  }
117
113
 
114
+ // -- Channel adapter interface ------------------------------------------------
115
+
118
116
  /** Interface that each channel adapter must implement. */
119
117
  export interface ChannelAdapter {
120
118
  channel: NotificationChannel;
@@ -122,77 +120,57 @@ export interface ChannelAdapter {
122
120
  payload: ChannelDeliveryPayload,
123
121
  destination: ChannelDestination,
124
122
  ): Promise<DeliveryResult>;
125
- /**
126
- * Optional: edit a previously-sent message in place. Channels that
127
- * cannot edit (push, email, SMS) omit this and the router treats
128
- * them as `"unsupported"`. Adapters that implement it use
129
- * `delivery.messageId` (captured at send time) as the channel-native
130
- * handle for the in-place update.
131
- */
132
123
  update?(
133
124
  delivery: ChannelUpdateContext,
134
125
  patch: ChannelUpdatePayload,
135
126
  ): Promise<DeliveryResult>;
136
127
  }
137
128
 
138
- /** Per-delivery state an adapter needs to update a previously-sent message. */
139
129
  export interface ChannelUpdateContext {
140
- /** notification_deliveries.id */
141
130
  deliveryId: string;
142
- /** Channel-native destination (e.g. Slack chat id). */
143
131
  destination: string;
144
- /** Channel-native message identifier captured at send time. */
145
132
  messageId: string | null;
146
133
  }
147
134
 
148
- // -- Decision engine output ---------------------------------------------------
149
-
150
- /** Rendered notification copy for a single channel. */
151
- export interface RenderedChannelCopy {
152
- title: string;
153
- body: string;
154
- /** Channel-native delivery text (e.g. Telegram chat message body). */
155
- deliveryText?: string;
156
- conversationTitle?: string;
157
- conversationSeedMessage?: string;
158
- /**
159
- * Structured content blocks for the seed message. When present,
160
- * conversation pairing stores `JSON.stringify(seedContentBlocks)` as the
161
- * message content instead of the plain-text seed, enabling Surface
162
- * rendering in the web/macOS/iOS apps.
163
- *
164
- * Typically includes a `ui_surface` block (rendered as an interactive
165
- * card) followed by a `text` block (plain-text fallback for search,
166
- * backward-compatible clients, and CLI display).
167
- */
168
- seedContentBlocks?: unknown[];
169
- }
170
-
171
135
  // -- Conversation action types ------------------------------------------------
172
136
 
173
- /** Start a new conversation for the notification delivery. */
174
- export interface ConversationActionStartNew {
175
- action: "start_new";
176
- }
137
+ export const ConversationActionSchema = z.discriminatedUnion("action", [
138
+ z.object({ action: z.literal("start_new") }),
139
+ z.object({
140
+ action: z.literal("reuse_existing"),
141
+ conversationId: z.string(),
142
+ }),
143
+ ]);
144
+ export type ConversationAction = z.infer<typeof ConversationActionSchema>;
177
145
 
178
- /** Reuse an existing conversation identified by conversationId. */
179
- export interface ConversationActionReuseExisting {
180
- action: "reuse_existing";
181
- conversationId: string;
182
- }
146
+ // -- Decision engine output ---------------------------------------------------
183
147
 
184
- /** Per-channel conversation action — either start a new conversation or reuse an existing one. */
185
- export type ConversationAction =
186
- | ConversationActionStartNew
187
- | ConversationActionReuseExisting;
148
+ export const NotificationDecisionSchema = z.object({
149
+ shouldNotify: z.boolean(),
150
+ selectedChannels: z.array(z.string()),
151
+ reasoningSummary: z.string(),
152
+ renderedCopy: z.record(z.string(), RenderedChannelCopySchema),
153
+ conversationActions: z
154
+ .record(z.string(), ConversationActionSchema)
155
+ .optional(),
156
+ deepLinkTarget: z.record(z.string(), z.unknown()).optional(),
157
+ dedupeKey: z.string(),
158
+ confidence: z.number(),
159
+ fallbackUsed: z.boolean(),
160
+ persistedDecisionId: z.string().optional(),
161
+ });
188
162
 
189
- /** Output produced by the notification decision engine for a given signal. */
163
+ /**
164
+ * Decision engine output. `selectedChannels` and `renderedCopy` are keyed
165
+ * by `NotificationChannel` — narrower than the Zod schema's `string` since
166
+ * `NotificationChannel` is a computed type derived from the channel config
167
+ * registry and cannot be expressed as a Zod enum.
168
+ */
190
169
  export interface NotificationDecision {
191
170
  shouldNotify: boolean;
192
171
  selectedChannels: NotificationChannel[];
193
172
  reasoningSummary: string;
194
173
  renderedCopy: Partial<Record<NotificationChannel, RenderedChannelCopy>>;
195
- /** Per-channel conversation actions decided by the model. Absent channels default to start_new. */
196
174
  conversationActions?: Partial<
197
175
  Record<NotificationChannel, ConversationAction>
198
176
  >;
@@ -200,6 +178,5 @@ export interface NotificationDecision {
200
178
  dedupeKey: string;
201
179
  confidence: number;
202
180
  fallbackUsed: boolean;
203
- /** UUID of the persisted decision row (set after persistence in the decision engine). */
204
181
  persistedDecisionId?: string;
205
182
  }
@@ -29,34 +29,15 @@ Then add the key to `ServicesSchema`:
29
29
 
30
30
  The key here **must** match the `managedServiceConfigKey` in `seed-providers.ts`. The cross-repo invariant test in `__tests__/seed-providers-managed.test.ts` will fail if they drift.
31
31
 
32
- ### 3. _(managed only)_ Enable by default during onboarding — `clients/macos/.../HatchingStepView.swift`
32
+ ### 3. _(managed only)_ Enable by default during onboarding
33
33
 
34
- In `buildOnboardingConfigValues()`, add a line so managed-sign-in users get the integration pre-enabled:
34
+ Managed-sign-in users should get the integration pre-enabled by setting `services.acme-oauth.mode` to `"managed"` in the client's onboarding config defaults.
35
35
 
36
- ```swift
37
- configValues["services.acme-oauth.mode"] = "managed"
38
- ```
39
-
40
- ### 4. Add a cached logo — `clients/shared/Resources/IntegrationLogos/`
41
-
42
- Drop a **vector PDF** named `{provider_key}.pdf` (e.g. `acme.pdf`) into the `IntegrationLogos/` directory. The file is automatically bundled via `.copy()` in `clients/Package.swift` and looked up at runtime by `IntegrationLogoBundle` using the provider key — no code changes needed.
43
-
44
- Most existing logos come from [Simple Icons](https://simpleicons.org) (CC0-licensed). To get a PDF from Simple Icons:
45
-
46
- 1. Find the icon slug on https://simpleicons.org (e.g. `slack`, `linear`).
47
- 2. Download the SVG: `curl -o acme.svg https://raw.githubusercontent.com/simple-icons/simple-icons/develop/icons/acme.svg`
48
- 3. Convert to PDF using `rsvg-convert` (same tool used by `clients/scripts/sync-lucide-icons.sh`):
49
- ```bash
50
- # Install if needed: brew install librsvg
51
- rsvg-convert -f pdf -o clients/shared/Resources/IntegrationLogos/acme.pdf acme.svg
52
- ```
53
- 4. Add the provider key to `clients/shared/Resources/integration-logos-manifest.json`.
54
-
55
- If the service is not on Simple Icons, source or create an SVG and convert it the same way. The result must be a true vector PDF (not a rasterized image wrapped in PDF) so it scales cleanly.
36
+ ### 4. Set the logo URL — `seed-providers.ts`
56
37
 
57
- The `logoUrl` field in `seed-providers.ts` serves as the remote fallback (most providers use a Simple Icons CDN URL like `https://cdn.simpleicons.org/acme`). The client renders the local PDF first, then falls back to `logoUrl`, then to an initials avatar.
38
+ The `logoUrl` field in `seed-providers.ts` is the source of truth for a provider's logo. Most providers use a [Simple Icons](https://simpleicons.org) (CC0-licensed) CDN URL like `https://cdn.simpleicons.org/acme`. The web client resolves logos from this field (see `clients/web/src/components/integrations/integration-icon.tsx`) and falls back to an initials avatar.
58
39
 
59
- For brands Simple Icons doesn't host (e.g. Salesforce, which Simple Icons removed for trademark reasons), use the same `glincker/thesvg` source via jsDelivr — `https://cdn.jsdelivr.net/gh/glincker/thesvg@main/public/icons/<key>/default.svg`. The recognised `logoUrl` prefixes are enforced by `oauth-provider-seed-logos.test.ts`; if you need a third source, extend that allowlist alongside the manifest in `clients/shared/Resources/integration-logos-manifest.json`.
40
+ For brands Simple Icons doesn't host (e.g. Salesforce, which Simple Icons removed for trademark reasons), use the `glincker/thesvg` source via jsDelivr — `https://cdn.jsdelivr.net/gh/glincker/thesvg@main/public/icons/<key>/default.svg`. The recognised `logoUrl` prefixes are enforced by `oauth-provider-seed-logos.test.ts`; if you need a third source, extend that allowlist.
60
41
 
61
42
  ### 5. Secret patterns (if applicable) — `../security/secret-patterns.ts`
62
43