@vellumai/assistant 0.9.1-staging.1 → 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 (433) hide show
  1. package/docs/activation-funnel-telemetry.md +24 -18
  2. package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
  3. package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
  4. package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
  5. package/openapi.yaml +852 -15
  6. package/package.json +1 -1
  7. package/src/__tests__/access-request-card-view.test.ts +98 -0
  8. package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
  9. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
  10. package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
  11. package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
  12. package/src/__tests__/app-compiler.test.ts +15 -1
  13. package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
  14. package/src/__tests__/call-pointer-messages.test.ts +28 -0
  15. package/src/__tests__/cancel-clears-processing.test.ts +89 -0
  16. package/src/__tests__/channel-approval-routes.test.ts +0 -4
  17. package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
  18. package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
  19. package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
  20. package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
  21. package/src/__tests__/config-loader-backfill.test.ts +174 -30
  22. package/src/__tests__/config-schema.test.ts +35 -0
  23. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
  24. package/src/__tests__/contact-store-user-file.test.ts +0 -6
  25. package/src/__tests__/contacts-tools.test.ts +29 -0
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
  27. package/src/__tests__/conversation-agent-loop.test.ts +58 -0
  28. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  29. package/src/__tests__/conversation-lifecycle.test.ts +7 -9
  30. package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
  31. package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
  32. package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
  33. package/src/__tests__/conversation-title-service.test.ts +62 -0
  34. package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
  35. package/src/__tests__/credential-prompt-route.test.ts +1 -0
  36. package/src/__tests__/credential-security-invariants.test.ts +2 -0
  37. package/src/__tests__/disk-pressure-policy.test.ts +12 -0
  38. package/src/__tests__/disk-usage.test.ts +65 -0
  39. package/src/__tests__/dynamic-page-surface.test.ts +51 -0
  40. package/src/__tests__/gateway-flag-listener.test.ts +110 -1
  41. package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
  42. package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
  43. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
  44. package/src/__tests__/guardian-grant-minting.test.ts +3 -35
  45. package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
  46. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  47. package/src/__tests__/headless-browser-mode.test.ts +10 -0
  48. package/src/__tests__/headless-browser-navigate.test.ts +8 -3
  49. package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
  50. package/src/__tests__/host-browser-proxy.test.ts +87 -0
  51. package/src/__tests__/injector-v3-suppression.test.ts +27 -20
  52. package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
  53. package/src/__tests__/invite-redemption-service.test.ts +0 -3
  54. package/src/__tests__/llm-catalog-parity.test.ts +30 -1
  55. package/src/__tests__/llm-resolver.test.ts +21 -0
  56. package/src/__tests__/llm-schema.test.ts +1 -0
  57. package/src/__tests__/managed-profile-guard.test.ts +163 -4
  58. package/src/__tests__/mcp-health-check.test.ts +6 -7
  59. package/src/__tests__/media-stream-server-integration.test.ts +317 -13
  60. package/src/__tests__/path-policy.test.ts +34 -0
  61. package/src/__tests__/persona-resolver.test.ts +38 -0
  62. package/src/__tests__/plugin-api-provider.test.ts +24 -0
  63. package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
  64. package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
  65. package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
  66. package/src/__tests__/reaction-persistence.test.ts +150 -29
  67. package/src/__tests__/relay-server.test.ts +285 -0
  68. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  69. package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
  70. package/src/__tests__/skill-execute-input.test.ts +5 -0
  71. package/src/__tests__/skills.test.ts +51 -0
  72. package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
  73. package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
  74. package/src/__tests__/subagent-tools.test.ts +150 -0
  75. package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
  76. package/src/__tests__/title-generate-hook.test.ts +100 -3
  77. package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
  78. package/src/__tests__/tool-audit-listener.test.ts +7 -7
  79. package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
  80. package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
  81. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
  82. package/src/__tests__/trusted-contact-verification.test.ts +2 -4
  83. package/src/__tests__/twilio-routes.test.ts +81 -1
  84. package/src/__tests__/voice-invite-redemption.test.ts +0 -1
  85. package/src/__tests__/weak-open-model.test.ts +30 -0
  86. package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
  87. package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
  88. package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
  89. package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
  90. package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
  91. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
  92. package/src/agent/loop.ts +33 -33
  93. package/src/api/events/tool-result.ts +6 -0
  94. package/src/api/events/workflow-completed.ts +53 -0
  95. package/src/api/events/workflow-leaf-finished.ts +38 -0
  96. package/src/api/events/workflow-leaf-started.ts +35 -0
  97. package/src/api/events/workflow-progress.ts +32 -0
  98. package/src/api/events/workflow-started.ts +31 -0
  99. package/src/api/index.ts +40 -0
  100. package/src/api/responses/conversation-message.ts +26 -0
  101. package/src/api/responses/home.ts +26 -0
  102. package/src/api/responses/workflow-journal.ts +53 -0
  103. package/src/approvals/guardian-card-withdrawal.ts +145 -0
  104. package/src/approvals/guardian-decision-primitive.ts +26 -3
  105. package/src/approvals/guardian-request-resolvers.ts +181 -78
  106. package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
  107. package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
  108. package/src/calls/call-pointer-messages.ts +10 -4
  109. package/src/calls/channel-admission-reader.ts +104 -0
  110. package/src/calls/guardian-dispatch.ts +17 -45
  111. package/src/calls/media-stream-server.ts +84 -2
  112. package/src/calls/relay-server.ts +66 -0
  113. package/src/calls/relay-setup-router.ts +82 -1
  114. package/src/calls/twilio-routes.ts +17 -8
  115. package/src/cli/commands/clients.ts +3 -0
  116. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
  117. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
  118. package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
  119. package/src/cli/commands/memory/index.ts +30 -0
  120. package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
  121. package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
  122. package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
  123. package/src/cli/commands/oauth/status.test.ts +36 -0
  124. package/src/cli/commands/oauth/status.ts +23 -3
  125. package/src/cli/commands/plugins.ts +57 -5
  126. package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
  127. package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
  128. package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
  129. package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
  130. package/src/cli/lib/inspect-plugin.ts +12 -1
  131. package/src/cli/lib/merge-plugin-tree.ts +149 -49
  132. package/src/cli/lib/plugin-surfaces.ts +104 -0
  133. package/src/cli/lib/upgrade-plugin.ts +64 -36
  134. package/src/cli/program.ts +2 -4
  135. package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
  136. package/src/config/assistant-feature-flags.ts +22 -7
  137. package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
  138. package/src/config/bundled-skills/messaging/SKILL.md +6 -4
  139. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
  140. package/src/config/bundled-skills/workflows/SKILL.md +14 -7
  141. package/src/config/call-site-defaults.ts +3 -0
  142. package/src/config/feature-flag-registry.json +49 -18
  143. package/src/config/llm-resolver.ts +3 -0
  144. package/src/config/memory-v3-gate.ts +11 -0
  145. package/src/config/schema.ts +8 -6
  146. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  147. package/src/config/schemas/call-site-catalog.ts +7 -0
  148. package/src/config/schemas/channels.ts +11 -0
  149. package/src/config/schemas/llm.ts +31 -0
  150. package/src/config/schemas/memory-lifecycle.ts +3 -7
  151. package/src/config/schemas/memory-v3.ts +6 -0
  152. package/src/config/schemas/services.ts +18 -0
  153. package/src/config/seed-inference-profiles.ts +94 -34
  154. package/src/config/skills.ts +21 -0
  155. package/src/config/sync-gated-profiles.ts +220 -0
  156. package/src/contacts/contact-store.ts +2 -10
  157. package/src/contacts/contacts-write.ts +1 -2
  158. package/src/contacts/types.ts +0 -1
  159. package/src/context/compactor.ts +86 -52
  160. package/src/context/strip-injections.ts +58 -10
  161. package/src/context/token-estimator.ts +1 -1
  162. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
  163. package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
  164. package/src/daemon/conversation-agent-loop.ts +100 -19
  165. package/src/daemon/conversation-history.ts +1 -1
  166. package/src/daemon/conversation-lifecycle.ts +3 -5
  167. package/src/daemon/conversation-process.ts +13 -5
  168. package/src/daemon/conversation-runtime-assembly.ts +13 -15
  169. package/src/daemon/conversation-surfaces.ts +26 -0
  170. package/src/daemon/conversation-tool-setup.ts +16 -11
  171. package/src/daemon/conversation.ts +64 -14
  172. package/src/daemon/disk-pressure-policy.ts +5 -3
  173. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
  174. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
  175. package/src/daemon/handlers/config-a2a.ts +0 -2
  176. package/src/daemon/handlers/config-channels.ts +5 -10
  177. package/src/daemon/handlers/config-slack-channel.ts +20 -0
  178. package/src/daemon/handlers/conversations.ts +107 -0
  179. package/src/daemon/host-browser-proxy.ts +41 -0
  180. package/src/daemon/lifecycle.ts +55 -20
  181. package/src/daemon/message-provenance.ts +2 -0
  182. package/src/daemon/message-types/contacts.ts +0 -1
  183. package/src/daemon/message-types/web-activity.ts +7 -1
  184. package/src/daemon/message-types/workflows.ts +83 -1
  185. package/src/daemon/tool-setup-types.ts +4 -0
  186. package/src/daemon/trust-context.ts +1 -1
  187. package/src/events/tool-audit-listener.ts +2 -2
  188. package/src/home/feed-source-enrichment.test.ts +151 -0
  189. package/src/home/feed-source-enrichment.ts +176 -0
  190. package/src/instrument.ts +18 -6
  191. package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
  192. package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
  193. package/src/ipc/assistant-server.ts +37 -4
  194. package/src/ipc/gateway-flag-listener.ts +18 -2
  195. package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
  196. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
  197. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
  198. package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
  199. package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
  200. package/src/memory/auth-fallback-events-store.ts +2 -2
  201. package/src/memory/auto-analysis-enqueue.ts +3 -5
  202. package/src/memory/canonical-guardian-store.ts +39 -1
  203. package/src/memory/conversation-crud.ts +9 -4
  204. package/src/memory/conversation-key-store.ts +17 -2
  205. package/src/memory/conversation-title-service.ts +64 -7
  206. package/src/memory/db-init.ts +10 -0
  207. package/src/memory/embedding-backend.ts +15 -1
  208. package/src/memory/jobs-worker.ts +2 -1
  209. package/src/memory/lifecycle-events-store.ts +2 -2
  210. package/src/memory/memory-retrospective-enqueue.ts +31 -6
  211. package/src/memory/memory-retrospective-job.ts +9 -0
  212. package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
  213. package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
  214. package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
  215. package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
  216. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
  217. package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
  218. package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
  219. package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
  220. package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
  221. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
  222. package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
  223. package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
  224. package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
  225. package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
  226. package/src/memory/migrations/index.ts +5 -0
  227. package/src/memory/onboarding-events-store.ts +3 -3
  228. package/src/memory/schema/contacts.ts +0 -1
  229. package/src/memory/skill-loaded-events-store.test.ts +7 -15
  230. package/src/memory/skill-loaded-events-store.ts +2 -2
  231. package/src/memory/tool-executed-events-store.test.ts +7 -7
  232. package/src/memory/turn-trace-store.test.ts +736 -0
  233. package/src/memory/turn-trace-store.ts +364 -0
  234. package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
  235. package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
  236. package/src/memory/v2/consolidation-job.ts +2 -2
  237. package/src/memory/v2/skill-content.ts +25 -7
  238. package/src/memory/v2/skill-store.ts +7 -1
  239. package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
  240. package/src/memory/v3-eval/eval-packets.ts +546 -0
  241. package/src/messaging/providers/slack/api.ts +31 -0
  242. package/src/messaging/providers/slack/send.test.ts +114 -2
  243. package/src/messaging/providers/slack/send.ts +30 -7
  244. package/src/messaging/providers/slack/withdraw.test.ts +200 -0
  245. package/src/messaging/providers/slack/withdraw.ts +161 -0
  246. package/src/notifications/AGENTS.md +2 -0
  247. package/src/notifications/access-request-copy.ts +72 -59
  248. package/src/notifications/adapters/slack.ts +55 -73
  249. package/src/notifications/approval-card-data.ts +333 -0
  250. package/src/notifications/broadcaster.ts +6 -2
  251. package/src/notifications/canonical-delivery-recorder.ts +139 -0
  252. package/src/notifications/copy-composer.ts +3 -3
  253. package/src/notifications/decision-engine.ts +4 -2
  254. package/src/notifications/destination-resolver.ts +4 -6
  255. package/src/notifications/guardian-question-mode.ts +10 -0
  256. package/src/notifications/home-feed-side-effect.ts +3 -13
  257. package/src/notifications/notification-utils.ts +2 -1
  258. package/src/notifications/signal.ts +79 -43
  259. package/src/notifications/types.ts +98 -128
  260. package/src/permissions/checker.test.ts +51 -0
  261. package/src/permissions/checker.ts +185 -26
  262. package/src/permissions/ipc-risk-types.ts +24 -0
  263. package/src/permissions/question-prompter.test.ts +27 -0
  264. package/src/permissions/question-prompter.ts +4 -0
  265. package/src/platform/client.test.ts +119 -0
  266. package/src/platform/client.ts +66 -0
  267. package/src/platform/consent-cache.test.ts +267 -0
  268. package/src/platform/consent-cache.ts +174 -0
  269. package/src/plugin-api/index.ts +27 -0
  270. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  271. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  272. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  273. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
  274. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  275. package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
  276. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  277. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  278. package/src/plugins/defaults/advisor/config.ts +21 -0
  279. package/src/plugins/defaults/advisor/consult.ts +93 -0
  280. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  281. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  282. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  283. package/src/plugins/defaults/advisor/package.json +14 -0
  284. package/src/plugins/defaults/advisor/steering.ts +67 -0
  285. package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
  286. package/src/plugins/defaults/advisor/transcript.ts +76 -0
  287. package/src/plugins/defaults/index.ts +35 -0
  288. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
  289. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  290. package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
  291. package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
  292. package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
  293. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
  294. package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
  295. package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
  296. package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
  297. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
  298. package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
  299. package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
  300. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
  301. package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
  302. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
  303. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
  304. package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
  305. package/src/prompts/persona-resolver.ts +12 -2
  306. package/src/prompts/templates/system-sections.ts +7 -2
  307. package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
  308. package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
  309. package/src/providers/__tests__/retry-callsite.test.ts +176 -0
  310. package/src/providers/atlascloud/client.ts +85 -0
  311. package/src/providers/fetch-provider-catalog.ts +85 -0
  312. package/src/providers/inference/adapter-factory.ts +3 -0
  313. package/src/providers/model-catalog.ts +58 -0
  314. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
  315. package/src/providers/openai/chat-completions-provider.ts +7 -0
  316. package/src/providers/openai/responses-provider.ts +10 -0
  317. package/src/providers/provider-send-message.ts +11 -3
  318. package/src/providers/retry.ts +53 -12
  319. package/src/providers/search-provider-catalog.ts +10 -0
  320. package/src/providers/weak-open-model.ts +22 -0
  321. package/src/runtime/__tests__/agent-wake.test.ts +181 -0
  322. package/src/runtime/__tests__/client-health.test.ts +44 -0
  323. package/src/runtime/access-request-helper.ts +21 -53
  324. package/src/runtime/actor-trust-resolver.ts +49 -21
  325. package/src/runtime/agent-wake.ts +52 -0
  326. package/src/runtime/assistant-event-hub.ts +18 -4
  327. package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
  328. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  329. package/src/runtime/capabilities.test.ts +120 -0
  330. package/src/runtime/capabilities.ts +197 -0
  331. package/src/runtime/channel-approval-types.ts +5 -1
  332. package/src/runtime/channel-retry-sweep.ts +1 -0
  333. package/src/runtime/channel-verification-service.ts +1 -2
  334. package/src/runtime/client-health.ts +26 -0
  335. package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
  336. package/src/runtime/effective-capabilities.test.ts +128 -0
  337. package/src/runtime/effective-capabilities.ts +84 -0
  338. package/src/runtime/guardian-reply-router.ts +106 -21
  339. package/src/runtime/invite-redemption-service.ts +6 -22
  340. package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
  341. package/src/runtime/migrations/vbundle-builder.ts +49 -20
  342. package/src/runtime/pending-interactions.ts +15 -0
  343. package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
  344. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
  345. package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
  346. package/src/runtime/routes/browser-tabs-routes.ts +9 -0
  347. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
  348. package/src/runtime/routes/client-routes.ts +10 -0
  349. package/src/runtime/routes/contact-routes.ts +31 -8
  350. package/src/runtime/routes/conversation-management-routes.ts +80 -1
  351. package/src/runtime/routes/conversation-query-routes.ts +68 -22
  352. package/src/runtime/routes/conversation-routes.ts +37 -12
  353. package/src/runtime/routes/events-routes.ts +1 -3
  354. package/src/runtime/routes/guardian-approval-interception.ts +14 -73
  355. package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
  356. package/src/runtime/routes/home-feed-routes.ts +8 -3
  357. package/src/runtime/routes/inbound-message-handler.ts +214 -228
  358. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
  359. package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
  360. package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
  361. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
  362. package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
  363. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
  364. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
  365. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
  366. package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
  367. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
  368. package/src/runtime/routes/index.ts +2 -0
  369. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
  370. package/src/runtime/routes/integrations/slack/channel.ts +36 -0
  371. package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
  372. package/src/runtime/routes/mcp-auth-routes.ts +233 -41
  373. package/src/runtime/routes/memory-eval-routes.ts +87 -0
  374. package/src/runtime/routes/notification-routes.ts +122 -133
  375. package/src/runtime/routes/platform-routes.ts +2 -2
  376. package/src/runtime/routes/plugins-routes.ts +40 -7
  377. package/src/runtime/routes/secret-routes.ts +10 -0
  378. package/src/runtime/routes/surface-action-routes.ts +2 -1
  379. package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
  380. package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
  381. package/src/runtime/routes/workflow-routes.test.ts +225 -1
  382. package/src/runtime/routes/workflow-routes.ts +131 -1
  383. package/src/runtime/tool-grant-request-helper.ts +18 -16
  384. package/src/runtime/trust-context-resolver.ts +8 -5
  385. package/src/schedule/schedule-store.ts +1 -1
  386. package/src/schedule/scheduler-types.ts +5 -1
  387. package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
  388. package/src/security/secret-patterns.ts +3 -0
  389. package/src/subagent/manager.ts +11 -4
  390. package/src/telemetry/trace-collection-policy.test.ts +28 -0
  391. package/src/telemetry/trace-collection-policy.ts +30 -0
  392. package/src/telemetry/types.ts +89 -0
  393. package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
  394. package/src/telemetry/usage-telemetry-reporter.ts +148 -41
  395. package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
  396. package/src/tools/browser/browser-execution.ts +29 -18
  397. package/src/tools/document/document-tool.ts +2 -3
  398. package/src/tools/executor.ts +5 -3
  399. package/src/tools/host-terminal/host-shell.ts +5 -4
  400. package/src/tools/memory/register.ts +2 -2
  401. package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
  402. package/src/tools/network/__tests__/web-search.test.ts +143 -0
  403. package/src/tools/network/web-fetch.ts +372 -1
  404. package/src/tools/network/web-search.ts +213 -10
  405. package/src/tools/permission-checker.ts +3 -2
  406. package/src/tools/registry.ts +20 -0
  407. package/src/tools/schedule/create.ts +4 -3
  408. package/src/tools/schedule/update.ts +2 -1
  409. package/src/tools/shared/filesystem/path-policy.ts +39 -13
  410. package/src/tools/skills/execute.ts +1 -2
  411. package/src/tools/subagent/spawn.ts +37 -13
  412. package/src/tools/terminal/shell.ts +10 -4
  413. package/src/tools/tool-approval-handler.ts +17 -10
  414. package/src/tools/types.ts +9 -0
  415. package/src/tools/ui-surface/definitions.ts +25 -2
  416. package/src/tools/verification-control-plane-policy.ts +3 -1
  417. package/src/tools/workflows/run-workflow.ts +1 -0
  418. package/src/util/disk-usage.ts +78 -23
  419. package/src/util/platform.ts +8 -1
  420. package/src/watcher/telemetry.ts +2 -2
  421. package/src/workflows/engine.test.ts +175 -1
  422. package/src/workflows/engine.ts +82 -0
  423. package/src/workflows/journal-store.test.ts +70 -0
  424. package/src/workflows/journal-store.ts +18 -3
  425. package/src/workflows/run-manager.test.ts +171 -3
  426. package/src/workflows/run-manager.ts +64 -0
  427. package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
  428. package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
  429. package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
  430. package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
  431. package/src/workspace/migrations/registry.ts +8 -0
  432. package/src/notifications/tool-approval-copy.ts +0 -142
  433. package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
