@vellumai/assistant 0.9.0 → 0.10.0-staging.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (572) hide show
  1. package/ARCHITECTURE.md +18 -34
  2. package/bun.lock +7 -8
  3. package/docs/activation-funnel-telemetry.md +28 -22
  4. package/docs/architecture/security.md +29 -28
  5. package/docs/stt-provider-onboarding.md +3 -5
  6. package/docs/workflows-testing.md +13 -44
  7. package/docs/workflows.md +3 -5
  8. package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
  9. package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
  10. package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
  11. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  12. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  13. package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
  14. package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
  15. package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
  16. package/openapi.yaml +976 -63
  17. package/package.json +2 -1
  18. package/scripts/sync-llm-catalog.ts +6 -15
  19. package/scripts/sync-web-search-catalog.ts +3 -11
  20. package/src/__tests__/access-request-card-view.test.ts +98 -0
  21. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  22. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
  23. package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
  24. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  25. package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
  26. package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
  27. package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
  28. package/src/__tests__/app-compiler.test.ts +15 -1
  29. package/src/__tests__/app-dir-path-guard.test.ts +0 -1
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
  32. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  33. package/src/__tests__/avatar-identity-sync.test.ts +2 -27
  34. package/src/__tests__/btw-routes.test.ts +6 -8
  35. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  36. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  37. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  38. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  39. package/src/__tests__/checker.test.ts +0 -3
  40. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  41. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  43. package/src/__tests__/config-loader-backfill.test.ts +268 -27
  44. package/src/__tests__/config-schema.test.ts +35 -0
  45. package/src/__tests__/config-watcher.test.ts +0 -18
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  47. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  48. package/src/__tests__/contacts-tools.test.ts +29 -0
  49. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
  50. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  51. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  52. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  53. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  54. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  55. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  56. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  57. package/src/__tests__/conversation-title-service.test.ts +62 -0
  58. package/src/__tests__/credential-broker.test.ts +449 -1
  59. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  60. package/src/__tests__/credential-execution-tools.test.ts +0 -1
  61. package/src/__tests__/credential-prompt-route.test.ts +4 -4
  62. package/src/__tests__/credential-routes.test.ts +360 -0
  63. package/src/__tests__/credential-security-invariants.test.ts +4 -13
  64. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  65. package/src/__tests__/disk-usage.test.ts +65 -0
  66. package/src/__tests__/dynamic-page-surface.test.ts +152 -1
  67. package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
  68. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  69. package/src/__tests__/gateway-only-guard.test.ts +3 -7
  70. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  71. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  72. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  73. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  74. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  75. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  76. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  77. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  78. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  79. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  80. package/src/__tests__/identity-routes.test.ts +0 -189
  81. package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
  82. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  83. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  84. package/src/__tests__/invite-redemption-service.test.ts +4 -7
  85. package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
  86. package/src/__tests__/llm-catalog-parity.test.ts +30 -23
  87. package/src/__tests__/llm-resolver.test.ts +70 -24
  88. package/src/__tests__/llm-schema.test.ts +1 -0
  89. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  90. package/src/__tests__/mcp-health-check.test.ts +6 -7
  91. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  92. package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
  93. package/src/__tests__/onboarding-persona-write.test.ts +1 -1
  94. package/src/__tests__/path-policy.test.ts +34 -0
  95. package/src/__tests__/persona-resolver.test.ts +49 -14
  96. package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
  97. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  98. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  99. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  100. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  101. package/src/__tests__/reaction-persistence.test.ts +150 -29
  102. package/src/__tests__/registry.test.ts +2 -7
  103. package/src/__tests__/relay-server.test.ts +285 -0
  104. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  105. package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
  106. package/src/__tests__/schedule-routes.test.ts +0 -30
  107. package/src/__tests__/schedule-tools.test.ts +2 -18
  108. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  109. package/src/__tests__/skill-execute-input.test.ts +51 -1
  110. package/src/__tests__/skill-runtime-path.test.ts +2 -3
  111. package/src/__tests__/skills.test.ts +51 -0
  112. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  113. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  114. package/src/__tests__/subagent-tools.test.ts +266 -0
  115. package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
  116. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  117. package/src/__tests__/title-generate-hook.test.ts +100 -3
  118. package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
  119. package/src/__tests__/token-manager.test.ts +519 -0
  120. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  121. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  122. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  123. package/src/__tests__/tool-executor.test.ts +0 -79
  124. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  125. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  126. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
  127. package/src/__tests__/trusted-contact-verification.test.ts +8 -10
  128. package/src/__tests__/twilio-routes.test.ts +81 -1
  129. package/src/__tests__/voice-invite-redemption.test.ts +2 -3
  130. package/src/__tests__/weak-open-model.test.ts +30 -0
  131. package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
  132. package/src/__tests__/workspace-greetings.test.ts +152 -0
  133. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  134. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  135. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  136. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  137. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  138. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  139. package/src/agent/loop.ts +49 -29
  140. package/src/api/README.md +6 -6
  141. package/src/api/events/tool-result.ts +6 -0
  142. package/src/api/events/workflow-completed.ts +53 -0
  143. package/src/api/events/workflow-leaf-finished.ts +38 -0
  144. package/src/api/events/workflow-leaf-started.ts +35 -0
  145. package/src/api/events/workflow-progress.ts +32 -0
  146. package/src/api/events/workflow-started.ts +31 -0
  147. package/src/api/index.ts +40 -0
  148. package/src/api/responses/conversation-message.ts +28 -4
  149. package/src/api/responses/home.ts +26 -4
  150. package/src/api/responses/workflow-journal.ts +53 -0
  151. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  152. package/src/approvals/guardian-decision-primitive.ts +26 -3
  153. package/src/approvals/guardian-request-resolvers.ts +183 -80
  154. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  155. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  156. package/src/calls/call-pointer-messages.ts +10 -4
  157. package/src/calls/channel-admission-reader.ts +104 -0
  158. package/src/calls/guardian-dispatch.ts +17 -45
  159. package/src/calls/media-stream-server.ts +84 -2
  160. package/src/calls/relay-access-wait.ts +1 -1
  161. package/src/calls/relay-server.ts +66 -0
  162. package/src/calls/relay-setup-router.ts +82 -1
  163. package/src/calls/twilio-routes.ts +17 -8
  164. package/src/calls/voice-session-bridge.ts +2 -2
  165. package/src/cli/commands/clients.ts +3 -0
  166. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  167. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  168. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  169. package/src/cli/commands/memory/index.ts +30 -0
  170. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  171. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  172. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  173. package/src/cli/commands/oauth/status.test.ts +36 -0
  174. package/src/cli/commands/oauth/status.ts +23 -3
  175. package/src/cli/commands/plugins.ts +197 -4
  176. package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
  177. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  178. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
  179. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  180. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
  181. package/src/cli/lib/diff-plugin.ts +346 -0
  182. package/src/cli/lib/inspect-plugin.ts +12 -1
  183. package/src/cli/lib/install-from-github.ts +105 -17
  184. package/src/cli/lib/merge-plugin-tree.ts +328 -0
  185. package/src/cli/lib/plugin-fingerprint.ts +14 -0
  186. package/src/cli/lib/plugin-surfaces.ts +104 -0
  187. package/src/cli/lib/upgrade-plugin.ts +298 -10
  188. package/src/cli/program.ts +2 -6
  189. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  190. package/src/config/assistant-feature-flags.ts +22 -7
  191. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  192. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  193. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  194. package/src/config/bundled-skills/subagent/SKILL.md +4 -0
  195. package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
  196. package/src/config/bundled-skills/workflows/SKILL.md +14 -8
  197. package/src/config/bundled-tool-registry.ts +2 -7
  198. package/src/config/call-site-defaults.ts +15 -2
  199. package/src/config/feature-flag-registry.json +46 -31
  200. package/src/config/inference-profile-validation.ts +26 -0
  201. package/src/config/llm-resolver.ts +3 -0
  202. package/src/config/loader.ts +4 -0
  203. package/src/config/memory-v3-gate.ts +11 -0
  204. package/src/config/profile-order.ts +28 -0
  205. package/src/config/schema.ts +8 -6
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  207. package/src/config/schemas/call-site-catalog.ts +7 -0
  208. package/src/config/schemas/channels.ts +11 -0
  209. package/src/config/schemas/elevenlabs.ts +0 -1
  210. package/src/config/schemas/llm.ts +31 -0
  211. package/src/config/schemas/memory-lifecycle.ts +3 -7
  212. package/src/config/schemas/memory-v3.ts +6 -0
  213. package/src/config/schemas/platform.ts +0 -8
  214. package/src/config/schemas/services.ts +18 -0
  215. package/src/config/seed-inference-profiles.ts +109 -44
  216. package/src/config/skills.ts +21 -0
  217. package/src/config/sync-gated-profiles.ts +220 -0
  218. package/src/contacts/contact-store.ts +89 -106
  219. package/src/contacts/contacts-write.ts +5 -22
  220. package/src/contacts/types.ts +0 -1
  221. package/src/context/compactor.ts +88 -54
  222. package/src/context/strip-injections.ts +58 -10
  223. package/src/context/token-estimator.ts +1 -1
  224. package/src/credential-execution/process-manager.ts +55 -14
  225. package/src/credential-execution/prompted-credential.ts +2 -3
  226. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  227. package/src/daemon/config-watcher.ts +0 -4
  228. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  229. package/src/daemon/conversation-agent-loop.ts +114 -22
  230. package/src/daemon/conversation-history.ts +1 -1
  231. package/src/daemon/conversation-lifecycle.ts +3 -5
  232. package/src/daemon/conversation-process.ts +13 -5
  233. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  234. package/src/daemon/conversation-slash.ts +2 -23
  235. package/src/daemon/conversation-surfaces.ts +26 -0
  236. package/src/daemon/conversation-tool-setup.ts +27 -14
  237. package/src/daemon/conversation.ts +66 -14
  238. package/src/daemon/disk-pressure-policy.ts +5 -3
  239. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  240. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  241. package/src/daemon/handlers/config-a2a.ts +0 -2
  242. package/src/daemon/handlers/config-channels.ts +15 -16
  243. package/src/daemon/handlers/config-slack-channel.ts +22 -3
  244. package/src/daemon/handlers/conversations.ts +107 -0
  245. package/src/daemon/host-browser-proxy.ts +41 -0
  246. package/src/daemon/lifecycle.ts +55 -27
  247. package/src/daemon/message-provenance.ts +2 -0
  248. package/src/daemon/message-types/contacts.ts +0 -1
  249. package/src/daemon/message-types/conversations.ts +3 -3
  250. package/src/daemon/message-types/sync.ts +0 -1
  251. package/src/daemon/message-types/web-activity.ts +7 -1
  252. package/src/daemon/message-types/workflows.ts +83 -1
  253. package/src/daemon/orphan-reaper.test.ts +0 -19
  254. package/src/daemon/orphan-reaper.ts +2 -24
  255. package/src/daemon/server.ts +0 -10
  256. package/src/daemon/tool-setup-types.ts +4 -0
  257. package/src/daemon/trust-context.ts +1 -1
  258. package/src/events/tool-audit-listener.ts +2 -2
  259. package/src/home/feed-source-enrichment.test.ts +151 -0
  260. package/src/home/feed-source-enrichment.ts +176 -0
  261. package/src/home/relationship-state.ts +2 -4
  262. package/src/instrument.ts +18 -6
  263. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  264. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  265. package/src/ipc/assistant-server.ts +37 -4
  266. package/src/ipc/gateway-flag-listener.ts +18 -2
  267. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  268. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  269. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  270. package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
  271. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  272. package/src/memory/auth-fallback-events-store.ts +2 -2
  273. package/src/memory/auto-analysis-enqueue.ts +3 -5
  274. package/src/memory/bookmark-crud.ts +1 -2
  275. package/src/memory/canonical-guardian-store.ts +39 -1
  276. package/src/memory/conversation-crud.ts +9 -4
  277. package/src/memory/conversation-key-store.ts +17 -2
  278. package/src/memory/conversation-title-service.ts +64 -7
  279. package/src/memory/db-init.ts +17 -17
  280. package/src/memory/embedding-backend.ts +38 -1
  281. package/src/memory/embedding-billing-breaker.ts +96 -0
  282. package/src/memory/jobs-store.ts +25 -13
  283. package/src/memory/jobs-worker.ts +54 -1
  284. package/src/memory/lifecycle-events-store.ts +2 -2
  285. package/src/memory/memory-retrospective-constants.ts +4 -4
  286. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  287. package/src/memory/memory-retrospective-job.ts +28 -227
  288. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  289. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  290. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  291. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
  292. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  293. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  294. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  295. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  296. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  297. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  298. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  299. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  300. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  301. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
  302. package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
  303. package/src/memory/migrations/index.ts +6 -0
  304. package/src/memory/migrations/run-migrations.ts +41 -0
  305. package/src/memory/migrations/validate-migration-state.ts +1 -1
  306. package/src/memory/onboarding-events-store.ts +3 -3
  307. package/src/memory/schema/contacts.ts +0 -5
  308. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  309. package/src/memory/skill-loaded-events-store.ts +2 -2
  310. package/src/memory/tool-executed-events-store.test.ts +7 -7
  311. package/src/memory/turn-trace-store.test.ts +736 -0
  312. package/src/memory/turn-trace-store.ts +364 -0
  313. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  314. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  315. package/src/memory/v2/consolidation-job.ts +2 -2
  316. package/src/memory/v2/skill-content.ts +25 -7
  317. package/src/memory/v2/skill-store.ts +7 -1
  318. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  319. package/src/memory/v3-eval/eval-packets.ts +546 -0
  320. package/src/messaging/providers/slack/adapter.ts +1 -1
  321. package/src/messaging/providers/slack/api.ts +31 -0
  322. package/src/messaging/providers/slack/send.test.ts +114 -2
  323. package/src/messaging/providers/slack/send.ts +30 -7
  324. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  325. package/src/messaging/providers/slack/withdraw.ts +161 -0
  326. package/src/notifications/AGENTS.md +2 -0
  327. package/src/notifications/access-request-copy.ts +72 -59
  328. package/src/notifications/adapters/shared.ts +29 -0
  329. package/src/notifications/adapters/slack.ts +58 -103
  330. package/src/notifications/adapters/telegram.ts +2 -20
  331. package/src/notifications/approval-card-data.ts +333 -0
  332. package/src/notifications/broadcaster.ts +16 -3
  333. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  334. package/src/notifications/copy-composer.ts +3 -3
  335. package/src/notifications/decision-engine.ts +4 -2
  336. package/src/notifications/destination-resolver.ts +4 -6
  337. package/src/notifications/guardian-question-mode.ts +10 -0
  338. package/src/notifications/home-feed-side-effect.ts +7 -16
  339. package/src/notifications/notification-utils.ts +19 -20
  340. package/src/notifications/signal.ts +79 -43
  341. package/src/notifications/types.ts +98 -121
  342. package/src/oauth/AGENTS.md +5 -24
  343. package/src/permissions/checker.test.ts +51 -0
  344. package/src/permissions/checker.ts +185 -26
  345. package/src/permissions/ipc-risk-types.ts +24 -0
  346. package/src/permissions/question-prompter.test.ts +27 -0
  347. package/src/permissions/question-prompter.ts +4 -0
  348. package/src/platform/client.test.ts +119 -0
  349. package/src/platform/client.ts +66 -0
  350. package/src/platform/consent-cache.test.ts +267 -0
  351. package/src/platform/consent-cache.ts +174 -0
  352. package/src/plugin-api/constants.ts +1 -1
  353. package/src/plugin-api/index.ts +33 -1
  354. package/src/plugin-api/model-profiles.ts +33 -0
  355. package/src/plugin-api/types.ts +50 -2
  356. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  357. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  358. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  359. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  360. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  361. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  362. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  363. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  364. package/src/plugins/defaults/advisor/config.ts +21 -0
  365. package/src/plugins/defaults/advisor/consult.ts +93 -0
  366. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  367. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  368. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  369. package/src/plugins/defaults/advisor/package.json +14 -0
  370. package/src/plugins/defaults/advisor/steering.ts +67 -0
  371. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  372. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  373. package/src/plugins/defaults/index.ts +60 -0
  374. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  375. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  376. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  377. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  378. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  379. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  380. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
  381. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  382. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  384. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  385. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
  386. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  387. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  388. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  389. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
  390. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
  391. package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
  392. package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
  393. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
  394. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  395. package/src/prompts/persona-resolver.ts +14 -4
  396. package/src/prompts/templates/system-sections.ts +7 -2
  397. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  398. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  399. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  400. package/src/providers/atlascloud/client.ts +85 -0
  401. package/src/providers/fetch-provider-catalog.ts +85 -0
  402. package/src/providers/inference/adapter-factory.ts +3 -0
  403. package/src/providers/model-catalog.ts +58 -0
  404. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  405. package/src/providers/openai/chat-completions-provider.ts +7 -0
  406. package/src/providers/openai/responses-provider.ts +10 -0
  407. package/src/providers/provider-send-message.ts +11 -3
  408. package/src/providers/retry.ts +53 -12
  409. package/src/providers/search-provider-catalog.ts +10 -0
  410. package/src/providers/weak-open-model.ts +22 -0
  411. package/src/runtime/AGENTS.md +0 -1
  412. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  413. package/src/runtime/__tests__/client-health.test.ts +44 -0
  414. package/src/runtime/access-request-helper.ts +21 -53
  415. package/src/runtime/actor-trust-resolver.ts +59 -63
  416. package/src/runtime/agent-wake.ts +52 -0
  417. package/src/runtime/assistant-event-hub.ts +18 -4
  418. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  419. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  420. package/src/runtime/btw-sidechain.ts +3 -6
  421. package/src/runtime/capabilities.test.ts +120 -0
  422. package/src/runtime/capabilities.ts +197 -0
  423. package/src/runtime/channel-approval-types.ts +22 -45
  424. package/src/runtime/channel-invite-transports/telegram.ts +4 -4
  425. package/src/runtime/channel-retry-sweep.ts +1 -0
  426. package/src/runtime/channel-verification-service.ts +3 -3
  427. package/src/runtime/client-health.ts +26 -0
  428. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  429. package/src/runtime/effective-capabilities.test.ts +128 -0
  430. package/src/runtime/effective-capabilities.ts +84 -0
  431. package/src/runtime/guardian-reply-router.ts +106 -21
  432. package/src/runtime/invite-redemption-service.ts +9 -25
  433. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  434. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  435. package/src/runtime/pending-interactions.ts +15 -0
  436. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  437. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  438. package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
  439. package/src/runtime/routes/app-routes.ts +1 -1
  440. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
  441. package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
  442. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  443. package/src/runtime/routes/btw-routes.ts +1 -27
  444. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  445. package/src/runtime/routes/client-routes.ts +10 -0
  446. package/src/runtime/routes/contact-routes.ts +31 -8
  447. package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
  448. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  449. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  450. package/src/runtime/routes/conversation-routes.ts +39 -14
  451. package/src/runtime/routes/credential-routes.ts +40 -16
  452. package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
  453. package/src/runtime/routes/events-routes.ts +1 -3
  454. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  455. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  456. package/src/runtime/routes/home-feed-routes.ts +8 -3
  457. package/src/runtime/routes/identity-routes.ts +1 -296
  458. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  459. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
  460. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  461. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  462. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  463. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  464. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  465. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  466. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  467. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  468. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  469. package/src/runtime/routes/index.ts +2 -0
  470. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  471. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  472. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  473. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  474. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  475. package/src/runtime/routes/notification-routes.ts +122 -133
  476. package/src/runtime/routes/platform-routes.ts +2 -2
  477. package/src/runtime/routes/plugins-routes.ts +202 -3
  478. package/src/runtime/routes/schedule-routes.ts +0 -22
  479. package/src/runtime/routes/secret-routes.ts +10 -0
  480. package/src/runtime/routes/surface-action-routes.ts +2 -1
  481. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  482. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  483. package/src/runtime/routes/workflow-routes.test.ts +229 -44
  484. package/src/runtime/routes/workflow-routes.ts +131 -29
  485. package/src/runtime/routes/workspace-greetings.ts +55 -0
  486. package/src/runtime/sync/resource-sync-events.ts +1 -11
  487. package/src/runtime/tool-grant-request-helper.ts +18 -16
  488. package/src/runtime/trust-context-resolver.ts +8 -5
  489. package/src/schedule/inference-profile.ts +2 -14
  490. package/src/schedule/schedule-store.ts +1 -1
  491. package/src/schedule/scheduler-types.ts +5 -1
  492. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  493. package/src/security/secret-patterns.ts +3 -0
  494. package/src/subagent/manager.ts +17 -4
  495. package/src/subagent/types.ts +6 -0
  496. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  497. package/src/telemetry/trace-collection-policy.ts +30 -0
  498. package/src/telemetry/types.ts +89 -0
  499. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  500. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  501. package/src/tools/AGENTS.md +3 -3
  502. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  503. package/src/tools/browser/browser-execution.ts +30 -19
  504. package/src/tools/document/document-tool.ts +2 -3
  505. package/src/tools/executor.ts +5 -3
  506. package/src/tools/host-terminal/host-shell.ts +5 -4
  507. package/src/tools/memory/register.ts +2 -2
  508. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  509. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  510. package/src/tools/network/web-fetch.ts +372 -1
  511. package/src/tools/network/web-search-error.ts +1 -1
  512. package/src/tools/network/web-search.ts +213 -10
  513. package/src/tools/permission-checker.ts +4 -3
  514. package/src/tools/registry.ts +20 -0
  515. package/src/tools/schedule/create.ts +7 -12
  516. package/src/tools/schedule/update.ts +4 -11
  517. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  518. package/src/tools/side-effects.ts +2 -17
  519. package/src/tools/skills/execute.ts +33 -0
  520. package/src/tools/subagent/spawn.ts +61 -12
  521. package/src/tools/terminal/shell.ts +10 -4
  522. package/src/tools/tool-approval-handler.ts +18 -13
  523. package/src/tools/tool-manifest.ts +0 -2
  524. package/src/tools/types.ts +9 -0
  525. package/src/tools/ui-surface/definitions.ts +64 -3
  526. package/src/tools/verification-control-plane-policy.ts +3 -1
  527. package/src/tools/workflows/run-workflow.test.ts +8 -18
  528. package/src/tools/workflows/run-workflow.ts +1 -0
  529. package/src/util/disk-usage.ts +78 -23
  530. package/src/util/platform.ts +10 -3
  531. package/src/watcher/telemetry.ts +2 -2
  532. package/src/workflows/capabilities.ts +2 -3
  533. package/src/workflows/engine.test.ts +175 -1
  534. package/src/workflows/engine.ts +82 -0
  535. package/src/workflows/journal-store.test.ts +70 -0
  536. package/src/workflows/journal-store.ts +18 -3
  537. package/src/workflows/run-manager.test.ts +171 -28
  538. package/src/workflows/run-manager.ts +66 -24
  539. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  540. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  541. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  542. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  543. package/src/workspace/migrations/registry.ts +8 -0
  544. package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
  545. package/src/__tests__/credential-security-e2e.test.ts +0 -362
  546. package/src/__tests__/credential-vault-unit.test.ts +0 -1528
  547. package/src/__tests__/credential-vault.test.ts +0 -1706
  548. package/src/__tests__/identity-intro-cache.test.ts +0 -315
  549. package/src/__tests__/secret-onetime-send.test.ts +0 -182
  550. package/src/cli/commands/__tests__/task.test.ts +0 -914
  551. package/src/cli/commands/task.ts +0 -771
  552. package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
  553. package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
  554. package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
  555. package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
  556. package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
  557. package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
  558. package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
  559. package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
  560. package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
  561. package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
  562. package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
  563. package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
  564. package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
  565. package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
  566. package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
  567. package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
  568. package/src/memory/preloaded-apps.ts +0 -116
  569. package/src/notifications/tool-approval-copy.ts +0 -142
  570. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
  571. package/src/runtime/routes/identity-intro-cache.ts +0 -172
  572. package/src/tools/credentials/vault.ts +0 -712
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Unified resolver + renderer for guardian approval card seed content.
3
+ *
4
+ * `contextPayload` resolves into a typed {@link ApprovalCardData} discriminated
5
+ * union, then renders into Surface `[ui_surface, text]` blocks via the shared
6
+ * {@link buildApprovalCardBlocks}. Access-request and tool-approval
7
+ * notifications share this single entry point, which parses and shapes the raw
8
+ * payload into card data.
9
+ *
10
+ * The Surface card data shape matches `CardSurfaceData` from
11
+ * `daemon/message-types/surfaces.ts`. Actions use the canonical
12
+ * `apr:<requestId>:<action>` callback format consumed by
13
+ * `surface-action-routes.ts` → `processGuardianDecision`.
14
+ */
15
+
16
+ import {
17
+ buildAccessRequestCardView,
18
+ buildAccessRequestContractText,
19
+ parseAccessRequestPayload,
20
+ } from "./access-request-copy.js";
21
+ import {
22
+ type ApprovalCardParams,
23
+ buildApprovalCardBlocks,
24
+ } from "./approval-card-builder.js";
25
+ import {
26
+ buildGuardianRequestCodeInstruction,
27
+ type GuardianQuestionPayload,
28
+ type LenientToolApprovalPayload,
29
+ LenientToolApprovalPayloadSchema,
30
+ parseGuardianQuestionPayload,
31
+ resolveGuardianInstructionModeFromFields,
32
+ resolveGuardianInstructionModeFromPayload,
33
+ } from "./guardian-question-mode.js";
34
+ import { nonEmpty, sanitizeIdentityField } from "./notification-utils.js";
35
+
36
+ // ── Surface ids ──────────────────────────────────────────────────────────────
37
+
38
+ /**
39
+ * `surfaceId` prefixes for the in-app approval cards. The full id is
40
+ * `${prefix}-${requestId}` (see {@link buildApprovalCardBlocks}). These are the
41
+ * single source of truth for the prefix, shared by the card builders below and
42
+ * by {@link approvalCardSurfaceId} so the withdrawal path can recompute the id.
43
+ */
44
+ const ACCESS_REQUEST_SURFACE_PREFIX = "access-request";
45
+ const TOOL_APPROVAL_SURFACE_PREFIX = "tool-approval";
46
+
47
+ /**
48
+ * Resolve the `ui_surface` id for a guardian request's in-app approval card
49
+ * from its kind, or `null` for kinds that never render an approval card.
50
+ *
51
+ * The card is rendered once at notification time; when the request is later
52
+ * resolved from a different surface the withdrawal path recomputes the id here
53
+ * to complete that card. Keeping this beside the builders ensures the two stay
54
+ * in lockstep.
55
+ */
56
+ export function approvalCardSurfaceId(
57
+ kind: string,
58
+ requestId: string,
59
+ ): string | null {
60
+ switch (kind) {
61
+ case "access_request":
62
+ return `${ACCESS_REQUEST_SURFACE_PREFIX}-${requestId}`;
63
+ case "tool_approval":
64
+ case "tool_grant_request":
65
+ case "pending_question":
66
+ return `${TOOL_APPROVAL_SURFACE_PREFIX}-${requestId}`;
67
+ default:
68
+ return null;
69
+ }
70
+ }
71
+
72
+ // ── Typed card data ─────────────────────────────────────────────────────────
73
+
74
+ /** Resolved card data for an access-request notification. */
75
+ export interface AccessRequestCardData {
76
+ kind: "access_request";
77
+ card: ApprovalCardParams;
78
+ }
79
+
80
+ /** Resolved card data for a tool-approval / tool-grant notification. */
81
+ export interface ToolApprovalCardData {
82
+ kind: "tool_approval";
83
+ card: ApprovalCardParams;
84
+ }
85
+
86
+ /**
87
+ * Channel-agnostic approval card content, resolved once from `contextPayload`.
88
+ * The discriminant `kind` lets consumers branch on the approval type without
89
+ * re-parsing the payload.
90
+ */
91
+ export type ApprovalCardData = AccessRequestCardData | ToolApprovalCardData;
92
+
93
+ // ── Access-request resolution ────────────────────────────────────────────────
94
+
95
+ /** Shape the parsed access-request payload into card params via the view model. */
96
+ function resolveAccessRequestCard(
97
+ payload: Record<string, unknown>,
98
+ ): ApprovalCardParams {
99
+ const view = buildAccessRequestCardView(parseAccessRequestPayload(payload));
100
+
101
+ const metadata: Array<{ label: string; value: string }> = [];
102
+
103
+ if (view.username) {
104
+ metadata.push({
105
+ label: "Username",
106
+ value: `@${view.username}`,
107
+ });
108
+ }
109
+
110
+ if (view.sourceChannel === "slack" && view.conversationExternalId) {
111
+ metadata.push({
112
+ label: "Source",
113
+ value: view.isSlackDm
114
+ ? "Slack — Direct message"
115
+ : `Slack — #${view.conversationExternalId}`,
116
+ });
117
+ } else if (view.sourceChannel) {
118
+ metadata.push({ label: "Source", value: view.sourceChannel });
119
+ }
120
+
121
+ const bodyParts: string[] = [];
122
+
123
+ if (view.messagePreview) {
124
+ bodyParts.push(`> "${view.messagePreview}"`);
125
+ }
126
+ for (const w of view.warnings) {
127
+ bodyParts.push(`⚠️ ${w}`);
128
+ }
129
+ if (view.messagePermalink) {
130
+ bodyParts.push(`[View message](${view.messagePermalink})`);
131
+ }
132
+
133
+ const body =
134
+ bodyParts.length > 0
135
+ ? bodyParts.join("\n\n")
136
+ : "No additional context available.";
137
+
138
+ return {
139
+ surfaceIdPrefix: ACCESS_REQUEST_SURFACE_PREFIX,
140
+ cardTitle: "Access Request",
141
+ requesterName: view.displayName,
142
+ subtitle: "Requesting access to the assistant",
143
+ body,
144
+ metadata,
145
+ requestId: view.requestId,
146
+ fallbackText: buildAccessRequestContractText(payload),
147
+ };
148
+ }
149
+
150
+ // ── Tool-approval resolution ─────────────────────────────────────────────────
151
+
152
+ /**
153
+ * Determine whether a typed guardian.question payload represents a tool
154
+ * approval (as opposed to a free-text answer). Uses the canonical mode
155
+ * resolver in `guardian-question-mode.ts` — the single source of truth
156
+ * for the requestKind → instructionMode mapping.
157
+ */
158
+ function isToolApprovalPayload(payload: GuardianQuestionPayload): boolean {
159
+ const { mode } = resolveGuardianInstructionModeFromPayload(payload);
160
+ return mode === "approval" && payload.requestKind !== "access_request";
161
+ }
162
+
163
+ /**
164
+ * Lenient approval detection for partially-constructed payloads that don't
165
+ * satisfy the strict discriminated union schema.
166
+ */
167
+ function isLenientToolApproval(payload: LenientToolApprovalPayload): boolean {
168
+ const modeResolution = resolveGuardianInstructionModeFromFields(
169
+ payload.requestKind,
170
+ payload.toolName,
171
+ );
172
+ if (!modeResolution) return false;
173
+ return (
174
+ modeResolution.mode === "approval" &&
175
+ payload.requestKind !== "access_request"
176
+ );
177
+ }
178
+
179
+ /** Shape a tool-approval/grant payload (strict or lenient) into card params. */
180
+ function extractToolApprovalCard(
181
+ p: GuardianQuestionPayload | LenientToolApprovalPayload,
182
+ ): ApprovalCardParams {
183
+ const toolName =
184
+ ("toolName" in p ? nonEmpty(p.toolName) : undefined) ?? "unknown tool";
185
+ const rawRequester = nonEmpty(p.requesterIdentifier);
186
+ const requester = rawRequester
187
+ ? sanitizeIdentityField(rawRequester)
188
+ : "Someone";
189
+
190
+ const isGrant = p.requestKind === "tool_grant_request";
191
+
192
+ const metadata: Array<{ label: string; value: string }> = [];
193
+ metadata.push({ label: "Tool", value: toolName });
194
+ const sourceChannel = nonEmpty(p.sourceChannel);
195
+ if (sourceChannel) {
196
+ metadata.push({ label: "Source", value: sourceChannel });
197
+ }
198
+
199
+ const body = p.questionText
200
+ ? `> ${p.questionText}`
201
+ : "No additional context available.";
202
+
203
+ // Fallback text with request-code instructions for older clients.
204
+ const baseFallback =
205
+ p.questionText ?? `${requester} is requesting approval to use ${toolName}`;
206
+ let fallbackText = baseFallback;
207
+ const requestCode = nonEmpty(p.requestCode);
208
+ if (requestCode) {
209
+ const modeResolution = resolveGuardianInstructionModeFromFields(
210
+ p.requestKind,
211
+ "toolName" in p ? (p.toolName ?? undefined) : undefined,
212
+ );
213
+ const mode = modeResolution?.mode ?? "approval";
214
+ const instruction = buildGuardianRequestCodeInstruction(
215
+ requestCode.trim().toUpperCase(),
216
+ mode,
217
+ );
218
+ fallbackText = `${baseFallback}\n\n${instruction}`;
219
+ }
220
+
221
+ return {
222
+ surfaceIdPrefix: TOOL_APPROVAL_SURFACE_PREFIX,
223
+ cardTitle: isGrant ? "Tool Grant Request" : "Tool Approval",
224
+ requesterName: requester,
225
+ subtitle: isGrant
226
+ ? "Requesting permission to use this tool"
227
+ : "Requesting approval to run this tool",
228
+ body,
229
+ metadata,
230
+ requestId: nonEmpty(p.requestId),
231
+ fallbackText,
232
+ };
233
+ }
234
+
235
+ /**
236
+ * Resolve a guardian.question payload into tool-approval card params, or
237
+ * `null` when it does not represent a tool approval.
238
+ *
239
+ * Tries strict Zod parsing first (full discriminated union), then falls back
240
+ * to lenient field extraction so cards still render when optional fields are
241
+ * absent.
242
+ */
243
+ function resolveToolApprovalCard(
244
+ payload: Record<string, unknown>,
245
+ ): ApprovalCardParams | null {
246
+ const strict = parseGuardianQuestionPayload(payload);
247
+ if (strict) {
248
+ if (!isToolApprovalPayload(strict)) return null;
249
+ return extractToolApprovalCard(strict);
250
+ }
251
+
252
+ const lenient = LenientToolApprovalPayloadSchema.safeParse(payload);
253
+ if (!lenient.success) return null;
254
+ if (!isLenientToolApproval(lenient.data)) return null;
255
+ return extractToolApprovalCard(lenient.data);
256
+ }
257
+
258
+ // ── Unified dispatcher + renderer ────────────────────────────────────────────
259
+
260
+ /**
261
+ * Resolve a notification's `contextPayload` into typed {@link ApprovalCardData}
262
+ * based on its source event, or `null` when the signal does not carry a
263
+ * renderable approval card. Single entry point so each consumer (copy
264
+ * composition, decision engine) shapes the payload identically.
265
+ */
266
+ export function resolveApprovalCardData(
267
+ sourceEventName: string,
268
+ contextPayload: Record<string, unknown> | undefined,
269
+ ): ApprovalCardData | null {
270
+ if (!contextPayload) return null;
271
+
272
+ if (sourceEventName === "ingress.access_request") {
273
+ return {
274
+ kind: "access_request",
275
+ card: resolveAccessRequestCard(contextPayload),
276
+ };
277
+ }
278
+
279
+ if (sourceEventName === "guardian.question") {
280
+ const card = resolveToolApprovalCard(contextPayload);
281
+ return card ? { kind: "tool_approval", card } : null;
282
+ }
283
+
284
+ return null;
285
+ }
286
+
287
+ /** Render resolved approval card data into Surface `[ui_surface, text]` blocks. */
288
+ export function renderApprovalCardData(data: ApprovalCardData): unknown[] {
289
+ return buildApprovalCardBlocks(data.card);
290
+ }
291
+
292
+ // ── Public seed-content builders ─────────────────────────────────────────────
293
+
294
+ /**
295
+ * Build structured content blocks for an access-request notification seed
296
+ * message. Produces a `ui_surface` card block that the web/macOS/iOS apps
297
+ * render as an interactive card via `SurfaceRouter → CardSurface`, plus a
298
+ * plain-text fallback block for search, CLI display, and backward-compatible
299
+ * clients that don't support surfaces.
300
+ */
301
+ export function buildAccessRequestSeedContentBlocks(
302
+ payload: Record<string, unknown>,
303
+ ): unknown[] {
304
+ return renderApprovalCardData({
305
+ kind: "access_request",
306
+ card: resolveAccessRequestCard(payload),
307
+ });
308
+ }
309
+
310
+ /**
311
+ * Build structured content blocks for a tool approval/grant notification seed
312
+ * message. Returns `null` when the payload does not represent a tool approval.
313
+ *
314
+ * Accepts both strict `GuardianQuestionPayload` (Zod-validated) and raw
315
+ * `Record<string, unknown>` payloads. For raw payloads, attempts strict
316
+ * parsing first, then falls back to lenient field extraction so cards
317
+ * still render when optional fields are absent.
318
+ */
319
+ export function buildToolApprovalSeedContentBlocks(
320
+ payload: GuardianQuestionPayload,
321
+ ): unknown[] | null;
322
+ export function buildToolApprovalSeedContentBlocks(
323
+ payload: Record<string, unknown>,
324
+ ): unknown[] | null;
325
+ export function buildToolApprovalSeedContentBlocks(
326
+ payload: GuardianQuestionPayload | Record<string, unknown>,
327
+ ): unknown[] | null {
328
+ const data = resolveApprovalCardData(
329
+ "guardian.question",
330
+ payload as Record<string, unknown>,
331
+ );
332
+ return data ? renderApprovalCardData(data) : null;
333
+ }
@@ -14,7 +14,10 @@ import { v4 as uuid } from "uuid";
14
14
  import { getConversation } from "../memory/conversation-crud.js";
