@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
@@ -5,6 +5,11 @@
5
5
  * invite token redemption.
6
6
  */
7
7
  import type { SourceMetadata } from "@vellumai/gateway-client";
8
+ import {
9
+ ADMISSION_POLICY_DEFAULT,
10
+ type AdmissionPolicy,
11
+ isAdmissionPolicy,
12
+ } from "@vellumai/gateway-client";
8
13
 
9
14
  import {
10
15
  attachmentsToContentBlocks,
@@ -17,6 +22,7 @@ import {
17
22
  isChannelId,
18
23
  parseInterfaceId,
19
24
  } from "../../channels/types.js";
25
+ import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
20
26
  import { getConfig } from "../../config/loader.js";
21
27
  import {
22
28
  createApprovalConversationGenerator,
@@ -53,7 +59,6 @@ import {
53
59
  import {
54
60
  clearPayload,
55
61
  findMessageBySourceId,
56
- linkMessage,
57
62
  recordInbound,
58
63
  } from "../../memory/delivery-crud.js";
59
64
  import { markProcessed } from "../../memory/delivery-status.js";
@@ -78,28 +83,45 @@ import {
78
83
  type SlackMessageMetadata,
79
84
  writeSlackMetadata,
80
85
  } from "../../messaging/providers/slack/message-metadata.js";
86
+ import { MESSAGE_PREVIEW_MAX_LENGTH } from "../../notifications/notification-utils.js";
81
87
  import type { ContentBlock } from "../../providers/types.js";
82
88
  import { wrapUntrustedContent } from "../../security/untrusted-content.js";
83
89
  import { canonicalizeInboundIdentity } from "../../util/canonicalize-identity.js";
84
90
  import { getLogger } from "../../util/logger.js";
91
+ import { truncate } from "../../util/truncate.js";
92
+ import { notifyGuardianOfAccessRequest } from "../access-request-helper.js";
85
93
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
86
94
  import { deliverChannelReply } from "../gateway-client.js";
87
95
  import { resolveTrustContext } from "../trust-context-resolver.js";
88
96
  import { canonicalChannelAssistantId } from "./channel-route-shared.js";
89
97
  import { BadRequestError } from "./errors.js";
90
98
  import { handleApprovalInterception } from "./guardian-approval-interception.js";
91
- import { enforceIngressAcl } from "./inbound-stages/acl-enforcement.js";
99
+ import {
100
+ channelStatusToMemberStatus,
101
+ enforceIngressAcl,
102
+ } from "./inbound-stages/acl-enforcement.js";
103
+ import { enforceAdmissionPolicy } from "./inbound-stages/admission-policy.js";
92
104
  import { processChannelMessageInBackground } from "./inbound-stages/background-dispatch.js";
93
105
  import { handleBootstrapIntercept } from "./inbound-stages/bootstrap-intercept.js";
94
106
  import { handleEditIntercept } from "./inbound-stages/edit-intercept.js";
95
107
  import { handleEscalationIntercept } from "./inbound-stages/escalation-intercept.js";
96
108
  import { handleGuardianActivationIntercept } from "./inbound-stages/guardian-activation-intercept.js";
97
109
  import { handleGuardianReplyIntercept } from "./inbound-stages/guardian-reply-intercept.js";
110
+ import {
111
+ handleSlackReactionIntercept,
112
+ isSlackReactionEvent,
113
+ } from "./inbound-stages/reaction-intercept.js";
98
114
  import { runSecretIngressCheck } from "./inbound-stages/secret-ingress-check.js";
99
115
  import { tryTranscribeAudioAttachments } from "./inbound-stages/transcribe-audio.js";
100
116
  import type { RouteHandlerArgs } from "./types.js";
101
117
 
102
118
  const log = getLogger("runtime-http");
119
+
120
+ // Gates the per-channel admission floor stage. When off, the floor is never
121
+ // enforced and inbound falls back to ACL-only behavior (the gateway also skips
122
+ // attaching a floor when off, so the ACL sees the default permissive policy).
123
+ const CHANNEL_TRUST_FLOORS_FLAG = "channel-trust-floors" as const;
124
+
103
125
  const DISK_PRESSURE_REMOTE_BLOCK_REPLY =
104
126
  "Storage is critically low, so remote messages are ignored until the guardian frees enough space. Please try again later.";
105
127
 
@@ -383,6 +405,57 @@ export async function handleChannelInbound({
383
405
  });
384
406
  if (guardianActivationResponse) return guardianActivationResponse;
385
407
 
408
+ // ── Slack reaction handling ──
409
+ // Reactions are passive channel signals — not messages, and not access
410
+ // attempts. Dispatch them to a dedicated interceptor BEFORE the message
411
+ // pipeline (ACL, admission floor, disk-pressure, conversation binding) so a
412
+ // 👍 never triggers a verification handshake or an access-request
413
+ // notification, and a stranger's reaction creates no conversation/binding.
414
+ // The interceptor drops strangers, records known contacts' reactions as
415
+ // transcript signals, and routes a guardian's reaction on an approval card
416
+ // through the canonical guardian decision pipeline. Reactions never drive an
417
+ // agent turn.
418
+ if (isSlackReactionEvent(body)) {
419
+ return handleSlackReactionIntercept({
420
+ callbackData: body.callbackData!,
421
+ sourceChannel,
422
+ sourceInterface,
423
+ conversationExternalId,
424
+ externalMessageId,
425
+ canonicalAssistantId,
426
+ rawSenderId,
427
+ canonicalSenderId,
428
+ actorDisplayName: body.actorDisplayName,
429
+ actorUsername: body.actorUsername,
430
+ replyCallbackUrl: body.replyCallbackUrl,
431
+ sourceMetadata: body.sourceMetadata,
432
+ slackChannelName,
433
+ approvalConversationGenerator,
434
+ });
435
+ }
436
+
437
+ // ── Admission policy pre-computation ──
438
+ // Resolve the effective policy before ACL so it can skip its hard-deny
439
+ // paths for permissive policies (`strangers`, `any_contact`). The same
440
+ // value is reused by the floor stage below, which is gated on
441
+ // `channel-trust-floors`.
442
+ const channelTrustFloorsEnabled = isAssistantFeatureFlagEnabled(
443
+ CHANNEL_TRUST_FLOORS_FLAG,
444
+ getConfig(),
445
+ );
446
+ const admissionPolicyFromGateway = isAdmissionPolicy(
447
+ sourceMetadata?.admissionPolicy,
448
+ )
449
+ ? (sourceMetadata!.admissionPolicy as AdmissionPolicy)
450
+ : ADMISSION_POLICY_DEFAULT;
451
+ // Pass `undefined` to the ACL when the feature is off so it takes none of
452
+ // its policy-aware bypasses (the floor stage is skipped too). This keeps the
453
+ // flag-off path on the pre-feature ACL behavior and prevents a bypass from
454
+ // routing to a disabled floor stage and admitting unconditionally.
455
+ const effectiveAdmissionPolicyForAcl = channelTrustFloorsEnabled
456
+ ? admissionPolicyFromGateway
457
+ : undefined;
458
+
386
459
  // ── Ingress ACL enforcement ──
387
460
  const aclResult = await enforceIngressAcl({
388
461
  canonicalSenderId,
@@ -398,6 +471,7 @@ export async function handleChannelInbound({
398
471
  replyCallbackUrl: body.replyCallbackUrl,
399
472
  assistantId,
400
473
  externalMessageId,
474
+ effectiveAdmissionPolicy: effectiveAdmissionPolicyForAcl,
401
475
  });
402
476
  if (aclResult.earlyResponse) return aclResult.earlyResponse;
403
477
  const { resolvedMember } = aclResult;
@@ -657,6 +731,144 @@ export async function handleChannelInbound({
657
731
  slackActorTimezone,
658
732
  );
659
733
 
734
+ // ── Admission policy floor ──
735
+ // Sits between trust resolution and the agent loop. The gateway attaches
736
+ // the per-channel-type floor (`sourceMetadata.admissionPolicy`); the
737
+ // runtime evaluates `trustClass ≥ floor`. Denials reuse the same
738
+ // canned-reply / guardian-notify side effects as `not_a_member` (no
739
+ // re-verification challenge — §8.2). The gateway kill switch already
740
+ // dropped `no_one` upstream, but the stage handles it defensively.
741
+ //
742
+ // Internal channels (`vellum`, `platform`, `a2a`) short-circuit admit
743
+ // inside `enforceAdmissionPolicy` — defense in depth alongside the
744
+ // gateway's exempt-channel skip and the PUT-handler's 403.
745
+ //
746
+ // Bootstrap deep-link: when ACL flagged a validated pending_bootstrap
747
+ // session, skip the floor entirely. The bootstrap intercept stage below
748
+ // handles identity binding and emits its own reply; the sender has not
749
+ // yet acquired a trust class and should not be denied here.
750
+ // Gated by `channel-trust-floors`: when off, skip the floor entirely (admit)
751
+ // so inbound falls back to ACL-only behavior. The gateway also omits the
752
+ // floor when off, so the ACL above already saw the default permissive policy.
753
+ const admissionResult =
754
+ !channelTrustFloorsEnabled || aclResult.isValidatedBootstrap
755
+ ? ({ admitted: true } as const)
756
+ : enforceAdmissionPolicy({
757
+ sourceChannel,
758
+ trustClass: trustCtx.trustClass,
759
+ memberStatus: resolvedMember?.channel.status,
760
+ policy: admissionPolicyFromGateway,
761
+ });
762
+ if (!admissionResult.admitted) {
763
+ log.info(
764
+ {
765
+ sourceChannel,
766
+ conversationExternalId,
767
+ eventId: result.eventId,
768
+ trustClass: trustCtx.trustClass,
769
+ reason: admissionResult.reason,
770
+ effectivePolicy: admissionResult.effectivePolicy,
771
+ shouldChallenge: admissionResult.shouldChallenge,
772
+ },
773
+ "Inbound admission policy floor denied",
774
+ );
775
+
776
+ // §8.2 + webhook idempotency: skip guardian-notify + reply side
777
+ // effects on duplicate deliveries (matches the disk-pressure branch
778
+ // below at line ~810). Without this guard, a webhook retry of the
779
+ // same duplicated event that hit the floor re-fires the access
780
+ // request notification and the canned denial reply — visible to the
781
+ // guardian/sender without a re-evaluation.
782
+ if (result.duplicate) {
783
+ return {
784
+ accepted: true,
785
+ duplicate: result.duplicate,
786
+ eventId: result.eventId,
787
+ denied: true,
788
+ reason: admissionResult.reason,
789
+ };
790
+ }
791
+
792
+ // Notify the guardian about the access attempt — same surface as
793
+ // `acl-enforcement.ts:267-449` for `not_a_member`, so denials are
794
+ // visible in the same UI. previousMemberStatus is only meaningful when
795
+ // a member record exists; we pass it through when available so the
796
+ // guardian sees "previously pending" etc.
797
+ let guardianNotified = false;
798
+ try {
799
+ const accessResult = notifyGuardianOfAccessRequest({
800
+ canonicalAssistantId,
801
+ sourceChannel,
802
+ conversationExternalId,
803
+ actorExternalId: canonicalSenderId ?? rawSenderId,
804
+ actorDisplayName: body.actorDisplayName,
805
+ actorUsername: body.actorUsername,
806
+ ...(resolvedMember
807
+ ? {
808
+ previousMemberStatus: channelStatusToMemberStatus(
809
+ resolvedMember.channel.status,
810
+ ),
811
+ }
812
+ : {}),
813
+ messagePreview: truncate(trimmedContent, MESSAGE_PREVIEW_MAX_LENGTH),
814
+ ...(typeof sourceMetadata?.isStranger === "boolean"
815
+ ? { isStranger: sourceMetadata.isStranger }
816
+ : {}),
817
+ ...(typeof sourceMetadata?.isRestricted === "boolean"
818
+ ? { isRestricted: sourceMetadata.isRestricted }
819
+ : {}),
820
+ ...(typeof sourceMetadata?.messageId === "string"
821
+ ? { messageTs: sourceMetadata.messageId }
822
+ : {}),
823
+ });
824
+ guardianNotified = accessResult.notified;
825
+ } catch (err) {
826
+ log.error(
827
+ { err, sourceChannel, conversationExternalId },
828
+ "Failed to notify guardian of access request (admission policy)",
829
+ );
830
+ }
831
+
832
+ // Canned reply mirrors the not_a_member surface. §8.2: no upgrade
833
+ // challenge text for `trusted_contacts` / `guardian_only` denials —
834
+ // sender gets the standard "ask the guardian" copy.
835
+ const replyText = guardianNotified
836
+ ? "Hmm looks like you don't have access to talk to me. I'll let your guardian know you tried."
837
+ : "Sorry, you haven't been approved to message this assistant.";
838
+ let replyDelivered = false;
839
+ if (replyCallbackUrl) {
840
+ const replyPayload: Parameters<typeof deliverChannelReply>[1] = {
841
+ chatId: conversationExternalId,
842
+ text: replyText,
843
+ assistantId: canonicalAssistantId,
844
+ };
845
+ if (sourceChannel === "slack" && (canonicalSenderId ?? rawSenderId)) {
846
+ replyPayload.ephemeral = true;
847
+ replyPayload.user = (canonicalSenderId ?? rawSenderId)!;
848
+ }
849
+ try {
850
+ await deliverChannelReply(replyCallbackUrl, replyPayload);
851
+ replyDelivered = true;
852
+ } catch (err) {
853
+ log.error(
854
+ { err, conversationExternalId },
855
+ "Failed to deliver admission policy denial reply",
856
+ );
857
+ }
858
+ }
859
+
860
+ if (!result.duplicate) markProcessed(result.eventId);
861
+
862
+ return {
863
+ accepted: true,
864
+ duplicate: result.duplicate,
865
+ eventId: result.eventId,
866
+ denied: true,
867
+ reason: admissionResult.reason,
868
+ ...(!replyDelivered && { replyText }),
869
+ };
870
+ }
871
+
660
872
  const diskPressureDecision = classifyDiskPressureTurnPolicy(
661
873
  getDiskPressureStatus(),
662
874
  {
@@ -717,121 +929,6 @@ export async function handleChannelInbound({
717
929
  };
718
930
  }
719
931
 
720
- // ── Slack reaction handling ──
721
- // Reactions arrive as regular `SlackInboundEvent`s with `callbackData`
722
- // prefixed `reaction:` (added) or `reaction_removed:` (removed).
723
- //
724
- // Two paths from here:
725
- // 1. Guardian approval-by-reaction. A `reaction:` (added) event from
726
- // the guardian on an active approval prompt is consumed by
727
- // `handleApprovalInterception` to apply the decision. In that case
728
- // we do NOT persist the reaction as a transcript line — resolved
729
- // guardian approval reactions have no transcript representation.
730
- // 2. All other reactions (non-guardian, no pending approval, stale,
731
- // and any `reaction_removed:` event regardless of actor) fall
732
- // through to `persistSlackReactionAsMessage` so Slack transcript
733
- // rendering can surface them inline. Reactions never trigger an
734
- // agent response, so we short-circuit before escalation and
735
- // agent-loop dispatch in both cases.
736
- if (isSlackReactionEvent(body)) {
737
- // Approval interception runs only for reactions (added) — `reaction_removed`
738
- // never expresses an approval intent, so un-reacting is left as a pure
739
- // transcript signal. Gated by the same `replyCallbackUrl && !duplicate`
740
- // preconditions used by the standard approval interception call below.
741
- const isReactionAdded = body.callbackData?.startsWith("reaction:") === true;
742
- if (isReactionAdded && replyCallbackUrl && !result.duplicate) {
743
- const trustCtxForReaction: TrustContext = attachSlackRequesterTimezone(
744
- resolveTrustContext({
745
- assistantId: canonicalAssistantId,
746
- sourceChannel,
747
- conversationExternalId,
748
- actorExternalId: rawSenderId,
749
- actorUsername: body.actorUsername,
750
- actorDisplayName: body.actorDisplayName,
751
- }),
752
- slackActorTimezone,
753
- );
754
-
755
- const approvalMessageTs =
756
- typeof sourceMetadata?.messageId === "string"
757
- ? sourceMetadata.messageId
758
- : undefined;
759
-
760
- const reactionApprovalResult = await handleApprovalInterception({
761
- conversationId: result.conversationId,
762
- callbackData: body.callbackData,
763
- content: trimmedContent,
764
- conversationExternalId,
765
- sourceChannel,
766
- actorExternalId: canonicalSenderId ?? rawSenderId,
767
- replyCallbackUrl,
768
- trustCtx: trustCtxForReaction,
769
- assistantId: canonicalAssistantId,
770
- approvalCopyGenerator,
771
- approvalConversationGenerator,
772
- approvalMessageTs,
773
- });
774
-
775
- // A real guardian decision was applied — short-circuit and skip the
776
- // reaction-persistence path so we do not double-record it as a
777
- // transcript line. All other interception outcomes (stale_ignored,
778
- // non-guardian, no pending approval) fall through to persistence.
779
- if (reactionApprovalResult.type === "guardian_decision_applied") {
780
- return {
781
- accepted: true,
782
- duplicate: false,
783
- eventId: result.eventId,
784
- approval: reactionApprovalResult.type,
785
- };
786
- }
787
- }
788
-
789
- const reactedMessageTs =
790
- typeof sourceMetadata?.messageId === "string"
791
- ? sourceMetadata.messageId
792
- : undefined;
793
- if (!reactedMessageTs) {
794
- log.debug(
795
- { conversationId: result.conversationId, eventId: result.eventId },
796
- "Skipping reaction persistence: missing sourceMetadata.messageId",
797
- );
798
- return {
799
- accepted: result.accepted,
800
- duplicate: result.duplicate,
801
- eventId: result.eventId,
802
- };
803
- }
804
-
805
- const threadTs =
806
- typeof sourceMetadata?.threadId === "string"
807
- ? sourceMetadata.threadId
808
- : undefined;
809
-
810
- try {
811
- await persistSlackReactionAsMessage({
812
- conversationId: result.conversationId,
813
- conversationExternalId,
814
- eventId: result.eventId,
815
- callbackData: body.callbackData!,
816
- actorDisplayName: body.actorDisplayName,
817
- threadTs,
818
- reactedMessageTs,
819
- duplicate: result.duplicate,
820
- });
821
- } catch (err) {
822
- log.error(
823
- { err, conversationId: result.conversationId, eventId: result.eventId },
824
- "Failed to persist Slack reaction event",
825
- );
826
- }
827
-
828
- return {
829
- accepted: result.accepted,
830
- duplicate: result.duplicate,
831
- eventId: result.eventId,
832
- };
833
- }
834
-
835
932
  // ── Ingress escalation ──
836
933
  const escalationResponse = handleEscalationIntercept({
837
934
  resolvedMember,
@@ -1294,117 +1391,6 @@ export async function handleChannelInbound({
1294
1391
  };
1295
1392
  }
1296
1393
 
1297
- /**
1298
- * Detect a Slack reaction event by inspecting the inbound payload's
1299
- * `callbackData` prefix. The gateway encodes reactions as a unified
1300
- * `SlackInboundEvent` with `callbackData` of the form
1301
- * `reaction:<emoji>` (added) or `reaction_removed:<emoji>` (removed) —
1302
- * see `gateway/src/slack/normalize.ts`. This helper centralizes that
1303
- * convention so the daemon can route reactions to a dedicated persistence
1304
- * branch instead of the agent-response pipeline.
1305
- */
1306
- export function isSlackReactionEvent(body: {
1307
- sourceChannel?: string;
1308
- callbackData?: string;
1309
- }): boolean {
1310
- if (body.sourceChannel !== "slack") return false;
1311
- const cb = body.callbackData;
1312
- if (typeof cb !== "string") return false;
1313
- return cb.startsWith("reaction:") || cb.startsWith("reaction_removed:");
1314
- }
1315
-
1316
- /**
1317
- * Parse a reaction `callbackData` string into its op (added/removed) and
1318
- * emoji name. Returns `null` when the input is not a reaction prefix or
1319
- * when the emoji portion is empty.
1320
- */
1321
- export function parseSlackReactionCallbackData(
1322
- callbackData: string,
1323
- ): { op: "added" | "removed"; emoji: string } | null {
1324
- let op: "added" | "removed";
1325
- let emoji: string;
1326
- if (callbackData.startsWith("reaction_removed:")) {
1327
- op = "removed";
1328
- emoji = callbackData.slice("reaction_removed:".length);
1329
- } else if (callbackData.startsWith("reaction:")) {
1330
- op = "added";
1331
- emoji = callbackData.slice("reaction:".length);
1332
- } else {
1333
- return null;
1334
- }
1335
- if (emoji.length === 0) return null;
1336
- return { op, emoji };
1337
- }
1338
-
1339
- /**
1340
- * Persist a Slack reaction event as a `messages` row with `slackMeta`
1341
- * envelope so the renderer can surface it inline in the chronological
1342
- * transcript. Reactions do not trigger an agent response — the row is
1343
- * written and the inbound event is linked, but the agent loop is not
1344
- * dispatched.
1345
- *
1346
- * The caller is expected to have run `recordInbound` already so that
1347
- * deduplication and conversation resolution have happened. Duplicate
1348
- * inbound events are skipped here to keep persistence idempotent.
1349
- */
1350
- async function persistSlackReactionAsMessage(params: {
1351
- conversationId: string;
1352
- conversationExternalId: string;
1353
- eventId: string;
1354
- callbackData: string;
1355
- actorDisplayName?: string;
1356
- threadTs?: string;
1357
- reactedMessageTs: string;
1358
- duplicate: boolean;
1359
- }): Promise<void> {
1360
- if (params.duplicate) return;
1361
-
1362
- const parsed = parseSlackReactionCallbackData(params.callbackData);
1363
- if (!parsed) {
1364
- log.debug(
1365
- {
1366
- conversationId: params.conversationId,
1367
- callbackData: params.callbackData,
1368
- },
1369
- "Skipping reaction persistence: unparseable callbackData",
1370
- );
1371
- return;
1372
- }
1373
-
1374
- const slackMeta: SlackMessageMetadata = {
1375
- source: "slack",
1376
- channelId: params.conversationExternalId,
1377
- channelTs: params.reactedMessageTs,
1378
- eventKind: "reaction",
1379
- ...(params.threadTs ? { threadTs: params.threadTs } : {}),
1380
- ...(params.actorDisplayName
1381
- ? { displayName: params.actorDisplayName }
1382
- : {}),
1383
- reaction: {
1384
- emoji: parsed.emoji,
1385
- targetChannelTs: params.reactedMessageTs,
1386
- op: parsed.op,
1387
- ...(params.actorDisplayName
1388
- ? { actorDisplayName: params.actorDisplayName }
1389
- : {}),
1390
- },
1391
- };
1392
-
1393
- // Sentinel content — Slack transcript renderers read `slackMeta` to format
1394
- // the reaction line; the literal text is never displayed to the model.
1395
- const persisted = await addMessage(
1396
- params.conversationId,
1397
- "user",
1398
- "[reaction]",
1399
- {
1400
- metadata: { slackMeta: writeSlackMetadata(slackMeta) },
1401
- skipIndexing: true,
1402
- },
1403
- );
1404
- linkMessage(params.eventId, persisted.id);
1405
- markProcessed(params.eventId);
1406
- }
1407
-
1408
1394
  /**
1409
1395
  * Threshold of stored Slack-tagged messages below which a conversation is
1410
1396
  * considered "cold" and eligible for one-shot backfill. The number is