@@ -92,7 +92,7 @@ mock.module("../runtime/channel-verification-service.js", () => ({
92
92
  return null;
93
93
  },
94
94
  createOutboundSession: () => ({
95
- conversationId: "test-session",
95
+ sessionId: "test-session",
96
96
  secret: "123456",
97
97
  }),
98
98
  bindSessionIdentity: () => {},
@@ -108,16 +108,23 @@ mock.module("../runtime/channel-verification-service.js", () => ({
108
108
  }),
109
109
  }));
110
110
 
111
- // Mock gateway client — capture delivery calls
111
+ // Mock gateway client — capture delivery calls. `failDeliveryWhen` lets a test
112
+ // simulate a delivery failure for a specific payload (e.g. a DM that can't be
113
+ // opened) so fallback paths can be exercised.
112
114
  const deliveredReplies: Array<{
113
115
  url: string;
114
116
  payload: Record<string, unknown>;
115
117
  }> = [];
118
+ let failDeliveryWhen: ((payload: Record<string, unknown>) => boolean) | null =
119
+ null;
116
120
  mock.module("../runtime/gateway-client.js", () => ({
117
121
  deliverChannelReply: async (
118
122
  url: string,
119
123
  payload: Record<string, unknown>,
120
124
  ) => {
125
+ if (failDeliveryWhen?.(payload)) {
126
+ throw new Error("simulated delivery failure");
127
+ }
121
128
  deliveredReplies.push({ url, payload });
122
129
  return { ok: true };
123
130
  },
@@ -150,6 +157,7 @@ mock.module("../config/env.js", () => ({
150
157
  import { applyCanonicalGuardianDecision } from "../approvals/guardian-decision-primitive.js";
151
158
  import type { ActorContext } from "../approvals/guardian-request-resolvers.js";
152
159
  import { getResolver } from "../approvals/guardian-request-resolvers.js";
160
+ import { upsertContactChannel } from "../contacts/contacts-write.js";
153
161
  import type { TrustContext } from "../daemon/trust-context.js";
154
162
  import {
155
163
  createCanonicalGuardianRequest,
@@ -564,7 +572,7 @@ describe("(d) unknown actor flow: fail-closed with no interactive approval", ()
564
572
 
565
573
  expect("skipped" in result && result.skipped).toBe(true);
566
574
  if ("skipped" in result) {
567
- expect(result.reason).toBe("not_trusted_contact");
575
+ expect(result.reason).toBe("not_bridgeable_trust_class");
568
576
  }
569
577
  });
570
578
  });
@@ -1088,3 +1096,212 @@ describe("cross-milestone integration checks", () => {
1088
1096
  });
1089
1097
  });
1090
1098
 
1099
+ // ===========================================================================
1100
+ // (g) access_request resolver: requester verification-code delivery
1101
+ //
1102
+ // On approval the requester must receive the 6-digit code so the guardian
1103
+ // never has to relay it by hand. On Slack the code is DM'd straight to the
1104
+ // requester (a private path is guaranteed via their user ID); other channels
1105
+ // keep the courier message because a private path to the requester is not
1106
+ // guaranteed there (e.g. a group chat would leak the secret).
1107
+ // ===========================================================================
1108
+
1109
+ describe("(g) access_request resolver: requester code delivery", () => {
1110
+ const REQUESTER_UID = "U_REQUESTER";
1111
+ const GUARDIAN_UID = "U_GUARDIAN";
1112
+
1113
+ function createAccessRequest(overrides: Record<string, unknown> = {}) {
1114
+ return createCanonicalGuardianRequest({
1115
+ id: `access-req-${Date.now()}-${Math.random().toString(36).slice(2)}`,
1116
+ kind: "access_request",
1117
+ sourceType: "channel",
1118
+ sourceChannel: "slack",
1119
+ conversationId: "conv-access-slack",
1120
+ requesterExternalUserId: REQUESTER_UID,
1121
+ requesterChatId: "C_SHARED_CHANNEL",
1122
+ guardianExternalUserId: GUARDIAN_UID,
1123
+ guardianPrincipalId: "test-principal-id",
1124
+ toolName: "ingress_access_request",
1125
+ expiresAt: Date.now() + 60_000,
1126
+ ...overrides,
1127
+ });
1128
+ }
1129
+
1130
+ beforeEach(() => {
1131
+ resetTables();
1132
+ deliveredReplies.length = 0;
1133
+ emittedSignals.length = 0;
1134
+ failDeliveryWhen = null;
1135
+ });
1136
+
1137
+ test("on-channel Slack approval DMs the verification code to the requester", async () => {
1138
+ const req = createAccessRequest();
1139
+
1140
+ const result = await applyCanonicalGuardianDecision({
1141
+ requestId: req.id,
1142
+ action: "approve_once",
1143
+ actorContext: guardianActor({
1144
+ channel: "slack",
1145
+ actorExternalUserId: GUARDIAN_UID,
1146
+ }),
1147
+ channelDeliveryContext: {
1148
+ replyCallbackUrl:
1149
+ "http://localhost:3000/deliver/slack?threadTs=111.222",
1150
+ guardianChatId: "C_SHARED_CHANNEL",
1151
+ assistantId: "self",
1152
+ },
1153
+ });
1154
+
1155
+ expect(result.applied).toBe(true);
1156
+
1157
+ // The requester receives the actual code in their DM (chatId = user ID),
1158
+ // not a "ask the guardian" courier message.
1159
+ const requesterCodeReply = deliveredReplies.find(
1160
+ (r) =>
1161
+ r.payload.chatId === REQUESTER_UID &&
1162
+ typeof r.payload.text === "string" &&
1163
+ (r.payload.text as string).includes("123456"),
1164
+ );
1165
+ expect(requesterCodeReply).toBeDefined();
1166
+ expect(requesterCodeReply!.payload.text).toContain(
1167
+ "your access request was approved",
1168
+ );
1169
+ // The code DM is durable, never ephemeral.
1170
+ expect(requesterCodeReply!.payload.ephemeral).toBeUndefined();
1171
+ // threadTs (the guardian's channel thread) is stripped for the DM.
1172
+ expect(requesterCodeReply!.url).not.toContain("threadTs");
1173
+
1174
+ // No courier "receive from the guardian" message goes to the requester.
1175
+ const courier = deliveredReplies.find(
1176
+ (r) =>
1177
+ typeof r.payload.text === "string" &&
1178
+ (r.payload.text as string).includes("receive from the guardian"),
1179
+ );
1180
+ expect(courier).toBeUndefined();
1181
+ });
1182
+
1183
+ test("desktop-decided approval DMs the code to the Slack requester via the deliver path", async () => {
1184
+ const req = createAccessRequest();
1185
+
1186
+ const result = await applyCanonicalGuardianDecision({
1187
+ requestId: req.id,
1188
+ action: "approve_once",
1189
+ // Desktop decision: no channelDeliveryContext, actor on the vellum channel.
1190
+ actorContext: guardianActor({
1191
+ channel: "vellum",
1192
+ actorExternalUserId: undefined,
1193
+ }),
1194
+ });
1195
+
1196
+ expect(result.applied).toBe(true);
1197
+
1198
+ const requesterCodeReply = deliveredReplies.find(
1199
+ (r) =>
1200
+ r.payload.chatId === REQUESTER_UID &&
1201
+ typeof r.payload.text === "string" &&
1202
+ (r.payload.text as string).includes("123456"),
1203
+ );
1204
+ expect(requesterCodeReply).toBeDefined();
1205
+ expect(requesterCodeReply!.url).toContain("/deliver/slack");
1206
+ expect(requesterCodeReply!.payload.text).toContain(
1207
+ "your access request was approved",
1208
+ );
1209
+ });
1210
+
1211
+ test("non-Slack channel keeps the courier message and never delivers the code to the requester chat", async () => {
1212
+ const req = createAccessRequest({
1213
+ sourceChannel: "telegram",
1214
+ requesterChatId: "requester-chat-1",
1215
+ conversationId: "conv-access-telegram",
1216
+ });
1217
+
1218
+ const result = await applyCanonicalGuardianDecision({
1219
+ requestId: req.id,
1220
+ action: "approve_once",
1221
+ actorContext: guardianActor({
1222
+ channel: "telegram",
1223
+ actorExternalUserId: GUARDIAN_UID,
1224
+ }),
1225
+ channelDeliveryContext: {
1226
+ replyCallbackUrl: "http://localhost:3000/deliver/telegram",
1227
+ guardianChatId: "guardian-chat-1",
1228
+ assistantId: "self",
1229
+ },
1230
+ });
1231
+
1232
+ expect(result.applied).toBe(true);
1233
+
1234
+ // The requester is told to expect the code from the guardian; the secret is
1235
+ // never delivered to the requester's (possibly group) chat.
1236
+ const requesterReply = deliveredReplies.find(
1237
+ (r) => r.payload.chatId === "requester-chat-1",
1238
+ );
1239
+ expect(requesterReply).toBeDefined();
1240
+ expect(requesterReply!.payload.text).toContain("receive from the guardian");
1241
+ expect(requesterReply!.payload.text).not.toContain("123456");
1242
+ });
1243
+
1244
+ test("Slack shared-channel fallback posts an ephemeral notice to the channel when the DM fails", async () => {
1245
+ const req = createAccessRequest();
1246
+
1247
+ // Make the direct DM (to the U... user ID) fail so the courier fallback runs.
1248
+ failDeliveryWhen = (payload) => payload.chatId === REQUESTER_UID;
1249
+
1250
+ const result = await applyCanonicalGuardianDecision({
1251
+ requestId: req.id,
1252
+ action: "approve_once",
1253
+ actorContext: guardianActor({
1254
+ channel: "slack",
1255
+ actorExternalUserId: GUARDIAN_UID,
1256
+ }),
1257
+ channelDeliveryContext: {
1258
+ replyCallbackUrl:
1259
+ "http://localhost:3000/deliver/slack?threadTs=111.222",
1260
+ guardianChatId: "C_SHARED_CHANNEL",
1261
+ assistantId: "self",
1262
+ },
1263
+ });
1264
+
1265
+ expect(result.applied).toBe(true);
1266
+
1267
+ // The courier notice falls back to an ephemeral message targeting the
1268
+ // originating channel (C...), since chat.postEphemeral needs a channel ID —
1269
+ // not the requester's user ID.
1270
+ const courier = deliveredReplies.find(
1271
+ (r) =>
1272
+ typeof r.payload.text === "string" &&
1273
+ (r.payload.text as string).includes("receive from the guardian"),
1274
+ );
1275
+ expect(courier).toBeDefined();
1276
+ expect(courier!.payload.chatId).toBe("C_SHARED_CHANNEL");
1277
+ expect(courier!.payload.ephemeral).toBe(true);
1278
+ expect(courier!.payload.user).toBe(REQUESTER_UID);
1279
+ });
1280
+
1281
+ test("guardian-facing reply uses the requester's display name, not the raw ID", async () => {
1282
+ // Seed a contact so the resolver can resolve a display name.
1283
+ upsertContactChannel({
1284
+ sourceChannel: "slack",
1285
+ externalUserId: REQUESTER_UID,
1286
+ displayName: "Alice",
1287
+ status: "unverified",
1288
+ });
1289
+
1290
+ const req = createAccessRequest();
1291
+
1292
+ const result = await applyCanonicalGuardianDecision({
1293
+ requestId: req.id,
1294
+ action: "approve_once",
1295
+ // Desktop decision → resolver returns guardianReplyText for assertion.
1296
+ actorContext: guardianActor({
1297
+ channel: "vellum",
1298
+ actorExternalUserId: undefined,
1299
+ }),
1300
+ });
1301
+
1302
+ expect(result.applied).toBe(true);
1303
+ const replyText = result.applied ? result.resolverReplyText : undefined;
1304
+ expect(replyText).toContain("Alice");
1305
+ expect(replyText).not.toContain(REQUESTER_UID);
1306
+ });
1307
+ });
@@ -108,7 +108,7 @@ describe("trusted contact verification → member activation", () => {
108
108
  expect(contactResult).not.toBeNull();
109
109
  expect(contactResult!.channel.status).toBe("active");
110
110
  expect(contactResult!.channel.policy).toBe("allow");
111
- expect(contactResult!.channel.externalUserId).toBe("requester-user-123");
111
+ expect(contactResult!.channel.address).toBe("requester-user-123");
112
112
  expect(contactResult!.channel.externalChatId).toBe("requester-chat-123");
113
113
  expect(contactResult!.contact.displayName).toBe("Requester Name");
114
114
  expect(contactResult!.channel.type).toBe("telegram");
@@ -390,9 +390,7 @@ describe("trusted contact verification → member activation", () => {
390
390
  // The original guardian binding should remain intact
391
391
  const guardianResult = findGuardianForChannel("telegram");
392
392
  expect(guardianResult).not.toBeNull();
393
- expect(guardianResult!.channel.externalUserId).toBe(
394
- "guardian-user-original",
395
- );
393
+ expect(guardianResult!.channel.address).toBe("guardian-user-original");
396
394
  });
397
395
 
398
396
  test("guardian inbound verification succeeds but does not create binding", async () => {
@@ -92,8 +92,21 @@ let mockRouteSetupResult: {
92
92
  },
93
93
  };
94
94
 
95
+ // Captures the last context passed to routeSetup so preflight tests can
96
+ // assert the resolved phone admission policy was threaded through.
97
+ let lastRouteSetupCtx: Record<string, unknown> | null = null;
95
98
  mock.module("../calls/relay-setup-router.js", () => ({
96
- routeSetup: () => mockRouteSetupResult,
99
+ routeSetup: (ctx: Record<string, unknown>) => {
100
+ lastRouteSetupCtx = ctx;
101
+ return mockRouteSetupResult;
102
+ },
103
+ }));
104
+
105
+ // Mock the phone channel admission reader used by the media-stream preflight.
106
+ // Tests override mockAdmissionPolicy to exercise floor classification.
107
+ let mockAdmissionPolicy: unknown = null;
108
+ mock.module("../calls/channel-admission-reader.js", () => ({
109
+ getChannelAdmissionPolicy: async () => mockAdmissionPolicy,
97
110
  }));
98
111
 
99
112
  mock.module("../config/env.js", () => ({
@@ -454,6 +467,9 @@ describe("twilio webhook routes", () => {
454
467
  mockTwilioApiValidationBody = JSON.stringify({ sid: "AC_validated" });
455
468
  // Reset STT config to defaults between tests
456
469
  mockConfigObj.services.stt.provider = "deepgram" as any;
470
+ // Reset admission policy + captured routeSetup context between tests
471
+ mockAdmissionPolicy = null;
472
+ lastRouteSetupCtx = null;
457
473
  // Reset routeSetup mock to default normal_call
458
474
  mockRouteSetupResult = {
459
475
  outcome: { action: "normal_call", isInbound: true },
@@ -1228,6 +1244,70 @@ describe("twilio webhook routes", () => {
1228
1244
  expect(twiml).not.toContain("<ConversationRelay");
1229
1245
  });
1230
1246
 
1247
+ test("media-stream: resolves the phone admission floor and threads it into the preflight routeSetup", async () => {
1248
+ mockConfigObj.services.stt.provider = "openai-whisper" as any;
1249
+ mockAdmissionPolicy = "guardian_only";
1250
+ mockRouteSetupResult = {
1251
+ outcome: { action: "normal_call", isInbound: true },
1252
+ resolved: {
1253
+ assistantId: "self",
1254
+ isInbound: true,
1255
+ otherPartyNumber: "+15559998888",
1256
+ actorTrust: { trustClass: "guardian", memberRecord: null },
1257
+ },
1258
+ };
1259
+
1260
+ const session = createTestSession("conv-ms-floor-1", "CA_ms_floor_1");
1261
+ const req = makeVoiceRequest(session.id, { CallSid: "CA_ms_floor_1" });
1262
+
1263
+ const res = await handleVoiceWebhook(req);
1264
+ expect(res.status).toBe(200);
1265
+
1266
+ // The resolved floor was passed into the preflight routeSetup so its
1267
+ // classification matches the real stream-start enforcement.
1268
+ expect(lastRouteSetupCtx).not.toBeNull();
1269
+ expect(lastRouteSetupCtx?.admissionPolicy).toBe("guardian_only");
1270
+ });
1271
+
1272
+ test("media-stream: floor-denied caller classified deny still produces Stream TwiML (deny handled at stream level)", async () => {
1273
+ mockConfigObj.services.stt.provider = "openai-whisper" as any;
1274
+ mockAdmissionPolicy = "guardian_only";
1275
+ // With the floor wired, the real router returns `deny` for a
1276
+ // below-floor trusted_contact caller; the mock reflects that.
1277
+ mockRouteSetupResult = {
1278
+ outcome: {
1279
+ action: "deny",
1280
+ message:
1281
+ "This number is not authorized to reach the assistant right now.",
1282
+ logReason: "Inbound voice admission floor: guardian_only",
1283
+ },
1284
+ resolved: {
1285
+ assistantId: "self",
1286
+ isInbound: true,
1287
+ otherPartyNumber: "+14155550000",
1288
+ actorTrust: { trustClass: "trusted_contact", memberRecord: null },
1289
+ },
1290
+ };
1291
+
1292
+ const session = createTestSession(
1293
+ "conv-ms-floor-deny-1",
1294
+ "CA_ms_floor_deny_1",
1295
+ );
1296
+ const req = makeVoiceRequest(session.id, {
1297
+ CallSid: "CA_ms_floor_deny_1",
1298
+ });
1299
+
1300
+ const res = await handleVoiceWebhook(req);
1301
+ expect(res.status).toBe(200);
1302
+
1303
+ // Policy was threaded; deny is supported on media-stream, so Stream
1304
+ // TwiML is still produced (denial spoken + torn down at stream start).
1305
+ expect(lastRouteSetupCtx?.admissionPolicy).toBe("guardian_only");
1306
+ const twiml = await res.text();
1307
+ expect(twiml).toContain("<Stream");
1308
+ expect(twiml).not.toContain("<ConversationRelay");
1309
+ });
1310
+
1231
1311
  test("media-stream: deny setup proceeds with Stream TwiML (deny is handled at stream level)", async () => {
1232
1312
  mockConfigObj.services.stt.provider = "openai-whisper" as any;
1233
1313
  mockRouteSetupResult = {
@@ -368,7 +368,6 @@ describe("redeemVoiceInviteCode", () => {
368
368
  {
369
369
  type: "phone",
370
370
  address: phone,
371
- externalUserId: phone,
372
371
  status: "revoked",
373
372
  },
374
373
  ],
@@ -0,0 +1,30 @@
1
+ import { describe, expect, test } from "bun:test";
2
+
3
+ import { isWeakOpenModel } from "../providers/weak-open-model.js";
4
+
5
+ describe("isWeakOpenModel", () => {
6
+ test("matches weak open models across provider naming conventions", () => {
7
+ for (const model of [
8
+ "accounts/fireworks/models/minimax-m3",
9
+ "minimax/minimax-m3",
10
+ "moonshotai/kimi-k2.6",
11
+ "accounts/fireworks/models/kimi-k2p6",
12
+ "deepseek/deepseek-chat",
13
+ "accounts/fireworks/models/glm-5p2",
14
+ ]) {
15
+ expect(isWeakOpenModel(model)).toBe(true);
16
+ }
17
+ });
18
+
19
+ test("does not match capable models", () => {
20
+ for (const model of ["claude-opus-4-8", "claude-sonnet-4-6", "gpt-5.5"]) {
21
+ expect(isWeakOpenModel(model)).toBe(false);
22
+ }
23
+ });
24
+
25
+ test("is false for null/undefined/empty", () => {
26
+ expect(isWeakOpenModel(null)).toBe(false);
27
+ expect(isWeakOpenModel(undefined)).toBe(false);
28
+ expect(isWeakOpenModel("")).toBe(false);
29
+ });
30
+ });
@@ -0,0 +1,149 @@
1
+ /**
2
+ * Tests for workspace migration `105-enable-memory-v3-live-for-new-workspaces`.
3
+ *
4
+ * The schema default for `memory.v3.live` is false so existing assistants stay
5
+ * on v2. The migration persists `memory.v3.live = true` for FRESH workspaces
6
+ * only (switching new assistants onto memory-v3 at creation), creating
7
+ * `config.json` when it does not exist, leaving existing workspaces and
8
+ * explicit values untouched, and being idempotent.
9
+ */
10
+
11
+ import {
12
+ existsSync,
13
+ mkdtempSync,
14
+ readFileSync,
15
+ rmSync,
16
+ writeFileSync,
17
+ } from "node:fs";
18
+ import { tmpdir } from "node:os";
19
+ import { join } from "node:path";
20
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
21
+
22
+ import { MemoryV3ConfigSchema } from "../config/schemas/memory-v3.js";
23
+ import { enableMemoryV3LiveForNewWorkspacesMigration } from "../workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.js";
24
+ import type { MigrationRunContext } from "../workspace/migrations/types.js";
25
+
26
+ const NEW_WORKSPACE_CTX: MigrationRunContext = { isNewWorkspace: true };
27
+ const UPGRADE_CTX: MigrationRunContext = { isNewWorkspace: false };
28
+
29
+ let workspaceDir: string;
30
+ let configPath: string;
31
+
32
+ beforeEach(() => {
33
+ workspaceDir = mkdtempSync(join(tmpdir(), "vellum-migration-105-test-"));
34
+ configPath = join(workspaceDir, "config.json");
35
+ });
36
+
37
+ afterEach(() => {
38
+ if (existsSync(workspaceDir)) {
39
+ rmSync(workspaceDir, { recursive: true, force: true });
40
+ }
41
+ });
42
+
43
+ function readConfig(): Record<string, unknown> {
44
+ return JSON.parse(readFileSync(configPath, "utf-8"));
45
+ }
46
+
47
+ describe("105-enable-memory-v3-live-for-new-workspaces migration", () => {
48
+ test("has correct id and description", () => {
49
+ expect(enableMemoryV3LiveForNewWorkspacesMigration.id).toBe(
50
+ "105-enable-memory-v3-live-for-new-workspaces",
51
+ );
52
+ expect(enableMemoryV3LiveForNewWorkspacesMigration.description).toContain(
53
+ "memory.v3.live",
54
+ );
55
+ });
56
+
57
+ test("schema default is off so existing assistants stay on v2", () => {
58
+ expect(MemoryV3ConfigSchema.parse({}).live).toBe(false);
59
+ });
60
+
61
+ test("creates config.json with memory.v3.live=true for a fresh workspace", () => {
62
+ enableMemoryV3LiveForNewWorkspacesMigration.run(
63
+ workspaceDir,
64
+ NEW_WORKSPACE_CTX,
65
+ );
66
+
67
+ expect(readConfig()).toEqual({ memory: { v3: { live: true } } });
68
+ });
69
+
70
+ test("adds memory.v3.live to a fresh workspace's existing config, preserving siblings", () => {
71
+ writeFileSync(
72
+ configPath,
73
+ JSON.stringify({ name: "Assistant", memory: { enabled: true } }),
74
+ "utf-8",
75
+ );
76
+
77
+ enableMemoryV3LiveForNewWorkspacesMigration.run(
78
+ workspaceDir,
79
+ NEW_WORKSPACE_CTX,
80
+ );
81
+
82
+ expect(readConfig()).toEqual({
83
+ name: "Assistant",
84
+ memory: { enabled: true, v3: { live: true } },
85
+ });
86
+ });
87
+
88
+ test("skips existing workspaces so they stay on v2 (no config written)", () => {
89
+ enableMemoryV3LiveForNewWorkspacesMigration.run(workspaceDir, UPGRADE_CTX);
90
+
91
+ expect(existsSync(configPath)).toBe(false);
92
+ });
93
+
94
+ test("treats a missing context as an existing workspace", () => {
95
+ enableMemoryV3LiveForNewWorkspacesMigration.run(workspaceDir);
96
+
97
+ expect(existsSync(configPath)).toBe(false);
98
+ });
99
+
100
+ test("leaves an explicit memory.v3.live value untouched on a fresh workspace", () => {
101
+ const original = JSON.stringify({ memory: { v3: { live: false } } });
102
+ writeFileSync(configPath, original, "utf-8");
103
+
104
+ enableMemoryV3LiveForNewWorkspacesMigration.run(
105
+ workspaceDir,
106
+ NEW_WORKSPACE_CTX,
107
+ );
108
+
109
+ expect(readFileSync(configPath, "utf-8")).toBe(original);
110
+ });
111
+
112
+ test("leaves malformed config.json untouched", () => {
113
+ writeFileSync(configPath, "{ not json", "utf-8");
114
+
115
+ enableMemoryV3LiveForNewWorkspacesMigration.run(
116
+ workspaceDir,
117
+ NEW_WORKSPACE_CTX,
118
+ );
119
+
120
+ expect(readFileSync(configPath, "utf-8")).toBe("{ not json");
121
+ });
122
+
123
+ test("is idempotent", () => {
124
+ enableMemoryV3LiveForNewWorkspacesMigration.run(
125
+ workspaceDir,
126
+ NEW_WORKSPACE_CTX,
127
+ );
128
+ const afterFirst = readFileSync(configPath, "utf-8");
129
+
130
+ enableMemoryV3LiveForNewWorkspacesMigration.run(
131
+ workspaceDir,
132
+ NEW_WORKSPACE_CTX,
133
+ );
134
+
135
+ expect(readFileSync(configPath, "utf-8")).toBe(afterFirst);
136
+ });
137
+
138
+ test("down is a no-op", () => {
139
+ enableMemoryV3LiveForNewWorkspacesMigration.run(
140
+ workspaceDir,
141
+ NEW_WORKSPACE_CTX,
142
+ );
143
+ const before = readFileSync(configPath, "utf-8");
144
+
145
+ enableMemoryV3LiveForNewWorkspacesMigration.down(workspaceDir);
146
+
147
+ expect(readFileSync(configPath, "utf-8")).toBe(before);
148
+ });
149
+ });