15
15
  import type { ApprovalUIMetadata } from "../runtime/channel-approval-types.js";
16
16
  import { getLogger } from "../util/logger.js";
17
- import { buildAccessRequestContractText } from "./access-request-copy.js";
17
+ import {
18
+ buildAccessRequestContractText,
19
+ parseAccessRequestPayload,
20
+ } from "./access-request-copy.js";
18
21
  import { isGuardianSensitiveEvent } from "./adapters/macos.js";
19
22
  import { pairDeliveryWithConversation } from "./conversation-pairing.js";
20
23
  import { composeFallbackCopy } from "./copy-composer.js";
@@ -80,11 +83,15 @@ function resolveApprovalContext(
80
83
  // Extract tool context so channel adapters can render structured
81
84
  // approval cards without re-parsing contextPayload.
82
85
  let toolName: string | undefined;
86
+ let riskLevel: string | undefined;
87
+ let commandPreview: string | undefined;
83
88
  if (
84
89
  parsed.requestKind === "tool_approval" ||
85
90
  parsed.requestKind === "tool_grant_request"
86
91
  ) {
87
92
  toolName = nonEmpty(parsed.toolName);
93
+ riskLevel = nonEmpty(parsed.riskLevel);
94
+ commandPreview = nonEmpty(parsed.commandPreview);
88
95
  } else if (parsed.requestKind === "pending_question") {
89
96
  toolName = nonEmpty(parsed.toolName);
90
97
  }
@@ -96,8 +103,8 @@ function resolveApprovalContext(
96
103
  permissionDetails: toolName
97
104
  ? {
98
105
  toolName,
99
- riskLevel: "medium",
100
- toolInput: {},
106
+ riskLevel: riskLevel ?? "medium",
107
+ toolInput: commandPreview ? { _summary: commandPreview } : {},
101
108
  requesterIdentifier: nonEmpty(parsed.requesterIdentifier),
102
109
  }
103
110
  : undefined,
@@ -181,6 +188,11 @@ export class NotificationBroadcaster {
181
188
  > | null = null;
182
189
 
183
190
  const approvalContext = resolveApprovalContext(signal);
191
+ const accessRequestContext =
192
+ signal.sourceEventName === "ingress.access_request" &&
193
+ signal.contextPayload
194
+ ? parseAccessRequestPayload(signal.contextPayload)
195
+ : undefined;
184
196
  const results: NotificationDeliveryResult[] = [];
185
197
 
186
198
  for (const channel of orderedChannels) {
@@ -411,6 +423,7 @@ export class NotificationBroadcaster {
411
423
  contextPayload: signal.contextPayload,
412
424
  urgency: signal.attentionHints.urgency,
413
425
  approvalContext,
426
+ accessRequestContext,
414
427
  };
415
428
 
416
429
  // Compute conversation decision audit fields for the delivery record
@@ -0,0 +1,139 @@
1
+ /**
2
+ * The single sink for the per-(request, surface) `canonical_guardian_deliveries`
3
+ * registry.
4
+ *
5
+ * Guardian-request producers (access requests, tool approvals, tool-grant
6
+ * escalations, voice questions, trusted-contact confirmations) emit a
7
+ * notification signal and then record one canonical delivery row per surface the
8
+ * card reached. Capturing the surface address here — the conversation id for the
9
+ * in-app vellum card, or the channel-native message id (e.g. Slack `ts`) for a
10
+ * channel card — is what lets a delivered card be addressed back to its request
11
+ * later:
12
+ *
13
+ * - to withdraw it in place when the request resolves
14
+ * (`approvals/guardian-card-withdrawal.ts`), and
15
+ * - to resolve an emoji reaction on the card to the right request when several
16
+ * are pending in the same chat
17
+ * (`getPendingCanonicalRequestByDestinationMessage`).
18
+ *
19
+ * Every producer records through here so the addressing convention lives in one
20
+ * place and cannot drift between the path that writes the row and the paths that
21
+ * read it back.
22
+ */
23
+
24
+ import {
25
+ type CanonicalGuardianDelivery,
26
+ createCanonicalGuardianDelivery,
27
+ updateCanonicalGuardianDelivery,
28
+ } from "../memory/canonical-guardian-store.js";
29
+ import { getLogger } from "../util/logger.js";
30
+ import type { NotificationDeliveryResult } from "./types.js";
31
+
32
+ const log = getLogger("canonical-delivery-recorder");
33
+
34
+ /**
35
+ * Where an approval card was delivered. Exactly one addressing modality is
36
+ * meaningful per channel:
37
+ *
38
+ * - vellum (in-app): addressed by `conversationId` + the card's surface id.
39
+ * - channels (slack/telegram/...): addressed by `chatId` + the channel-native
40
+ * `messageId` (Slack `ts`).
41
+ */
42
+ export interface ApprovalCardDeliveryAddress {
43
+ requestId: string;
44
+ channel: string;
45
+ /** In-app vellum addressing: the conversation the card was posted to. */
46
+ conversationId?: string;
47
+ /** Channel addressing: the chat the card was delivered to. */
48
+ chatId?: string;
49
+ /** Channel-native message id (e.g. Slack `ts`) — the reaction/withdrawal key. */
50
+ messageId?: string;
51
+ /** Initial delivery status (defaults to "pending"). */
52
+ status?: string;
53
+ }
54
+
55
+ /**
56
+ * Record where an approval card was delivered so it can be located later.
57
+ *
58
+ * Best-effort: a recording failure must never be mistaken for a delivery
59
+ * failure (which, in the prompt path, would trigger a fallback re-post), so
60
+ * errors are swallowed and logged and `null` is returned. Callers that need the
61
+ * row id (to apply a status afterwards) must null-check the result.
62
+ */
63
+ export function recordApprovalCardDelivery(
64
+ address: ApprovalCardDeliveryAddress,
65
+ ): CanonicalGuardianDelivery | null {
66
+ try {
67
+ return createCanonicalGuardianDelivery({
68
+ requestId: address.requestId,
69
+ destinationChannel: address.channel,
70
+ destinationConversationId: address.conversationId,
71
+ destinationChatId: address.chatId,
72
+ destinationMessageId: address.messageId,
73
+ ...(address.status ? { status: address.status } : {}),
74
+ });
75
+ } catch (err) {
76
+ log.error(
77
+ { err, requestId: address.requestId, channel: address.channel },
78
+ "Failed to record approval card delivery; reaction/withdrawal on this card will not resolve",
79
+ );
80
+ return null;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Record every delivery for a guardian request from a notification signal's
86
+ * results, persisting each delivery's terminal status.
87
+ *
88
+ * This is the shared post-broadcast recording loop for the signal-based
89
+ * producers. The vellum row is normally created up front in the signal's
90
+ * `onConversationCreated` callback so the in-app client sees it immediately; pass
91
+ * that row's id as `vellumDeliveryId` and it is reused (only its status applied)
92
+ * — otherwise the vellum row is created here from the result.
93
+ *
94
+ * The pipeline result carries the delivered address directly: a `vellum` result
95
+ * carries a `conversationId`; channel results carry the chat (`destination`) and
96
+ * channel-native id (`messageId`). A blank `destination` is recorded as unknown
97
+ * rather than persisting the literal channel name as a chat id. Status is
98
+ * diagnostic — the read paths key off addressing, not status.
99
+ *
100
+ * Returns the vellum delivery id (passed in, or created here) so a caller can
101
+ * record its own "pipeline produced no vellum delivery" fallback.
102
+ */
103
+ export function recordGuardianRequestDeliveries(params: {
104
+ requestId: string;
105
+ deliveryResults: NotificationDeliveryResult[];
106
+ vellumDeliveryId?: string;
107
+ }): string | undefined {
108
+ const { requestId, deliveryResults } = params;
109
+ let vellumDeliveryId = params.vellumDeliveryId;
110
+
111
+ for (const result of deliveryResults) {
112
+ let deliveryId: string | undefined;
113
+ if (result.channel === "vellum") {
114
+ if (!vellumDeliveryId) {
115
+ vellumDeliveryId = recordApprovalCardDelivery({
116
+ requestId,
117
+ channel: "vellum",
118
+ conversationId: result.conversationId,
119
+ })?.id;
120
+ }
121
+ deliveryId = vellumDeliveryId;
122
+ } else {
123
+ deliveryId = recordApprovalCardDelivery({
124
+ requestId,
125
+ channel: result.channel,
126
+ chatId: result.destination.length > 0 ? result.destination : undefined,
127
+ messageId: result.messageId,
128
+ })?.id;
129
+ }
130
+
131
+ if (deliveryId) {
132
+ updateCanonicalGuardianDelivery(deliveryId, {
133
+ status: result.status === "sent" ? "sent" : "failed",
134
+ });
135
+ }
136
+ }
137
+
138
+ return vellumDeliveryId;
139
+ }
@@ -9,10 +9,11 @@
9
9
  * values from the context payload.
10
10
  */
11
11
 
12
+ import { buildAccessRequestContractText } from "./access-request-copy.js";
12
13
  import {
13
- buildAccessRequestContractText,
14
14
  buildAccessRequestSeedContentBlocks,
15
- } from "./access-request-copy.js";
15
+ buildToolApprovalSeedContentBlocks,
16
+ } from "./approval-card-data.js";
16
17
  import {
17
18
  buildGuardianRequestCodeInstruction,
18
19
  parseGuardianQuestionPayload,
@@ -28,7 +29,6 @@ import type {
28
29
  NotificationSignal,
29
30
  NotificationSourceEventName,
30
31
  } from "./signal.js";
31
- import { buildToolApprovalSeedContentBlocks } from "./tool-approval-copy.js";
32
32
  import type { NotificationChannel, RenderedChannelCopy } from "./types.js";
33
33
 
34
34
  type CopyTemplate = (payload: Record<string, unknown>) => RenderedChannelCopy;
@@ -26,10 +26,13 @@ import { truncate } from "../util/truncate.js";
26
26
  import {
27
27
  buildAccessRequestContractText,
28
28
  buildAccessRequestInviteDirective,
29
- buildAccessRequestSeedContentBlocks,
30
29
  hasAccessRequestInstructions,
31
30
  hasInviteFlowDirective,
32
31
  } from "./access-request-copy.js";
32
+ import {
33
+ buildAccessRequestSeedContentBlocks,
34
+ buildToolApprovalSeedContentBlocks,
35
+ } from "./approval-card-data.js";
33
36
  import {
34
37
  buildConversationCandidates,
35
38
  type ConversationCandidateSet,
@@ -46,7 +49,6 @@ import {
46
49
  import { nonEmpty, readPayloadString } from "./notification-utils.js";
47
50
  import { getPreferenceSummary } from "./preference-summary.js";
48
51
  import type { NotificationSignal, RoutingIntent } from "./signal.js";
49
- import { buildToolApprovalSeedContentBlocks } from "./tool-approval-copy.js";
50
52
  import type {
51
53
  ConversationAction,
52
54
  NotificationChannel,
@@ -70,13 +70,12 @@ export function resolveDestinations(
70
70
  channel: channel as NotificationChannel,
71
71
  endpoint: externalChatId,
72
72
  metadata: {
73
- externalUserId: guardianResult.channel.externalUserId,
73
+ externalUserId: guardianResult.channel.address,
74
74
  },
75
75
  bindingContext: {
76
76
  sourceChannel: channel as NotificationChannel,
77
77
  externalChatId,
78
- externalUserId:
79
- guardianResult.channel.externalUserId ?? undefined,
78
+ externalUserId: guardianResult.channel.address,
80
79
  },
81
80
  });
82
81
  }
@@ -101,13 +100,12 @@ export function resolveDestinations(
101
100
  channel: "slack",
102
101
  endpoint: chatId,
103
102
  metadata: {
104
- externalUserId: guardianResult.channel.externalUserId,
103
+ externalUserId: guardianResult.channel.address,
105
104
  },
106
105
  bindingContext: {
107
106
  sourceChannel: "slack",
108
107
  externalChatId: chatId,
109
- externalUserId:
110
- guardianResult.channel.externalUserId ?? undefined,
108
+ externalUserId: guardianResult.channel.address,
111
109
  },
112
110
  });
113
111
  } else if (guardianResult && chatId) {
@@ -82,11 +82,19 @@ export const ToolApprovalPayloadSchema =
82
82
  GuardianQuestionPayloadBaseSchema.extend({
83
83
  requestKind: z.literal("tool_approval"),
84
84
  toolName: z.string().min(1),
85
+ /** Risk classification from the permission checker (e.g. "low", "medium", "high"). */
86
+ riskLevel: z.string().optional(),
87
+ /** Secret-redacted summary of the tool invocation arguments. */
88
+ commandPreview: z.string().optional(),
85
89
  });
86
90
 
87
91
  export const ToolGrantPayloadSchema = GuardianQuestionPayloadBaseSchema.extend({
88
92
  requestKind: z.literal("tool_grant_request"),
89
93
  toolName: z.string().min(1),
94
+ /** Risk classification from the permission checker (e.g. "low", "medium", "high"). */
95
+ riskLevel: z.string().optional(),
96
+ /** Secret-redacted summary of the tool invocation arguments. */
97
+ commandPreview: z.string().optional(),
90
98
  });
91
99
 
92
100
  export const AccessRequestGuardianPayloadSchema =
@@ -124,6 +132,8 @@ export const LenientToolApprovalPayloadSchema = z.object({
124
132
  requesterIdentifier: z.string().nullable().optional(),
125
133
  requesterExternalUserId: z.string().nullable().optional(),
126
134
  requesterChatId: z.string().nullable().optional(),
135
+ riskLevel: z.string().nullable().optional(),
136
+ commandPreview: z.string().nullable().optional(),
127
137
  });
128
138
 
129
139
  export type LenientToolApprovalPayload = z.infer<