@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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.9.0",
3
+ "version": "0.10.0-staging.2",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -53,6 +53,7 @@
53
53
  "@vellumai/twilio-client": "file:../packages/twilio-client",
54
54
  "commander": "13.1.0",
55
55
  "croner": "10.0.1",
56
+ "diff": "8.0.4",
56
57
  "dotenv": "17.3.1",
57
58
  "drizzle-orm": "0.45.2",
58
59
  "jszip": "3.10.1",
@@ -3,19 +3,13 @@
3
3
  * Generate `llm-provider-catalog.json` from the canonical
4
4
  * `PROVIDER_CATALOG` in `assistant/src/providers/model-catalog.ts`.
5
5
  *
6
- * The JSON file is the client-facing catalog bundled into native clients
7
- * (macOS, web). Keeping it generated rather than hand-mirrored — eliminates
8
- * the recurring "I edited model-catalog.ts and forgot the JSON" failure mode
9
- * that the parity test only catches after push.
6
+ * The JSON file is the client-facing catalog. Keeping it generated — rather
7
+ * than hand-mirrored eliminates the recurring "I edited model-catalog.ts and
8
+ * forgot the JSON" failure mode that the parity test only catches after push.
10
9
  *
11
- * Two byte-identical copies are written:
10
+ * Output:
12
11
  * - `meta/llm-provider-catalog.json` — primary checked-in artifact, read
13
- * by web codegen (§D) and any non-Swift consumer.
14
- * - `clients/shared/Resources/llm-provider-catalog.json` — SwiftPM resource
15
- * bundled into `VellumAssistantShared`. SwiftPM cannot reach files
16
- * outside a target's source directory, so this mirror is necessary;
17
- * both files are produced by the same generator and asserted equal by
18
- * the parity test, making drift impossible.
12
+ * by web codegen (§D) and any downstream consumer.
19
13
  *
20
14
  * The projection drops daemon-only fields (today: `apiKeyUrl`, which clients
21
15
  * read from `credentialsGuide.url` instead) and pins field order so the
@@ -37,10 +31,7 @@ import {
37
31
  } from "../src/providers/model-catalog.js";
38
32
 
39
33
  const ROOT = resolve(import.meta.dir, "../..");
40
- const OUTPUT_PATHS = [
41
- join(ROOT, "meta/llm-provider-catalog.json"),
42
- join(ROOT, "clients/shared/Resources/llm-provider-catalog.json"),
43
- ] as const;
34
+ const OUTPUT_PATHS = [join(ROOT, "meta/llm-provider-catalog.json")] as const;
44
35
 
