@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
@@ -7,7 +7,6 @@
7
7
 
8
8
  import { z } from "zod";
9
9
 
10
- import { buildApprovalCardBlocks } from "./approval-card-builder.js";
11
10
  import {
12
11
  nonEmpty,
13
12
  sanitizeIdentityField,
@@ -312,74 +311,88 @@ export function buildAccessRequestContractText(
312
311
  return lines.join("\n");
313
312
  }
314
313
 
315
- // ── Seed content blocks (Surface-based rendering) ───────────────────────────
314
+ // ── Card view model ─────────────────────────────────────────────────────────
316
315
 
317
316
  /**
318
- * Build structured content blocks for an access request notification seed
319
- * message. Produces a `ui_surface` card block that the web/macOS/iOS apps
320
- * render as an interactive card via `SurfaceRouter CardSurface`, plus a
321
- * plain-text fallback block for search, CLI display, and backward-compatible
322
- * clients that don't support surfaces.
317
+ * Display-ready projection of an access request, shared by every renderer
318
+ * (the Vellum Surface card and the Slack Card block). It carries the
319
+ * sanitized, pre-computed facts each renderer needs identity sanitizing,
320
+ * warnings, permalink, DM detection, preview sanitizing so that projection
321
+ * lives in exactly one place. Renderers lay these facts out in their
322
+ * channel-native shape without re-deriving them.
323
323
  */
324
- export function buildAccessRequestSeedContentBlocks(
325
- payload: Record<string, unknown>,
326
- ): unknown[] {
327
- const p = parseAccessRequestPayload(payload);
324
+ export interface AccessRequestCardView {
325
+ /** Sanitized display name (actorDisplayName ?? senderIdentifier, else "Someone"). */
326
+ displayName: string;
327
+ /** Sanitized username, without the leading `@`. */
328
+ username: string | undefined;
329
+ /** Sanitized external ID. */
330
+ externalId: string | undefined;
331
+ sourceChannel: string | undefined;
332
+ conversationExternalId: string | undefined;
333
+ /** Whether the source Slack conversation is a DM. */
334
+ isSlackDm: boolean;
335
+ /** Slack permalink — present only for a slack source with conversation + ts. */
336
+ messagePermalink: string | undefined;
337
+ /** Sanitized message preview, or undefined when blank after sanitizing. */
338
+ messagePreview: string | undefined;
339
+ /** Human-readable trust/security warnings. */
340
+ warnings: string[];
341
+ guardianResolutionSource: string | undefined;
342
+ requestId: string | undefined;
343
+ }
328
344
 
345
+ /**
346
+ * Project a parsed access-request payload into display-ready card facts.
347
+ *
348
+ * The payload is parsed once upstream — the broadcaster resolves
349
+ * `accessRequestContext`, and the Surface seed path parses the raw payload —
350
+ * so this takes the parsed payload rather than re-parsing it.
351
+ */
352
+ export function buildAccessRequestCardView(
353
+ p: ParsedAccessRequestPayload,
354
+ ): AccessRequestCardView {
329
355
  const rawName = nonEmpty(p.actorDisplayName) ?? nonEmpty(p.senderIdentifier);
330
356
  const displayName = rawName ? sanitizeIdentityField(rawName) : "Someone";
331
357
 
332
- const metadata: Array<{ label: string; value: string }> = [];
358
+ const rawUsername = nonEmpty(p.actorUsername);
359
+ const username = rawUsername ? sanitizeIdentityField(rawUsername) : undefined;
333
360
 
334
- if (p.actorUsername) {
335
- metadata.push({
336
- label: "Username",
337
- value: `@${sanitizeIdentityField(p.actorUsername)}`,
338
- });
339
- }
361
+ const rawExternalId = nonEmpty(p.actorExternalId);
362
+ const externalId = rawExternalId
363
+ ? sanitizeIdentityField(rawExternalId)
364
+ : undefined;
340
365
 
341
- if (p.sourceChannel === "slack" && p.conversationExternalId) {
342
- const isDm = isSlackDmConversation(p.conversationExternalId);
343
- metadata.push({
344
- label: "Source",
345
- value: isDm
346
- ? "Slack — Direct message"
347
- : `Slack — #${p.conversationExternalId}`,
348
- });
349
- } else if (p.sourceChannel) {
350
- metadata.push({ label: "Source", value: p.sourceChannel });
351
- }
366
+ const sourceChannel = nonEmpty(p.sourceChannel);
367
+ const conversationExternalId = nonEmpty(p.conversationExternalId);
368
+ const messageTs = nonEmpty(p.messageTs);
352
369
 
353
- const warnings = buildAccessRequestWarnings(p);
354
- const bodyParts: string[] = [];
370
+ const isSlackDm =
371
+ sourceChannel === "slack" && conversationExternalId != null
372
+ ? isSlackDmConversation(conversationExternalId)
373
+ : false;
355
374
 
356
- if (p.messagePreview) {
357
- bodyParts.push(`> "${sanitizeMessagePreview(p.messagePreview)}"`);
358
- }
359
- for (const w of warnings) {
360
- bodyParts.push(`⚠️ ${w}`);
361
- }
362
- if (p.sourceChannel === "slack" && p.conversationExternalId && p.messageTs) {
363
- const permalink = buildSlackMessagePermalink(
364
- p.conversationExternalId,
365
- p.messageTs,
366
- );
367
- bodyParts.push(`[View message](${permalink})`);
368
- }
375
+ const messagePermalink =
376
+ sourceChannel === "slack" && conversationExternalId && messageTs
377
+ ? buildSlackMessagePermalink(conversationExternalId, messageTs)
378
+ : undefined;
379
+
380
+ const rawPreview = nonEmpty(p.messagePreview);
381
+ const messagePreview = rawPreview
382
+ ? sanitizeMessagePreview(rawPreview) || undefined
383
+ : undefined;
369
384
 
370
- const body =
371
- bodyParts.length > 0
372
- ? bodyParts.join("\n\n")
373
- : "No additional context available.";
374
-
375
- return buildApprovalCardBlocks({
376
- surfaceIdPrefix: "access-request",
377
- cardTitle: "Access Request",
378
- requesterName: displayName,
379
- subtitle: "Requesting access to the assistant",
380
- body,
381
- metadata,
382
- requestId: p.requestId,
383
- fallbackText: buildAccessRequestContractText(payload),
384
- });
385
+ return {
386
+ displayName,
387
+ username,
388
+ externalId,
389
+ sourceChannel,
390
+ conversationExternalId,
391
+ isSlackDm,
392
+ messagePermalink,
393
+ messagePreview,
394
+ warnings: buildAccessRequestWarnings(p),
395
+ guardianResolutionSource: nonEmpty(p.guardianResolutionSource),
396
+ requestId: nonEmpty(p.requestId),
397
+ };
385
398
  }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Shared adapter utilities — functions used by multiple channel adapters.
3
+ */
4
+
5
+ import { isConversationSeedSane } from "../conversation-seed-composer.js";
6
+ import { nonEmpty } from "../notification-utils.js";
7
+ import type { ChannelDeliveryPayload } from "../types.js";
8
+
9
+ /**
10
+ * Resolve the primary message text for a notification delivery.
11
+ *
12
+ * Cascade: deliveryText → conversationSeedMessage → body → title → event name.
13
+ */
14
+ export function resolveMessageText(payload: ChannelDeliveryPayload): string {
15
+ const deliveryText = nonEmpty(payload.copy.deliveryText);
16
+ if (deliveryText) return deliveryText;
17
+
18
+ if (isConversationSeedSane(payload.copy.conversationSeedMessage)) {
19
+ return payload.copy.conversationSeedMessage.trim();
20
+ }
21
+
22
+ const body = nonEmpty(payload.copy.body);
23
+ if (body) return body;
24
+
25
+ const title = nonEmpty(payload.copy.title);
26
+ if (title) return title;
27
+
28
+ return payload.sourceEventName.replace(/[._]/g, " ");
29
+ }
@@ -16,19 +16,11 @@ import { sendSlackReply } from "../../messaging/providers/slack/send.js";
16
16
  import type { ApprovalUIMetadata } from "../../runtime/channel-approval-types.js";
17
17
  import { getLogger } from "../../util/logger.js";
18
18
  import {
19
+ type AccessRequestCardView,
20
+ buildAccessRequestCardView,
19
21
  buildAccessRequestInviteDirective,
20
- buildAccessRequestWarnings,
21
- buildSlackMessagePermalink,
22
- isSlackDmConversation,
23
- parseAccessRequestPayload,
24
- type ParsedAccessRequestPayload,
25
22
  } from "../access-request-copy.js";
26
- import { isConversationSeedSane } from "../conversation-seed-composer.js";
27
- import {
28
- nonEmpty,
29
- sanitizeIdentityField,
30
- sanitizeMessagePreview,
31
- } from "../notification-utils.js";
23
+ import { truncate } from "../notification-utils.js";
32
24
  import type {
33
25
  ChannelAdapter,
34
26
  ChannelDeliveryPayload,
@@ -38,36 +30,10 @@ import type {
38
30
  DeliveryResult,
39
31
  NotificationChannel,
40
32
  } from "../types.js";
33
+ import { resolveMessageText } from "./shared.js";
41
34
 
42
35
  const log = getLogger("notif-adapter-slack");
43
36
 
44
- // ---------------------------------------------------------------------------
45
- // Text resolution
46
- // ---------------------------------------------------------------------------
47
-
48
- function resolveSlackMessageText(payload: ChannelDeliveryPayload): string {
49
- const deliveryText = nonEmpty(payload.copy.deliveryText);
50
- if (deliveryText) return deliveryText;
51
-
52
- if (isConversationSeedSane(payload.copy.conversationSeedMessage)) {
53
- return payload.copy.conversationSeedMessage.trim();
54
- }
55
-
56
- const body = nonEmpty(payload.copy.body);
57
- if (body) return body;
58
-
59
- const title = nonEmpty(payload.copy.title);
60
- if (title) return title;
61
-
62
- return payload.sourceEventName.replace(/[._]/g, " ");
63
- }
64
-
65
- /** Truncate to `maxLength`, appending "…" when exceeded. */
66
- function truncate(text: string, maxLength: number): string {
67
- if (text.length <= maxLength) return text;
68
- return text.slice(0, maxLength - 1) + "…";
69
- }
70
-
71
37
  // ---------------------------------------------------------------------------
72
38
  // Slack Card block builders for approval notifications
73
39
  // ---------------------------------------------------------------------------
@@ -88,57 +54,48 @@ function buildCardActions(approval: ApprovalUIMetadata): unknown[] {
88
54
  // ---------------------------------------------------------------------------
89
55
 
90
56
  /** Concise requester identity for the card subtitle (≤150 chars). */
91
- function buildAccessRequestSubtitle(p: ParsedAccessRequestPayload): string {
92
- const rawName = nonEmpty(p.actorDisplayName) ?? nonEmpty(p.senderIdentifier);
93
- const displayName = rawName ? sanitizeIdentityField(rawName) : "Someone";
94
- const parts = [displayName];
95
-
96
- const username = nonEmpty(p.actorUsername);
97
- if (username) {
98
- const safe = sanitizeIdentityField(username);
99
- if (safe !== displayName) parts.push(`(@${safe})`);
57
+ function buildAccessRequestSubtitle(view: AccessRequestCardView): string {
58
+ const parts = [view.displayName];
59
+
60
+ if (view.username && view.username !== view.displayName) {
61
+ parts.push(`(@${view.username})`);
100
62
  }
101
63
 
102
- if (p.sourceChannel) parts.push(`via ${p.sourceChannel}`);
64
+ if (view.sourceChannel) parts.push(`via ${view.sourceChannel}`);
103
65
 
104
66
  return truncate(parts.join(" "), 150);
105
67
  }
106
68
 
107
69
  /** Card body: message preview when available, otherwise a default label. */
108
- function buildAccessRequestBody(p: ParsedAccessRequestPayload): string {
109
- if (p.messagePreview) {
110
- const sanitized = sanitizeMessagePreview(p.messagePreview);
111
- if (sanitized) {
112
- // Truncate content before wrapping so formatting chars stay balanced.
113
- // Wrapper `> _"..."_` is 6 chars; reserve space for them.
114
- const trimmed = truncate(sanitized, 200 - 6);
115
- return `> _"${trimmed}"_`;
116
- }
70
+ function buildAccessRequestBody(view: AccessRequestCardView): string {
71
+ if (view.messagePreview) {
72
+ // Truncate content before wrapping so formatting chars stay balanced.
73
+ // Wrapper `> _"..."_` is 6 chars; reserve space for them.
74
+ const trimmed = truncate(view.messagePreview, 200 - 6);
75
+ return `> _"${trimmed}"_`;
117
76
  }
118
77
  return "Requesting access to the assistant";
119
78
  }
120
79
 
121
80
  /** Source-channel context block with Slack permalink when available. */
122
81
  function buildSourceContextBlock(
123
- p: ParsedAccessRequestPayload,
82
+ view: AccessRequestCardView,
124
83
  ): unknown | undefined {
125
- if (p.sourceChannel !== "slack" || !p.conversationExternalId) {
84
+ if (view.sourceChannel !== "slack" || !view.conversationExternalId) {
126
85
  return undefined;
127
86
  }
128
87
 
129
- const permalink = p.messageTs
130
- ? buildSlackMessagePermalink(p.conversationExternalId, p.messageTs)
131
- : undefined;
88
+ const permalink = view.messagePermalink;
132
89
 
133
90
  let sourceText: string;
134
- if (isSlackDmConversation(p.conversationExternalId)) {
91
+ if (view.isSlackDm) {
135
92
  sourceText = permalink
136
93
  ? `Source: Slack — Direct message · <${permalink}|View message>`
137
94
  : "Source: Slack — Direct message";
138
95
  } else {
139
96
  sourceText = permalink
140
- ? `Source: Slack — <#${p.conversationExternalId}> · <${permalink}|View message>`
141
- : `Source: Slack — <#${p.conversationExternalId}>`;
97
+ ? `Source: Slack — <#${view.conversationExternalId}> · <${permalink}|View message>`
98
+ : `Source: Slack — <#${view.conversationExternalId}>`;
142
99
  }
143
100
 
144
101
  return {
@@ -149,27 +106,12 @@ function buildSourceContextBlock(
149
106
 
150
107
  /** Stable requester identifier context block (external ID when it adds info). */
151
108
  function buildRequesterIdBlock(
152
- p: ParsedAccessRequestPayload,
109
+ view: AccessRequestCardView,
153
110
  ): unknown | undefined {
154
- const safeExternalId = nonEmpty(
155
- p.actorExternalId ? sanitizeIdentityField(p.actorExternalId) : undefined,
156
- );
111
+ const safeExternalId = view.externalId;
157
112
  if (!safeExternalId) return undefined;
158
113
 
159
- const displayedName = nonEmpty(
160
- p.actorDisplayName
161
- ? sanitizeIdentityField(p.actorDisplayName)
162
- : p.senderIdentifier
163
- ? sanitizeIdentityField(p.senderIdentifier)
164
- : undefined,
165
- );
166
- const displayedUsername = nonEmpty(
167
- p.actorUsername ? sanitizeIdentityField(p.actorUsername) : undefined,
168
- );
169
- if (
170
- safeExternalId === displayedName ||
171
- safeExternalId === displayedUsername
172
- ) {
114
+ if (safeExternalId === view.displayName || safeExternalId === view.username) {
173
115
  return undefined;
174
116
  }
175
117
 
@@ -183,7 +125,8 @@ function buildRequesterIdBlock(
183
125
  * Build Slack blocks for an access request using a native Card block.
184
126
  *
185
127
  * Layout:
186
- * Card — title + subtitle (identity) + body (preview) + actions + subtext (warnings)
128
+ * Card — title + subtitle (identity) + body (preview) + actions
129
+ * Context — security warnings (revoked/restricted/stranger), when present
187
130
  * Context — source permalink (when the request is from Slack)
188
131
  * Context — stable requester ID (when it adds info beyond subtitle)
189
132
  * Context — invite directive
@@ -193,32 +136,42 @@ function buildAccessRequestCardBlocks(
193
136
  payload: ChannelDeliveryPayload,
194
137
  ): unknown[] {
195
138
  const approval = payload.approvalContext!;
196
- const p = parseAccessRequestPayload(payload.contextPayload!);
139
+ const view = buildAccessRequestCardView(payload.accessRequestContext!);
197
140
  const blocks: unknown[] = [];
198
141
 
199
- const subtitle = buildAccessRequestSubtitle(p);
200
- const body = buildAccessRequestBody(p);
142
+ const subtitle = buildAccessRequestSubtitle(view);
143
+ const body = buildAccessRequestBody(view);
201
144
 
202
- const warnings = buildAccessRequestWarnings(p);
203
- const subtext =
204
- warnings.length > 0
205
- ? truncate(warnings.map((w) => `:warning: ${w}`).join(" · "), 200)
145
+ const warningsText =
146
+ view.warnings.length > 0
147
+ ? truncate(view.warnings.map((w) => `:warning: ${w}`).join(" · "), 200)
206
148
  : undefined;
207
149
 
208
150
  const card: Record<string, unknown> = {
209
151
  type: "card",
210
- title: { type: "plain_text", text: "Access Request" },
152
+ title: { type: "mrkdwn", text: "Access Request" },
211
153
  subtitle: { type: "mrkdwn", text: subtitle },
212
154
  body: { type: "mrkdwn", text: body },
213
155
  actions: buildCardActions(approval),
214
156
  };
215
- if (subtext) card.subtext = { type: "mrkdwn", text: subtext };
216
157
  blocks.push(card);
217
158
 
218
- const sourceContext = buildSourceContextBlock(p);
159
+ // Security warnings (revoked / restricted / stranger) render in a context
160
+ // block under the card. Slack's card block schema has no field for them
161
+ // (https://docs.slack.dev/reference/block-kit/blocks/card-block) and Slack
162
+ // silently drops unknown card fields, so a dedicated block is needed to
163
+ // surface them to the guardian.
164
+ if (warningsText) {
165
+ blocks.push({
166
+ type: "context",
167
+ elements: [{ type: "mrkdwn", text: warningsText }],
168
+ });
169
+ }
170
+
171
+ const sourceContext = buildSourceContextBlock(view);
219
172
  if (sourceContext) blocks.push(sourceContext);
220
173
 
221
- const idBlock = buildRequesterIdBlock(p);
174
+ const idBlock = buildRequesterIdBlock(view);
222
175
  if (idBlock) blocks.push(idBlock);
223
176
 
224
177
  blocks.push({
@@ -227,16 +180,16 @@ function buildAccessRequestCardBlocks(
227
180
  });
228
181
 
229
182
  if (
230
- (p.guardianResolutionSource === "vellum-anchor" ||
231
- p.guardianResolutionSource === "none") &&
232
- p.sourceChannel
183
+ (view.guardianResolutionSource === "vellum-anchor" ||
184
+ view.guardianResolutionSource === "none") &&
185
+ view.sourceChannel
233
186
  ) {
234
187
  blocks.push({
235
188
  type: "context",
236
189
  elements: [
237
190
  {
238
191
  type: "mrkdwn",
239
- text: `_You haven't verified your identity on ${p.sourceChannel} yet. If this was you trying to message your assistant, say "help me verify as guardian on ${p.sourceChannel}" to set up direct access._`,
192
+ text: `_You haven't verified your identity on ${view.sourceChannel} yet. If this was you trying to message your assistant, say "help me verify as guardian on ${view.sourceChannel}" to set up direct access._`,
240
193
  },
241
194
  ],
242
195
  });
@@ -276,7 +229,7 @@ function buildToolApprovalCardBlocks(
276
229
  const card: Record<string, unknown> = {
277
230
  type: "card",
278
231
  title: {
279
- type: "plain_text",
232
+ type: "mrkdwn",
280
233
  text: details ? "Tool Approval" : "Approval Request",
281
234
  },
282
235
  body: {
@@ -307,14 +260,16 @@ function buildToolApprovalCardBlocks(
307
260
  /**
308
261
  * Build Slack blocks for any notification carrying approval context.
309
262
  * Dispatches to the appropriate card builder based on source event type.
263
+ *
264
+ * Exported for characterization tests of the approval-card block output.
310
265
  */
311
- function buildApprovalNotificationBlocks(
266
+ export function buildApprovalNotificationBlocks(
312
267
  payload: ChannelDeliveryPayload,
313
268
  messageText: string,
314
269
  ): unknown[] {
315
270
  if (
316
271
  payload.sourceEventName === "ingress.access_request" &&
317
- payload.contextPayload != null
272
+ payload.accessRequestContext != null
318
273
  ) {
319
274
  return buildAccessRequestCardBlocks(payload);
320
275
  }
@@ -345,7 +300,7 @@ export class SlackAdapter implements ChannelAdapter {
345
300
  };
346
301
  }
347
302
 
348
- const messageText = resolveSlackMessageText(payload);
303
+ const messageText = resolveMessageText(payload);
349
304
 
350
305
  try {
351
306
  const result = payload.approvalContext
@@ -11,8 +11,6 @@
11
11
  import { sendTelegramReply } from "../../messaging/providers/telegram-bot/send.js";
12
12
  import { ConfigError } from "../../util/errors.js";
13
13
  import { getLogger } from "../../util/logger.js";
14
- import { isConversationSeedSane } from "../conversation-seed-composer.js";
15
- import { nonEmpty } from "../notification-utils.js";
16
14
  import type {
17
15
  ChannelAdapter,
18
16
  ChannelDeliveryPayload,
@@ -20,26 +18,10 @@ import type {
20
18
  DeliveryResult,
21
19
  NotificationChannel,
22
20
  } from "../types.js";
21
+ import { resolveMessageText } from "./shared.js";
23
22
 
24
23
  const log = getLogger("notif-adapter-telegram");
25
24
 
26
- function resolveTelegramMessageText(payload: ChannelDeliveryPayload): string {
27
- const deliveryText = nonEmpty(payload.copy.deliveryText);
28
- if (deliveryText) return deliveryText;
29
-
30
- if (isConversationSeedSane(payload.copy.conversationSeedMessage)) {
31
- return payload.copy.conversationSeedMessage.trim();
32
- }
33
-
34
- const body = nonEmpty(payload.copy.body);
35
- if (body) return body;
36
-
37
- const title = nonEmpty(payload.copy.title);
38
- if (title) return title;
39
-
40
- return payload.sourceEventName.replace(/[._]/g, " ");
41
- }
42
-
43
25
  export class TelegramAdapter implements ChannelAdapter {
44
26
  readonly channel: NotificationChannel = "telegram";
45
27
 
@@ -59,7 +41,7 @@ export class TelegramAdapter implements ChannelAdapter {
59
41
  };
60
42
  }
61
43
 
62
- const messageText = resolveTelegramMessageText(payload);
44
+ const messageText = resolveMessageText(payload);
63
45
  const approval = payload.approvalContext;
64
46
 
65
47
  try {