45
36
  /**
46
37
  * Bumped when the *shape* of the client catalog JSON changes in a way native
@@ -4,7 +4,7 @@
4
4
  * canonical `SEARCH_PROVIDER_CATALOG` in
5
5
  * `assistant/src/providers/search-provider-catalog.ts`.
6
6
  *
7
- * Two byte-identical copies are written:
7
+ * Output:
8
8
  * - `meta/web-search-provider-catalog.json` — primary checked-in artifact,
9
9
  * consumed by:
10
10
  * - `cli/src/__tests__/search-provider-env-var-parity.test.ts`
@@ -12,13 +12,8 @@
12
12
  * - Downstream `vellum-assistant-platform/web/src/lib/generated/
13
13
  * web-search-provider-catalog.json` (manually sync'd today; the
14
14
  * scheduled sync workflow is a planned follow-up).
15
- * - `clients/shared/Resources/web-search-provider-catalog.json` — SwiftPM
16
- * resource bundled into `VellumAssistantShared`. SwiftPM cannot reach
17
- * files outside a target's source directory, so this mirror is
18
- * necessary; both files are produced by the same generator and
19
- * asserted equal by the parity test, making drift impossible.
20
15
  *
21
- * Companion to `sync-llm-catalog.ts`; same dual-write pattern.
16
+ * Companion to `sync-llm-catalog.ts`.
22
17
  *
23
18
  * Usage:
24
19
  * cd assistant && bun run scripts/sync-web-search-catalog.ts
@@ -37,7 +32,6 @@ import {
37
32
  const ROOT = resolve(import.meta.dir, "../..");
38
33
  const OUTPUT_PATHS = [
39
34
  join(ROOT, "meta/web-search-provider-catalog.json"),
40
- join(ROOT, "clients/shared/Resources/web-search-provider-catalog.json"),
41
35
  ] as const;
42
36
 
43
37
  /**
@@ -108,9 +102,7 @@ async function main(): Promise<void> {
108
102
  continue;
109
103
  }
110
104
  if (existing !== next) {
111
- console.error(
112
- `${rel} is stale. Run: bun run sync:web-search-catalog`,
113
- );
105
+ console.error(`${rel} is stale. Run: bun run sync:web-search-catalog`);
114
106
  anyStale = true;
115
107
  continue;
116
108
  }
@@ -0,0 +1,98 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import {
4
+ buildAccessRequestCardView,
5
+ parseAccessRequestPayload,
6
+ } from "../notifications/access-request-copy.js";
7
+
8
+ function view(raw: Record<string, unknown>) {
9
+ return buildAccessRequestCardView(parseAccessRequestPayload(raw));
10
+ }
11
+
12
+ const TAB = String.fromCharCode(9);
13
+
14
+ describe("buildAccessRequestCardView", () => {
15
+ test("prefers actorDisplayName, falls back to senderIdentifier then 'Someone'", () => {
16
+ expect(
17
+ view({ actorDisplayName: "Alice", senderIdentifier: "U999" }).displayName,
18
+ ).toBe("Alice");
19
+ expect(view({ senderIdentifier: "U999" }).displayName).toBe("U999");
20
+ expect(view({}).displayName).toBe("Someone");
21
+ });
22
+
23
+ test("sanitizes identity fields (strips control characters)", () => {
24
+ const v = view({
25
+ actorDisplayName: `Al${TAB}ice`,
26
+ actorUsername: `a${TAB}lice`,
27
+ actorExternalId: `U9${TAB}99`,
28
+ });
29
+ expect(v.displayName).toBe("Al ice");
30
+ expect(v.username).toBe("a lice");
31
+ expect(v.externalId).toBe("U9 99");
32
+ });
33
+
34
+ test("username and externalId are undefined when absent", () => {
35
+ const v = view({ actorDisplayName: "Alice" });
36
+ expect(v.username).toBeUndefined();
37
+ expect(v.externalId).toBeUndefined();
38
+ });
39
+
40
+ test("detects Slack DM conversations", () => {
41
+ expect(
42
+ view({ sourceChannel: "slack", conversationExternalId: "D01XYZ" })
43
+ .isSlackDm,
44
+ ).toBe(true);
45
+ expect(
46
+ view({ sourceChannel: "slack", conversationExternalId: "C01ABC" })
47
+ .isSlackDm,
48
+ ).toBe(false);
49
+ expect(
50
+ view({ sourceChannel: "telegram", conversationExternalId: "D01XYZ" })
51
+ .isSlackDm,
52
+ ).toBe(false);
53
+ });
54
+
55
+ test("builds a Slack permalink only with slack source + conversation + ts", () => {
56
+ expect(
57
+ view({
58
+ sourceChannel: "slack",
59
+ conversationExternalId: "C01ABC",
60
+ messageTs: "1700000000.000100",
61
+ }).messagePermalink,
62
+ ).toBe("https://slack.com/archives/C01ABC/p1700000000000100");
63
+ expect(
64
+ view({ sourceChannel: "slack", conversationExternalId: "C01ABC" })
65
+ .messagePermalink,
66
+ ).toBeUndefined();
67
+ expect(
68
+ view({
69
+ sourceChannel: "telegram",
70
+ conversationExternalId: "C01ABC",
71
+ messageTs: "1.2",
72
+ }).messagePermalink,
73
+ ).toBeUndefined();
74
+ });
75
+
76
+ test("sanitizes message preview and yields undefined when blank after sanitizing", () => {
77
+ expect(view({ messagePreview: " hello " }).messagePreview).toBe("hello");
78
+ // Blank / control-character-only previews sanitize to empty → undefined
79
+ // (no empty quote block is rendered downstream).
80
+ expect(view({ messagePreview: "" }).messagePreview).toBeUndefined();
81
+ expect(view({ messagePreview: " " }).messagePreview).toBeUndefined();
82
+ expect(view({}).messagePreview).toBeUndefined();
83
+ });
84
+
85
+ test("collects trust/security warnings", () => {
86
+ const v = view({
87
+ isStranger: true,
88
+ isRestricted: true,
89
+ previousMemberStatus: "revoked",
90
+ });
91
+ expect(v.warnings).toEqual([
92
+ "This user was previously revoked.",
93
+ "External Slack user (not in this workspace).",
94
+ "Guest / restricted account.",
95
+ ]);
96
+ expect(view({}).warnings).toEqual([]);
97
+ });
98
+ });
@@ -1,9 +1,7 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
 
3
- import {
4
- buildAccessRequestSeedContentBlocks,
5
- parseAccessRequestPayload,
6
- } from "../notifications/access-request-copy.js";
3
+ import { parseAccessRequestPayload } from "../notifications/access-request-copy.js";
4
+ import { buildAccessRequestSeedContentBlocks } from "../notifications/approval-card-data.js";
7
5
 
8
6
  describe("buildAccessRequestSeedContentBlocks", () => {
9
7
  const basePayload: Record<string, unknown> = {
@@ -1,16 +1,14 @@
1
1
  /**
2
- * Tests for the address-fallback path in resolveActorTrust.
2
+ * Tests for address-based member resolution in resolveActorTrust.
3
3
  *
4
- * Phone channels registered by the inbound name-capture flow have `address`
5
- * set (E.164) but `externalUserId` = NULL until DTMF verification completes.
6
- * The primary `findContactByChannelExternalId` lookup misses these channels,
7
- * which historically caused the relay router to fall into the name-capture
8
- * ("I don't recognize this number") path instead of the unverified-caller
9
- * guidance path.
4
+ * All member lookups use the canonical (type, address) path — the address
5
+ * column is the sole identity for all channel types. Phone channels
6
+ * registered by the inbound name-capture flow have `address` set (E.164)
7
+ * and are discovered through the same path as every other channel.
10
8
  *
11
- * This suite verifies that the address-based fallback fires correctly and
12
- * that the returned `memberRecord` carries the right channel/status so
13
- * relay-setup-router can emit the `unverified_caller` outcome.
9
+ * This suite verifies that address-based lookup returns the correct
10
+ * `memberRecord` with the right channel/status so relay-setup-router
11
+ * can emit the appropriate outcome (e.g. `unverified_caller`).
14
12
  */
15
13
 
16
14
  import { beforeEach, describe, expect, mock, test } from "bun:test";
@@ -22,18 +20,14 @@ mock.module("../util/logger.js", () => ({
22
20
  }));
23
21
 
24
22
  // ── Contact store stubs — filled in per test ─────────────────────────────────
25
- let _byExternalId: ReturnType<
26
- typeof import("../contacts/contact-store.js")["findContactByChannelExternalId"]
27
- > = null;
28
23
  let _byAddress: ReturnType<
29
- typeof import("../contacts/contact-store.js")["findContactByAddress"]
24
+ (typeof import("../contacts/contact-store.js"))["findContactByAddress"]
30
25
  > = null;
31
26
  let _guardian: ReturnType<
32
- typeof import("../contacts/contact-store.js")["findGuardianForChannel"]
27
+ (typeof import("../contacts/contact-store.js"))["findGuardianForChannel"]
33
28
  > = null;
34
29
 
35
30
  mock.module("../contacts/contact-store.js", () => ({
36
- findContactByChannelExternalId: (_type: string, _id: string) => _byExternalId,
37
31
  findContactByAddress: (_type: string, _addr: string) => _byAddress,
38
32
  findGuardianForChannel: (_channel: string) => _guardian,
39
33
  }));
@@ -49,7 +43,6 @@ const PHONE = "+15559871234";
49
43
  function makeContact(
50
44
  role: "guardian" | "contact" = "contact",
51
45
  status: "unverified" | "active" = "unverified",
52
- externalUserId: string | null = null,
53
46
  ): ContactWithChannels {
54
47
  const channelId = "ch-test";
55
48
  return {
@@ -69,8 +62,7 @@ function makeContact(
69
62
  id: channelId,
70
63
  contactId: "contact-test",
71
64
  type: "phone",
72
- address: PHONE.toLowerCase(),
73
- externalUserId,
65
+ address: PHONE,
74
66
  externalChatId: null,
75
67
  isPrimary: true,
76
68
  status,
@@ -94,14 +86,13 @@ function makeContact(
94
86
 
95
87
  describe("resolveActorTrust — address fallback", () => {
96
88
  beforeEach(() => {
97
- _byExternalId = null;
98
89
  _byAddress = null;
99
90
  _guardian = null;
100
91
  });
101
92
 
102
93
  test("finds unverified channel via address when externalUserId is null", () => {
103
94
  // Simulate a contact registered by name-capture: address set, externalUserId null.
104
- _byAddress = makeContact("contact", "unverified", null);
95
+ _byAddress = makeContact("contact", "unverified");
105
96
 
106
97
  const result = resolveActorTrust({
107
98
  assistantId: "asst-1",
@@ -113,16 +104,13 @@ describe("resolveActorTrust — address fallback", () => {
113
104
  expect(result.memberRecord).not.toBeNull();
114
105
  expect(result.memberRecord?.contact.displayName).toBe("Patrick Test");
115
106
  expect(result.memberRecord?.channel.status).toBe("unverified");
116
- // trustClass is 'unknown' for an unverified member (correct not yet active)
117
- expect(result.trustClass).toBe("unknown");
107
+ // trustClass is 'unverified_contact' for a member whose channel is
108
+ // pending or unverified — known to the guardian but not yet verified.
109
+ expect(result.trustClass).toBe("unverified_contact");
118
110
  });
119
111
 
120
- test("externalUserId path takes priority over address path", () => {
121
- // Both lookups return a contact; the externalUserId one should win.
122
- const primaryContact = makeContact("contact", "active", PHONE);
123
- const addressContact = makeContact("contact", "unverified", null);
124
- _byExternalId = primaryContact;
125
- _byAddress = addressContact;
112
+ test("address lookup is the sole member resolution path", () => {
113
+ _byAddress = makeContact("contact", "active");
126
114
 
127
115
  const result = resolveActorTrust({
128
116
  assistantId: "asst-1",
@@ -132,11 +120,10 @@ describe("resolveActorTrust — address fallback", () => {
132
120
  });
133
121
 
134
122
  expect(result.memberRecord?.channel.status).toBe("active");
135
- expect(result.memberRecord?.channel.externalUserId).toBe(PHONE);
123
+ expect(result.memberRecord?.channel.address).toBe(PHONE);
136
124
  });
137
125
 
138
126
  test("returns null memberRecord when neither lookup finds the number", () => {
139
- _byExternalId = null;
140
127
  _byAddress = null;
141
128
 
142
129
  const result = resolveActorTrust({
@@ -153,7 +140,7 @@ describe("resolveActorTrust — address fallback", () => {
153
140
  test("address-found active channel elevates trust to trusted_contact", () => {
154
141
  // An active channel found via address (e.g. after manual verify without externalUserId set)
155
142
  // should still yield trusted_contact trust class.
156
- _byAddress = makeContact("contact", "active", null);
143
+ _byAddress = makeContact("contact", "active");
157
144
 
158
145
  const result = resolveActorTrust({
159
146
  assistantId: "asst-1",
@@ -166,4 +153,57 @@ describe("resolveActorTrust — address fallback", () => {
166
153
  expect(result.memberRecord?.channel.status).toBe("active");
167
154
  expect(result.trustClass).toBe("trusted_contact");
168
155
  });
156
+
157
+ test("pending-status member is classified as unverified_contact", () => {
158
+ // Mirrors the unverified branch but for `pending` status (e.g. a phone
159
+ // contact registered by name-capture awaiting the DTMF challenge).
160
+ const contact = makeContact("contact", "unverified");
161
+ // Override status to "pending" — makeContact only accepts unverified/active
162
+ contact.channels[0]!.status = "pending";
163
+ _byAddress = contact;
164
+
165
+ const result = resolveActorTrust({
166
+ assistantId: "asst-1",
167
+ sourceChannel: "phone",
168
+ conversationExternalId: PHONE,
169
+ actorExternalId: PHONE,
170
+ });
171
+
172
+ expect(result.memberRecord?.channel.status).toBe("pending");
173
+ expect(result.trustClass).toBe("unverified_contact");
174
+ });
175
+
176
+ test("blocked-status member is classified as unknown (not unverified_contact)", () => {
177
+ // Hard-deny statuses (blocked, revoked) stay `unknown` — admission-layer
178
+ // re-checks channel.status and emits the hard-deny reasons.
179
+ const contact = makeContact("contact", "unverified");
180
+ contact.channels[0]!.status = "blocked";
181
+ _byAddress = contact;
182
+
183
+ const result = resolveActorTrust({
184
+ assistantId: "asst-1",
185
+ sourceChannel: "phone",
186
+ conversationExternalId: PHONE,
187
+ actorExternalId: PHONE,
188
+ });
189
+
190
+ expect(result.memberRecord?.channel.status).toBe("blocked");
191
+ expect(result.trustClass).toBe("unknown");
192
+ });
193
+
194
+ test("revoked-status member is classified as unknown", () => {
195
+ const contact = makeContact("contact", "unverified");
196
+ contact.channels[0]!.status = "revoked";
197
+ _byAddress = contact;
198
+
199
+ const result = resolveActorTrust({
200
+ assistantId: "asst-1",
201
+ sourceChannel: "phone",
202
+ conversationExternalId: PHONE,
203
+ actorExternalId: PHONE,
204
+ });
205
+
206
+ expect(result.memberRecord?.channel.status).toBe("revoked");
207
+ expect(result.trustClass).toBe("unknown");
208
+ });
169
209
  });
@@ -0,0 +1,241 @@
1
+ /**
2
+ * The compaction summarizer's input.
3
+ *
4
+ * Both the budget gate and overflow recovery summarize the *injected* history
5
+ * (the genuine conversation as the agent saw it) so the summary call's prompt
6
+ * prefix matches the agent's warm prefix cache — a cache read rather than a
7
+ * fresh cache write. The agent loop hands the full injected history to
8
+ * compaction unconditionally; the post-compaction re-injection hook owns
9
+ * injection idempotency by stripping the tail's per-turn blocks before
10
+ * re-applying them.
11
+ */
12
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
13
+
14
+ import type { PostCompactContext } from "@vellumai/plugin-api";
15
+
16
+ import type { AgentEvent } from "../agent/loop.js";
17
+ import { AgentLoop } from "../agent/loop.js";
18
+ import type { ContextWindowConfig } from "../config/types.js";
19
+ import type { TrustContext } from "../daemon/trust-context.js";
20
+ import { HOOKS } from "../plugin-api/constants.js";
21
+ import {
22
+ createContextWindowManager,
23
+ disposeContextWindowManager,
24
+ getContextWindowManager,
25
+ } from "../plugins/defaults/compaction/manager-store.js";
26
+ import {
27
+ registerPlugin,
28
+ resetPluginRegistryForTests,
29
+ } from "../plugins/registry.js";
30
+ import type {
31
+ Message,
32
+ Provider,
33
+ ProviderResponse,
34
+ SendMessageOptions,
35
+ } from "../providers/types.js";
36
+ import { ContextOverflowError } from "../providers/types.js";
37
+
38
+ const testPostCompactPlugin = {
39
+ manifest: { name: "test-post-compact", version: "0.0.0" },
40
+ hooks: {
41
+ [HOOKS.POST_COMPACT]: async (input: PostCompactContext): Promise<void> => {
42
+ void input;
43
+ },
44
+ },
45
+ };
46
+
47
+ const CONVERSATION_ID = "compaction-strip-conversation";
48
+
49
+ /**
50
+ * A runtime `<workspace>` injection block. Its presence in the summarizer's
51
+ * input proves compaction received the injected history rather than a stripped
52
+ * copy.
53
+ */
54
+ const WORKSPACE_INJECTION =
55
+ "<workspace>\nActive workspace: project-x\n</workspace>";
56
+ const TURN_BODY = "Hello there, this is the turn body.";
57
+
58
+ /**
59
+ * A user turn carrying a real text block plus a runtime injection block, both
60
+ * of which ride into compaction's input as-is.
61
+ */
62
+ const injectedUserMessage: Message = {
63
+ role: "user",
64
+ content: [
65
+ { type: "text", text: TURN_BODY },
66
+ { type: "text", text: WORKSPACE_INJECTION },
67
+ ],
68
+ };
69
+
70
+ function textResponse(text: string): ProviderResponse {
71
+ return {
72
+ content: [{ type: "text", text }],
73
+ model: "mock-model",
74
+ usage: { inputTokens: 10, outputTokens: 5 },
75
+ stopReason: "end_turn",
76
+ };
77
+ }
78
+
79
+ function createMockProvider(responses: ProviderResponse[]): Provider {
80
+ let callIndex = 0;
81
+ return {
82
+ name: "mock",
83
+ async sendMessage(
84
+ _messages: Message[],
85
+ _options?: SendMessageOptions,
86
+ ): Promise<ProviderResponse> {
87
+ const response = responses[callIndex] ?? responses[responses.length - 1];
88
+ callIndex++;
89
+ return response;
90
+ },
91
+ };
92
+ }
93
+
94
+ interface CompactionInputCapture {
95
+ budget: Message[] | null;
96
+ overflow: Message[] | null;
97
+ }
98
+
99
+ /**
100
+ * Install a per-conversation manager that records the messages handed to the
101
+ * budget summarizer (`maybeCompact`) and to overflow recovery
102
+ * (`recoverContextOverflow`).
103
+ */
104
+ function installCapturingManager(capture: CompactionInputCapture): {
105
+ trust: TrustContext;
106
+ } {
107
+ createContextWindowManager({
108
+ provider: { name: "mock-provider" } as unknown as Provider,
109
+ systemPrompt: "system",
110
+ config: {} as unknown as ContextWindowConfig,
111
+ conversationId: CONVERSATION_ID,
112
+ });
113
+ const manager = getContextWindowManager(CONVERSATION_ID);
114
+ if (manager) {
115
+ manager.maybeCompact = (async (messages: Message[]) => {
116
+ capture.budget = messages;
117
+ return {
118
+ messages: [injectedUserMessage],
119
+ compacted: true,
120
+ exhausted: false,
121
+ };
122
+ }) as unknown as typeof manager.maybeCompact;
123
+ manager.recoverContextOverflow = (async (messages: Message[]) => {
124
+ capture.overflow = messages;
125
+ return {
126
+ messages: [injectedUserMessage],
127
+ compacted: true,
128
+ exhausted: false,
129
+ };
130
+ }) as unknown as typeof manager.recoverContextOverflow;
131
+ }
132
+ return { trust: { sourceChannel: "vellum", trustClass: "unknown" } };
133
+ }
134
+
135
+ function serialize(messages: Message[] | null): string {
136
+ return JSON.stringify(messages ?? []);
137
+ }
138
+
139
+ describe("AgentLoop compaction summarizer input", () => {
140
+ beforeEach(() => {
141
+ resetPluginRegistryForTests();
142
+ registerPlugin(testPostCompactPlugin);
143
+ });
144
+
145
+ afterEach(() => {
146
+ disposeContextWindowManager(CONVERSATION_ID);
147
+ });
148
+
149
+ test("budget-gate compaction summarizes the injected history", async () => {
150
+ // GIVEN a history whose user turn carries a runtime-injection block
151
+ const capture: CompactionInputCapture = { budget: null, overflow: null };
152
+ const provider = createMockProvider([
153
+ textResponse("done after compaction"),
154
+ ]);
155
+ const loop = new AgentLoop({
156
+ provider,
157
+ systemPrompt: "system",
158
+ conversationId: CONVERSATION_ID,
159
+ tools: [],
160
+ toolExecutor: async () => ({ content: "ok", isError: false }),
161
+ });
162
+ const events: AgentEvent[] = [];
163
+
164
+ // WHEN the budget gate trips and in-place compaction runs
165
+ await loop.run({
166
+ requestId: "req",
167
+ messages: [injectedUserMessage],
168
+ onEvent: (event) => {
169
+ events.push(event);
170
+ },
171
+ resolveContextWindow: () => ({
172
+ maxInputTokens: 10,
173
+ overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
174
+ }),
175
+ compactInPlace: true,
176
+ ...installCapturingManager(capture),
177
+ });
178
+
179
+ // THEN the budget summarizer received the injected history, injection intact
180
+ expect(capture.budget).not.toBeNull();
181
+ expect(serialize(capture.budget)).toContain("<workspace>");
182
+ // AND overflow recovery was not invoked
183
+ expect(capture.overflow).toBeNull();
184
+ // AND the history-stripped marker still fires for the durable base
185
+ expect(events.some((e) => e.type === "history_stripped")).toBe(true);
186
+ });
187
+
188
+ test("overflow-driven compaction summarizes the injected history", async () => {
189
+ // GIVEN a history whose user turn carries a runtime-injection block
190
+ const capture: CompactionInputCapture = { budget: null, overflow: null };
191
+
192
+ // AND a provider that rejects the first call as context-too-large
193
+ let throwOnce = true;
194
+ const provider: Provider = {
195
+ name: "mock",
196
+ async sendMessage(): Promise<ProviderResponse> {
197
+ if (throwOnce) {
198
+ throwOnce = false;
199
+ throw new ContextOverflowError("prompt too long", "mock", {
200
+ actualTokens: 999_999,
201
+ });
202
+ }
203
+ return textResponse("done after overflow recovery");
204
+ },
205
+ };
206
+ const loop = new AgentLoop({
207
+ provider,
208
+ systemPrompt: "system",
209
+ conversationId: CONVERSATION_ID,
210
+ tools: [],
211
+ toolExecutor: async () => ({ content: "ok", isError: false }),
212
+ });
213
+ // A blocking watermark plus `compactInPlace: false` keep the budget gate
214
+ // from firing, so only the overflow rejection drives compaction.
215
+ loop.compactionCircuit.lastPostCompactionEstimate = 0;
216
+ const events: AgentEvent[] = [];
217
+
218
+ // WHEN the provider overflow forces overflow recovery
219
+ await loop.run({
220
+ requestId: "req",
221
+ messages: [injectedUserMessage],
222
+ onEvent: (event) => {
223
+ events.push(event);
224
+ },
225
+ resolveContextWindow: () => ({
226
+ maxInputTokens: 10,
227
+ overflowRecovery: { enabled: true, safetyMarginRatio: 0 },
228
+ }),
229
+ compactInPlace: false,
230
+ ...installCapturingManager(capture),
231
+ });
232
+
233
+ // THEN overflow recovery received the injected history, injection intact
234
+ expect(capture.overflow).not.toBeNull();
235
+ expect(serialize(capture.overflow)).toContain("<workspace>");
236
+ // AND the real turn body is present alongside the injection
237
+ expect(serialize(capture.overflow)).toContain(TURN_BODY);
238
+ // AND the budget summarizer was not invoked
239
+ expect(capture.budget).toBeNull();
240
+ });
241
+ });