@vellumai/assistant 0.10.2-dev.202606250318.5e7cfb0 → 0.10.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 (430) hide show
  1. package/bun.lock +0 -20
  2. package/docs/workspace-tools.md +33 -42
  3. package/eslint-rules/cli-no-daemon-internals.js +0 -6
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -31
  5. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -44
  6. package/node_modules/@vellumai/gateway-client/src/index.ts +0 -14
  7. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -17
  8. package/node_modules/@vellumai/service-contracts/package.json +0 -1
  9. package/node_modules/@vellumai/service-contracts/src/index.ts +0 -1
  10. package/openapi.yaml +0 -155
  11. package/package.json +1 -4
  12. package/scripts/test.sh +15 -36
  13. package/src/__tests__/actor-token-service.test.ts +14 -36
  14. package/src/__tests__/agent-loop-override-profile.test.ts +0 -1
  15. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -2
  16. package/src/__tests__/agent-wake-override-profile.test.ts +0 -2
  17. package/src/__tests__/annotate-activity-metadata.test.ts +0 -2
  18. package/src/__tests__/annotate-risk-options.test.ts +0 -2
  19. package/src/__tests__/approval-cascade.test.ts +0 -2
  20. package/src/__tests__/assistant-attachments.test.ts +0 -42
  21. package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
  22. package/src/__tests__/btw-routes.test.ts +0 -2
  23. package/src/__tests__/build-persisted-content.test.ts +0 -2
  24. package/src/__tests__/call-controller.test.ts +0 -19
  25. package/src/__tests__/channel-guardian.test.ts +58 -94
  26. package/src/__tests__/channel-reply-delivery.test.ts +0 -2
  27. package/src/__tests__/compaction-events.test.ts +0 -2
  28. package/src/__tests__/compaction.benchmark.test.ts +0 -2
  29. package/src/__tests__/compactor-call-site-logging.test.ts +0 -2
  30. package/src/__tests__/compactor-low-watermark-cut.test.ts +0 -2
  31. package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -2
  32. package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -2
  33. package/src/__tests__/compactor-web-search-strip.test.ts +0 -2
  34. package/src/__tests__/config-loader-backfill.test.ts +10 -123
  35. package/src/__tests__/config-schema.test.ts +0 -1
  36. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -31
  37. package/src/__tests__/contacts-relay-reads.test.ts +15 -13
  38. package/src/__tests__/conversation-abort-tool-results.test.ts +0 -2
  39. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -2
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -2
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
  42. package/src/__tests__/conversation-agent-loop.test.ts +0 -134
  43. package/src/__tests__/conversation-analysis-routes.test.ts +0 -2
  44. package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -2
  45. package/src/__tests__/conversation-confirmation-signals.test.ts +0 -2
  46. package/src/__tests__/conversation-history-web-search.test.ts +0 -2
  47. package/src/__tests__/conversation-load-history-repair.test.ts +0 -2
  48. package/src/__tests__/conversation-load-history-stripped.test.ts +0 -2
  49. package/src/__tests__/conversation-pairing.test.ts +0 -2
  50. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +0 -2
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -2
  53. package/src/__tests__/conversation-queue.test.ts +0 -91
  54. package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -14
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -14
  56. package/src/__tests__/conversation-slash-queue.test.ts +0 -2
  57. package/src/__tests__/conversation-slash-unknown.test.ts +0 -2
  58. package/src/__tests__/conversation-speed-override.test.ts +0 -2
  59. package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -29
  60. package/src/__tests__/conversation-title-service.test.ts +0 -2
  61. package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -47
  62. package/src/__tests__/conversation-usage.test.ts +0 -2
  63. package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -2
  64. package/src/__tests__/conversation-workspace-injection.test.ts +0 -2
  65. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -2
  66. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  67. package/src/__tests__/db-migration-rollback.test.ts +171 -205
  68. package/src/__tests__/db-test-helpers.ts +4 -5
  69. package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -4
  70. package/src/__tests__/disk-pressure-guard.test.ts +0 -41
  71. package/src/__tests__/dm-persistence.test.ts +0 -2
  72. package/src/__tests__/emit-signal-routing-intent.test.ts +5 -10
  73. package/src/__tests__/events-dev-bypass-actor.test.ts +1 -7
  74. package/src/__tests__/exploration-drift-hook.test.ts +2 -3
  75. package/src/__tests__/filing-service.test.ts +0 -2
  76. package/src/__tests__/guardian-binding-drift-heal.test.ts +10 -75
  77. package/src/__tests__/guardian-dispatch.test.ts +1 -95
  78. package/src/__tests__/guardian-outbound-http.test.ts +0 -13
  79. package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -2
  80. package/src/__tests__/heartbeat-service.test.ts +0 -2
  81. package/src/__tests__/helpers/channel-test-adapter.ts +7 -1
  82. package/src/__tests__/host-app-control-routes.test.ts +30 -24
  83. package/src/__tests__/host-bash-routes.test.ts +41 -31
  84. package/src/__tests__/host-browser-routes.test.ts +32 -26
  85. package/src/__tests__/host-cu-routes-targeted.test.ts +33 -25
  86. package/src/__tests__/host-file-routes-targeted.test.ts +52 -40
  87. package/src/__tests__/host-transfer-routes-targeted.test.ts +43 -31
  88. package/src/__tests__/http-user-message-parity.test.ts +8 -290
  89. package/src/__tests__/inbound-invite-redemption.test.ts +0 -28
  90. package/src/__tests__/inbound-slack-persistence.test.ts +0 -2
  91. package/src/__tests__/invite-redemption-service.test.ts +0 -198
  92. package/src/__tests__/llm-context-normalization.test.ts +0 -105
  93. package/src/__tests__/llm-request-log-error-payload.test.ts +9 -71
  94. package/src/__tests__/llm-usage-store.test.ts +0 -25
  95. package/src/__tests__/mcp-health-check.test.ts +1 -2
  96. package/src/__tests__/media-stream-server-integration.test.ts +0 -127
  97. package/src/__tests__/memory-retrieval-hook.test.ts +0 -2
  98. package/src/__tests__/messaging-send-tool.test.ts +0 -2
  99. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  100. package/src/__tests__/mtime-cache.test.ts +5 -146
  101. package/src/__tests__/native-web-search.test.ts +0 -2
  102. package/src/__tests__/non-member-access-request.test.ts +17 -189
  103. package/src/__tests__/notification-broadcaster.test.ts +0 -4
  104. package/src/__tests__/notification-decision-recipient-context.test.ts +32 -33
  105. package/src/__tests__/notification-deep-link.test.ts +0 -6
  106. package/src/__tests__/notification-guardian-path.test.ts +0 -19
  107. package/src/__tests__/openai-provider.test.ts +12 -22
  108. package/src/__tests__/openai-responses-provider.test.ts +2 -12
  109. package/src/__tests__/outbound-slack-persistence.test.ts +0 -2
  110. package/src/__tests__/pending-interactions-resolved-event.test.ts +4 -7
  111. package/src/__tests__/persistence-secret-redaction.test.ts +0 -2
  112. package/src/__tests__/plugin-bootstrap.test.ts +73 -3
  113. package/src/__tests__/plugin-route-contribution.test.ts +17 -4
  114. package/src/__tests__/plugin-tool-contribution.test.ts +18 -3
  115. package/src/__tests__/plugin-types.test.ts +2 -0
  116. package/src/__tests__/process-message-background-slack.test.ts +0 -2
  117. package/src/__tests__/process-message-display-content.test.ts +0 -2
  118. package/src/__tests__/provider-error-scenarios.test.ts +4 -5
  119. package/src/__tests__/provider-usage-tracking.test.ts +0 -39
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -2
  121. package/src/__tests__/registry.test.ts +1 -4
  122. package/src/__tests__/relay-server.test.ts +25 -694
  123. package/src/__tests__/runtime-attachment-metadata.test.ts +1 -0
  124. package/src/__tests__/secret-ingress-http.test.ts +0 -14
  125. package/src/__tests__/send-endpoint-busy.test.ts +8 -30
  126. package/src/__tests__/skills.test.ts +0 -44
  127. package/src/__tests__/slack-inbound-verification.test.ts +2 -47
  128. package/src/__tests__/stt-hints.test.ts +13 -44
  129. package/src/__tests__/subagent-detail.test.ts +0 -27
  130. package/src/__tests__/subagent-disposal.test.ts +0 -65
  131. package/src/__tests__/subagent-notify-parent.test.ts +0 -2
  132. package/src/__tests__/subagent-role-registry.test.ts +2 -7
  133. package/src/__tests__/subagent-spawn-tool-fork.test.ts +0 -2
  134. package/src/__tests__/subagent-tools.test.ts +0 -2
  135. package/src/__tests__/suggestion-routes.test.ts +0 -2
  136. package/src/__tests__/title-generate-hook.test.ts +0 -2
  137. package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -2
  138. package/src/__tests__/tool-executor.test.ts +11 -16
  139. package/src/__tests__/tool-preview-lifecycle.test.ts +0 -2
  140. package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -2
  141. package/src/__tests__/tool-start-timestamp.test.ts +0 -2
  142. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
  143. package/src/__tests__/twilio-routes.test.ts +0 -96
  144. package/src/__tests__/ui-file-upload-surface.test.ts +0 -86
  145. package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
  146. package/src/__tests__/voice-invite-redemption.test.ts +0 -33
  147. package/src/__tests__/web-search-backend-failure.test.ts +0 -2
  148. package/src/__tests__/workspace-migration-remove-hooks.test.ts +35 -14
  149. package/src/__tests__/workspace-tool-loader.test.ts +2 -195
  150. package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
  151. package/src/agent/loop.ts +0 -56
  152. package/src/api/index.ts +1 -19
  153. package/src/api/responses/llm-request-log-entry.ts +0 -29
  154. package/src/api/responses/subagent-detail.ts +0 -17
  155. package/src/api/surfaces.ts +3 -39
  156. package/src/approvals/guardian-request-resolvers.ts +11 -1
  157. package/src/calls/__tests__/relay-setup-router.test.ts +4 -262
  158. package/src/calls/call-domain.ts +3 -3
  159. package/src/calls/guardian-dispatch.ts +8 -10
  160. package/src/calls/inbound-trust-reader.ts +1 -17
  161. package/src/calls/media-stream-server.ts +0 -21
  162. package/src/calls/relay-server.ts +50 -167
  163. package/src/calls/relay-setup-router.ts +7 -37
  164. package/src/calls/relay-verification.ts +4 -4
  165. package/src/calls/stt-hints.ts +12 -9
  166. package/src/calls/twilio-routes.ts +4 -14
  167. package/src/channels/types.ts +20 -10
  168. package/src/cli/commands/__tests__/cache.test.ts +1 -8
  169. package/src/cli/commands/cache.ts +181 -194
  170. package/src/cli/commands/db/__tests__/repair.test.ts +5 -6
  171. package/src/cli/commands/db/status.ts +1 -37
  172. package/src/cli/commands/mcp.ts +218 -252
  173. package/src/cli/commands/memory/index.ts +0 -2
  174. package/src/cli/commands/plugins.ts +3 -75
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +0 -102
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +1 -160
  177. package/src/cli/lib/list-installed-plugins.ts +1 -179
  178. package/src/config/__tests__/sync-gated-profiles.test.ts +3 -11
  179. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +17 -27
  180. package/src/config/bundled-skills/contacts/tools/contact-search.ts +3 -13
  181. package/src/config/bundled-skills/subagent/SKILL.md +1 -1
  182. package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
  183. package/src/config/feature-flag-registry.json +13 -5
  184. package/src/config/loader.ts +5 -38
  185. package/src/config/schemas/__tests__/memory-v3.test.ts +0 -1
  186. package/src/config/schemas/memory-lifecycle.ts +0 -12
  187. package/src/config/schemas/memory-v3.ts +0 -7
  188. package/src/config/schemas/memory.ts +0 -4
  189. package/src/config/schemas/timeouts.ts +0 -8
  190. package/src/config/seed-inference-profiles.ts +11 -21
  191. package/src/config/skills.ts +5 -27
  192. package/src/config/sync-gated-profiles.ts +13 -12
  193. package/src/contacts/contacts-write.ts +0 -3
  194. package/src/daemon/assistant-attachments.ts +4 -27
  195. package/src/daemon/conversation-agent-loop.ts +0 -28
  196. package/src/daemon/conversation-process.ts +16 -35
  197. package/src/daemon/conversation-surfaces.ts +38 -111
  198. package/src/daemon/conversation-tool-setup.ts +16 -50
  199. package/src/daemon/conversation.ts +1 -13
  200. package/src/daemon/disk-pressure-guard.ts +2 -12
  201. package/src/daemon/event-loop-watchdog.ts +1 -28
  202. package/src/daemon/external-plugins-bootstrap.ts +34 -4
  203. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -25
  204. package/src/daemon/handlers/config-a2a.ts +14 -6
  205. package/src/daemon/handlers/config-channels.ts +22 -78
  206. package/src/daemon/handlers/conversations.ts +0 -77
  207. package/src/daemon/lifecycle.ts +0 -4
  208. package/src/daemon/mcp-reload-service.ts +0 -10
  209. package/src/daemon/memory-v2-startup.test.ts +0 -72
  210. package/src/daemon/memory-v2-startup.ts +19 -87
  211. package/src/daemon/message-types/conversations.ts +0 -2
  212. package/src/daemon/message-types/surfaces.ts +12 -12
  213. package/src/daemon/server.ts +4 -0
  214. package/src/daemon/shutdown-handlers.ts +0 -20
  215. package/src/daemon/tool-setup-types.ts +0 -9
  216. package/src/daemon/workspace-tools-watcher.ts +328 -0
  217. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  218. package/src/ipc/assistant-server.ts +2 -2
  219. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
  220. package/src/mcp/client.ts +1 -15
  221. package/src/mcp/mcp-auth-orchestrator.ts +1 -6
  222. package/src/mcp/mcp-oauth-provider.ts +8 -19
  223. package/src/memory/__tests__/memory-retrospective-job.test.ts +0 -8
  224. package/src/memory/conversation-crud.ts +0 -38
  225. package/src/memory/db-connection.ts +3 -22
  226. package/src/memory/db-init.ts +502 -36
  227. package/src/memory/db-singleton.ts +4 -6
  228. package/src/memory/jobs-worker.ts +0 -58
  229. package/src/memory/llm-request-log-store.ts +1 -26
  230. package/src/memory/llm-usage-store.ts +20 -48
  231. package/src/memory/memory-retrospective-job.ts +8 -9
  232. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +56 -130
  233. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  234. package/src/memory/migrations/registry.ts +573 -0
  235. package/src/memory/migrations/run-migrations.ts +6 -90
  236. package/src/memory/migrations/validate-migration-state.ts +66 -101
  237. package/src/memory/schema/conversations.ts +0 -9
  238. package/src/memory/schema/infrastructure.ts +0 -20
  239. package/src/memory/v2/__tests__/cli-command-store.test.ts +0 -25
  240. package/src/memory/v2/__tests__/skill-store.test.ts +0 -80
  241. package/src/memory/v2/cli-command-store.ts +38 -75
  242. package/src/memory/v2/prompts/consolidation.ts +82 -13
  243. package/src/memory/v2/prompts/router.ts +93 -21
  244. package/src/memory/v2/skill-store.ts +31 -68
  245. package/src/notifications/__tests__/broadcaster.test.ts +8 -16
  246. package/src/notifications/__tests__/decision-engine.test.ts +9 -78
  247. package/src/notifications/broadcaster.ts +1 -8
  248. package/src/notifications/decision-engine.ts +7 -15
  249. package/src/notifications/destination-resolver.ts +24 -68
  250. package/src/notifications/emit-signal.ts +14 -39
  251. package/src/permissions/question-prompter.test.ts +1 -1
  252. package/src/permissions/question-prompter.ts +4 -7
  253. package/src/plugin-api/index.ts +6 -6
  254. package/src/plugin-api/types.ts +5 -3
  255. package/src/plugin-api/vision-support.test.ts +4 -28
  256. package/src/plugin-api/vision-support.ts +31 -66
  257. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -161
  258. package/src/plugins/defaults/advisor/consult.ts +6 -110
  259. package/src/plugins/defaults/advisor/steering.ts +2 -14
  260. package/src/plugins/defaults/advisor/tools/advisor.ts +5 -32
  261. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +1 -2
  262. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +7 -47
  263. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +11 -10
  264. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +20 -12
  265. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +11 -42
  266. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +3 -33
  267. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +4 -48
  268. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +8 -4
  269. package/src/plugins/defaults/memory-v3-shadow/injector.ts +15 -43
  270. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +2 -11
  271. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -77
  272. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +11 -12
  273. package/src/plugins/mtime-cache.ts +291 -76
  274. package/src/plugins/pipeline.ts +13 -111
  275. package/src/plugins/types.ts +2 -0
  276. package/src/providers/anthropic/client.ts +0 -5
  277. package/src/providers/call-site-routing.ts +0 -4
  278. package/src/providers/model-catalog.ts +0 -16
  279. package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
  280. package/src/providers/openai/chat-completions-provider.ts +83 -37
  281. package/src/providers/openai/responses-provider.ts +46 -50
  282. package/src/providers/openrouter/client.ts +0 -5
  283. package/src/providers/provider-send-message.ts +0 -4
  284. package/src/providers/ratelimit.ts +0 -4
  285. package/src/providers/retry.ts +0 -4
  286. package/src/providers/types.ts +0 -9
  287. package/src/providers/usage-tracking.ts +0 -4
  288. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +3 -335
  289. package/src/runtime/access-request-helper.ts +39 -19
  290. package/src/runtime/actor-trust-resolver.ts +2 -2
  291. package/src/runtime/assistant-event-hub.ts +1 -1
  292. package/src/runtime/assistant-stream-state.ts +2 -9
  293. package/src/runtime/auth/require-bound-guardian.ts +11 -21
  294. package/src/runtime/channel-verification-service.ts +31 -56
  295. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  296. package/src/runtime/guardian-vellum-migration.ts +7 -66
  297. package/src/runtime/invite-redemption-service.ts +187 -198
  298. package/src/runtime/local-actor-identity.ts +11 -76
  299. package/src/runtime/pending-interactions.ts +1 -11
  300. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +5 -56
  301. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  302. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +0 -187
  303. package/src/runtime/routes/browser-routes.ts +1 -1
  304. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -13
  305. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  306. package/src/runtime/routes/contact-routes.ts +32 -8
  307. package/src/runtime/routes/conversation-cli-routes.ts +5 -4
  308. package/src/runtime/routes/conversation-list-routes.ts +7 -4
  309. package/src/runtime/routes/conversation-query-routes.ts +0 -72
  310. package/src/runtime/routes/conversation-routes.ts +85 -84
  311. package/src/runtime/routes/events-routes.ts +2 -2
  312. package/src/runtime/routes/global-search-routes.ts +1 -3
  313. package/src/runtime/routes/guardian-action-routes.ts +5 -4
  314. package/src/runtime/routes/host-app-control-routes.ts +4 -5
  315. package/src/runtime/routes/host-bash-routes.ts +4 -5
  316. package/src/runtime/routes/host-browser-routes.ts +11 -9
  317. package/src/runtime/routes/host-cu-routes.ts +4 -5
  318. package/src/runtime/routes/host-file-routes.ts +4 -5
  319. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  320. package/src/runtime/routes/http-adapter.ts +1 -1
  321. package/src/runtime/routes/identity-routes.ts +2 -3
  322. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  323. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +5 -97
  324. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +49 -61
  325. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -16
  326. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  327. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +8 -21
  328. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +3 -14
  329. package/src/runtime/routes/index.ts +0 -2
  330. package/src/runtime/routes/llm-context-normalization.ts +0 -83
  331. package/src/runtime/routes/mcp-auth-routes.ts +19 -171
  332. package/src/runtime/routes/migration-rollback-routes.ts +3 -4
  333. package/src/runtime/routes/migration-routes.ts +1 -4
  334. package/src/runtime/routes/subagents-routes.ts +0 -5
  335. package/src/runtime/routes/surface-action-routes.ts +56 -42
  336. package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
  337. package/src/runtime/services/conversation-serializer.ts +9 -7
  338. package/src/runtime/tool-grant-request-helper.ts +3 -3
  339. package/src/runtime/trust-verdict-consumer.ts +9 -85
  340. package/src/runtime/verification-outbound-actions.ts +18 -18
  341. package/src/signals/user-message.ts +0 -16
  342. package/src/subagent/manager.ts +0 -9
  343. package/src/subagent/types.ts +3 -3
  344. package/src/telemetry/types.ts +1 -34
  345. package/src/telemetry/usage-telemetry-reporter.test.ts +2 -3
  346. package/src/telemetry/usage-telemetry-reporter.ts +3 -87
  347. package/src/tools/ask-question/ask-question-tool.test.ts +0 -29
  348. package/src/tools/ask-question/ask-question-tool.ts +0 -13
  349. package/src/tools/executor.ts +4 -4
  350. package/src/tools/registry.ts +0 -18
  351. package/src/tools/shared/filesystem/path-policy.ts +5 -12
  352. package/src/tools/tool-approval-handler.ts +1 -1
  353. package/src/tools/tool-defaults.ts +2 -9
  354. package/src/tools/tool-manifest.ts +0 -3
  355. package/src/tools/types.ts +2 -17
  356. package/src/tools/workspace-tools/loader.ts +244 -348
  357. package/src/util/errors.ts +1 -26
  358. package/src/util/platform.ts +0 -5
  359. package/src/workflows/library.test.ts +0 -140
  360. package/src/workflows/library.ts +28 -82
  361. package/src/workspace/migrations/017-seed-persona-dirs.ts +34 -3
  362. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +24 -3
  363. package/src/workspace/migrations/048-remove-workspace-hooks.ts +66 -14
  364. package/src/workspace/migrations/registry.ts +0 -2
  365. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +0 -91
  366. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +0 -48
  367. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +0 -28
  368. package/node_modules/@vellumai/service-contracts/src/channels.ts +0 -41
  369. package/src/__tests__/code-search-tool.test.ts +0 -585
  370. package/src/__tests__/guardian-expiry-notifier.test.ts +0 -282
  371. package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -390
  372. package/src/__tests__/plugin-pipeline.test.ts +0 -96
  373. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +0 -102
  374. package/src/__tests__/steer-on-enqueue-question.test.ts +0 -181
  375. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +0 -208
  376. package/src/agent/loop-exclusive-tool.test.ts +0 -150
  377. package/src/api/constants/sse-replay.ts +0 -41
  378. package/src/api/events/conversation-notice.ts +0 -26
  379. package/src/approvals/guardian-channel-delivery.ts +0 -30
  380. package/src/approvals/guardian-expiry-notifier.ts +0 -148
  381. package/src/cli/commands/memory/__tests__/worker.test.ts +0 -302
  382. package/src/cli/commands/memory/worker.ts +0 -175
  383. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -143
  384. package/src/config/prune-seeded-callsite-defaults.ts +0 -110
  385. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -129
  386. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +0 -312
  387. package/src/contacts/__tests__/member-write-relay.test.ts +0 -202
  388. package/src/contacts/guardian-delivery-reader.ts +0 -223
  389. package/src/contacts/member-write-relay.ts +0 -189
  390. package/src/daemon/conversation-notices.ts +0 -60
  391. package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -225
  392. package/src/hooks/hook-loader.ts +0 -341
  393. package/src/mcp/mcp-header-store.ts +0 -134
  394. package/src/memory/__tests__/301-create-watchdog-events.test.ts +0 -110
  395. package/src/memory/__tests__/prompt-override.test.ts +0 -192
  396. package/src/memory/__tests__/watchdog-events-store.test.ts +0 -161
  397. package/src/memory/migrations/300-add-processing-started-at.ts +0 -30
  398. package/src/memory/migrations/301-create-watchdog-events.ts +0 -45
  399. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +0 -224
  400. package/src/memory/prompt-override.ts +0 -129
  401. package/src/memory/steps.ts +0 -573
  402. package/src/memory/watchdog-events-store.ts +0 -87
  403. package/src/memory/worker-control.ts +0 -118
  404. package/src/memory/worker-process.ts +0 -72
  405. package/src/notifications/__tests__/connected-channels.test.ts +0 -114
  406. package/src/notifications/__tests__/destination-resolver.test.ts +0 -256
  407. package/src/onboarding/checkin-event.test.ts +0 -222
  408. package/src/onboarding/checkin-event.ts +0 -321
  409. package/src/onboarding/schedule-checkin.ts +0 -190
  410. package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
  411. package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
  412. package/src/plugins/defaults/advisor/context-pack.ts +0 -288
  413. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +0 -146
  414. package/src/plugins/surface-import.ts +0 -121
  415. package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -321
  416. package/src/providers/openai/api-error-normalization.ts +0 -270
  417. package/src/runtime/__tests__/channel-verification-service.test.ts +0 -133
  418. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +0 -181
  419. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +0 -66
  420. package/src/runtime/__tests__/local-principal-trust.test.ts +0 -164
  421. package/src/runtime/anchored-guardian.test.ts +0 -156
  422. package/src/runtime/anchored-guardian.ts +0 -135
  423. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +0 -99
  424. package/src/runtime/local-principal-trust.ts +0 -52
  425. package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -212
  426. package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -93
  427. package/src/runtime/routes/onboarding-checkin-routes.ts +0 -86
  428. package/src/tools/filesystem/search.ts +0 -543
  429. package/src/util/telemetry-db-path.ts +0 -24
  430. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +0 -134
@@ -87,19 +87,6 @@ mock.module("../prompts/user-reference.js", () => ({
87
87
  },
88
88
  }));
89
89
 
90
- // ── Guardian delivery reader mock ───────────────────────────────────
91
- //
92
- // resolveGuardianLabel primes its displayName from the gateway binding via
93
- // getGuardianDelivery at setup. Tests drive the binding through this list.
94
-
95
- // Tests set this to drive the guardian binding directly. When null (the
96
- // default), the guardian-delivery-reader mock below derives the binding from
97
- // the DB-seeded createGuardianBinding setup. Single mock registration lives
98
- // below since `mock.module` is process-global and last-write-wins in Bun.
99
- let mockGuardianDeliveryList:
100
- | Array<{ channelType: string; status: string; displayName: string | null }>
101
- | null = null;
102
-
103
90
  // ── Config mock ─────────────────────────────────────────────────────
104
91
 
105
92
  const mockConfig = {
@@ -176,103 +163,6 @@ mock.module("../calls/channel-admission-reader.js", () => ({
176
163
  },
177
164
  }));
178
165
 
179
- // ── Inbound trust verdict reader mock ───────────────────────────────
180
- //
181
- // Mid-call re-resolution (post verification/activation) prefers the gateway
182
- // verdict via getInboundTrustVerdict, falling back to local resolution on a
183
- // missing/failed/unusable verdict. Tests drive the verdict through
184
- // `mockMidCallVerdict`; null (the default) exercises the local fallback. As
185
- // with the admission reader, delegate to the real module for sibling files
186
- // that load later in the same worker.
187
- let mockMidCallVerdict:
188
- | import("@vellumai/gateway-client").TrustVerdict
189
- | null = null;
190
- // When set, the mid-call re-resolution verdict reader blocks on this gate
191
- // before returning, simulating a slow gateway round-trip so a test can drive a
192
- // prompt into the re-resolution await window. The gate targets the mid-call
193
- // re-resolution read (getInboundTrustVerdict) only — the per-caller redemption
194
- // gate read (getPhoneCallerVerdict) resolves immediately so invite redemption
195
- // reaches activation before the gated re-resolution runs.
196
- let mockMidCallVerdictGate: Promise<void> | null = null;
197
- const realInboundTrustReaderModule = {
198
- ...(await import("../calls/inbound-trust-reader.js")),
199
- };
200
- let inboundTrustMockActive = false;
201
- mock.module("../calls/inbound-trust-reader.js", () => ({
202
- ...realInboundTrustReaderModule,
203
- getInboundTrustVerdict: async (input: {
204
- channelType: import("../channels/types.js").ChannelId;
205
- actorExternalId?: string;
206
- }) => {
207
- if (!inboundTrustMockActive) {
208
- return realInboundTrustReaderModule.getInboundTrustVerdict(input);
209
- }
210
- if (mockMidCallVerdictGate) await mockMidCallVerdictGate;
211
- return mockMidCallVerdict;
212
- },
213
- getPhoneCallerVerdict: async (otherPartyNumber: string | undefined) => {
214
- if (!inboundTrustMockActive) {
215
- return realInboundTrustReaderModule.getPhoneCallerVerdict(
216
- otherPartyNumber,
217
- );
218
- }
219
- return mockMidCallVerdict;
220
- },
221
- }));
222
-
223
- // ── Guardian delivery reader ────────────────────────────────────────
224
- //
225
- // Guardian identity now resolves via the gateway delivery reader. Derive the
226
- // list from the DB-seeded guardian bindings so the existing createGuardianBinding
227
- // setup keeps driving guardian resolution without per-test changes.
228
- const realContactStoreModule = {
229
- ...(await import("../contacts/contact-store.js")),
230
- };
231
- mock.module("../contacts/guardian-delivery-reader.js", () => ({
232
- getGuardianDelivery: async () => {
233
- // Tests that set mockGuardianDeliveryList drive the binding directly;
234
- // otherwise derive from the DB-seeded createGuardianBinding bindings.
235
- if (mockGuardianDeliveryList) return mockGuardianDeliveryList;
236
- const guardians = realContactStoreModule.listGuardianChannels();
237
- if (!guardians) return [];
238
- return guardians.channels.map((ch) => ({
239
- channelType: ch.type,
240
- contactId: guardians.contact.id,
241
- principalId: guardians.contact.principalId ?? null,
242
- displayName: guardians.contact.displayName ?? null,
243
- address: ch.address,
244
- externalChatId: ch.externalChatId ?? null,
245
- status: ch.status,
246
- verifiedAt: ch.verifiedAt ?? null,
247
- }));
248
- },
249
- guardianForChannel: (
250
- list: Array<{ channelType: string; status: string }>,
251
- channelType: string,
252
- ) => list.find((g) => g.channelType === channelType && g.status === "active"),
253
- anyGuardian: (list: unknown[]) => list[0],
254
- }));
255
-
256
- // ── Trust verdict consumer spy ──────────────────────────────────────
257
- //
258
- // Tracks whether the verdict mapper produced the final mid-call context, so a
259
- // test can assert the local resolver was used instead (verdict not consumed).
260
- let trustVerdictMapperUsed = false;
261
- const realTrustVerdictConsumerModule = {
262
- ...(await import("../runtime/trust-verdict-consumer.js")),
263
- };
264
- mock.module("../runtime/trust-verdict-consumer.js", () => ({
265
- ...realTrustVerdictConsumerModule,
266
- trustContextFromVerdict: (
267
- ...args: Parameters<
268
- typeof realTrustVerdictConsumerModule.trustContextFromVerdict
269
- >
270
- ) => {
271
- trustVerdictMapperUsed = true;
272
- return realTrustVerdictConsumerModule.trustContextFromVerdict(...args);
273
- },
274
- }));
275
-
276
166
  // ── TTS provider mocks (for call-speech-output) ─────────────────────
277
167
 
278
168
  let mockTtsProviderId: string = "elevenlabs";
@@ -418,12 +308,10 @@ await initializeDb();
418
308
  // sibling files that load later in the same worker.
419
309
  beforeAll(() => {
420
310
  admissionMockActive = true;
421
- inboundTrustMockActive = true;
422
311
  });
423
312
 
424
313
  afterAll(() => {
425
314
  admissionMockActive = false;
426
- inboundTrustMockActive = false;
427
315
  resetDbForTesting();
428
316
  });
429
317
 
@@ -589,12 +477,8 @@ describe("relay-server", () => {
589
477
  inviteClaimGate = null;
590
478
  mockUserReference = "my human";
591
479
  mockAssistantName = "Vellum";
592
- mockGuardianDeliveryList = null;
593
480
  mockAdmissionPolicy = null;
594
481
  mockAdmissionGate = null;
595
- mockMidCallVerdict = null;
596
- mockMidCallVerdictGate = null;
597
- trustVerdictMapperUsed = false;
598
482
  mockSendMessage.mockImplementation(createMockProviderResponse(["Hello"]));
599
483
  mockConfig.calls.verification.enabled = false;
600
484
  mockConfig.calls.verification.maxAttempts = 3;
@@ -1746,7 +1630,7 @@ describe("relay-server", () => {
1746
1630
  // Guardian binding is NOT created by the assistant — the gateway owns
1747
1631
  // binding creation for inbound voice verification. The assistant only
1748
1632
  // transitions to connected state and starts the normal call flow.
1749
- const binding = await getGuardianBinding("self", "phone");
1633
+ const binding = getGuardianBinding("self", "phone");
1750
1634
  expect(binding).toBeNull();
1751
1635
 
1752
1636
  // Orchestrator greeting should have fired
@@ -1817,7 +1701,7 @@ describe("relay-server", () => {
1817
1701
  expect(relay.getConnectionState()).toBe("connected");
1818
1702
 
1819
1703
  // Binding is NOT created by the assistant — gateway owns this.
1820
- const binding = await getGuardianBinding("self", "phone");
1704
+ const binding = getGuardianBinding("self", "phone");
1821
1705
  expect(binding).toBeNull();
1822
1706
 
1823
1707
  // Greeting should have started
@@ -2339,9 +2223,6 @@ describe("relay-server", () => {
2339
2223
  await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
2340
2224
  }
2341
2225
 
2342
- // Let the fire-and-forget verification result handler flush
2343
- await new Promise((resolve) => setTimeout(resolve, 10));
2344
-
2345
2226
  // Verification should have succeeded
2346
2227
  expect(relay.isVerificationSessionActive()).toBe(false);
2347
2228
 
@@ -4791,7 +4672,7 @@ describe("relay-server", () => {
4791
4672
  expect(relay.getConnectionState()).toBe("connected");
4792
4673
 
4793
4674
  // Guardian binding is NOT created by the assistant — gateway owns this.
4794
- const binding = await getGuardianBinding("self", "phone");
4675
+ const binding = getGuardianBinding("self", "phone");
4795
4676
  expect(binding).toBeNull();
4796
4677
 
4797
4678
  // Normal greeting should fire (from mockSendMessage), not the handoff copy
@@ -5069,10 +4950,15 @@ describe("relay-server", () => {
5069
4950
  test("guardian label: guardian persona name takes precedence over Contact.displayName", async () => {
5070
4951
  mockUserReference = "Alice";
5071
4952
 
5072
- // Gateway binding carries a different displayName
5073
- mockGuardianDeliveryList = [
5074
- { channelType: "phone", status: "active", displayName: "Bob" },
5075
- ];
4953
+ // Create a guardian binding with a different displayName
4954
+ createGuardianBinding({
4955
+ channel: "phone",
4956
+ guardianExternalUserId: "+15559990001",
4957
+ guardianDeliveryChatId: "+15559990001",
4958
+ guardianPrincipalId: "+15559990001",
4959
+ verifiedVia: "test",
4960
+ metadataJson: JSON.stringify({ displayName: "Bob" }),
4961
+ });
5076
4962
 
5077
4963
  ensureConversation("conv-label-user-md");
5078
4964
  const session = createCallSession({
@@ -5109,10 +4995,15 @@ describe("relay-server", () => {
5109
4995
  test("guardian label: Contact.displayName used when guardian persona name is empty", async () => {
5110
4996
  mockUserReference = "my human";
5111
4997
 
5112
- // Gateway binding carries the guardian displayName
5113
- mockGuardianDeliveryList = [
5114
- { channelType: "phone", status: "active", displayName: "Charlie" },
5115
- ];
4998
+ // Create a guardian binding with a displayName
4999
+ createGuardianBinding({
5000
+ channel: "phone",
5001
+ guardianExternalUserId: "+15559990002",
5002
+ guardianDeliveryChatId: "+15559990002",
5003
+ guardianPrincipalId: "+15559990002",
5004
+ verifiedVia: "test",
5005
+ metadataJson: JSON.stringify({ displayName: "Charlie" }),
5006
+ });
5116
5007
 
5117
5008
  ensureConversation("conv-label-contact");
5118
5009
  const session = createCallSession({
@@ -5148,8 +5039,10 @@ describe("relay-server", () => {
5148
5039
  test("guardian label: DEFAULT_USER_REFERENCE used when both guardian persona name and Contact.displayName are empty", async () => {
5149
5040
  mockUserReference = "my human";
5150
5041
 
5151
- // Empty binding list so resolveGuardianLabel falls back to DEFAULT_USER_REFERENCE
5152
- mockGuardianDeliveryList = [];
5042
+ // Clear guardian binding so resolveGuardianLabel falls back to DEFAULT_USER_REFERENCE
5043
+ const db = getDb();
5044
+ db.run("DELETE FROM contact_channels");
5045
+ db.run("DELETE FROM contacts");
5153
5046
 
5154
5047
  ensureConversation("conv-label-default");
5155
5048
  const session = createCallSession({
@@ -5547,566 +5440,4 @@ describe("relay-server", () => {
5547
5440
  relay.destroy();
5548
5441
  });
5549
5442
  });
5550
-
5551
- // ── Mid-call trust re-resolution from the gateway verdict ───────────
5552
- //
5553
- // After a verification/activation success the relay re-resolves caller trust.
5554
- // It prefers the gateway verdict (authoritative right after the gateway
5555
- // updated the binding) and falls back to local resolution on a missing/
5556
- // failed/unusable verdict so a blip never drops the call.
5557
-
5558
- function readControllerTrustClass(relay: RelayConnection): string | undefined {
5559
- return (
5560
- relay.getController() as unknown as {
5561
- trustContext?: { trustClass?: string };
5562
- }
5563
- )?.trustContext?.trustClass;
5564
- }
5565
-
5566
- test("inbound guardian verification: re-resolves trust from the gateway verdict", async () => {
5567
- ensureConversation("conv-midcall-verdict-guardian");
5568
- const session = createCallSession({
5569
- conversationId: "conv-midcall-verdict-guardian",
5570
- provider: "twilio",
5571
- fromNumber: "+15559999999",
5572
- toNumber: "+15551111111",
5573
- });
5574
-
5575
- const secret = createPendingVoiceGuardianChallenge();
5576
-
5577
- // The gateway verdict upgrades the caller to guardian post-verification.
5578
- mockMidCallVerdict = {
5579
- trustClass: "guardian",
5580
- canonicalSenderId: "+15559999999",
5581
- guardianExternalUserId: "+15559999999",
5582
- guardianPrincipalId: "+15559999999",
5583
- };
5584
-
5585
- mockSendMessage.mockImplementation(
5586
- createMockProviderResponse(["Hello, verified guardian!"]),
5587
- );
5588
-
5589
- const { relay } = createMockWs(session.id);
5590
-
5591
- await relay.handleMessage(
5592
- JSON.stringify({
5593
- type: "setup",
5594
- callSid: "CA_midcall_verdict_guardian",
5595
- from: "+15559999999",
5596
- to: "+15551111111",
5597
- }),
5598
- );
5599
-
5600
- for (const digit of secret) {
5601
- await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
5602
- }
5603
- await new Promise((resolve) => setTimeout(resolve, 10));
5604
-
5605
- expect(relay.getConnectionState()).toBe("connected");
5606
- // Controller trust reflects the gateway verdict's upgraded class.
5607
- expect(readControllerTrustClass(relay)).toBe("guardian");
5608
-
5609
- relay.destroy();
5610
- });
5611
-
5612
- test("inbound guardian verification: resolutionFailed verdict falls back to local resolution without dropping the call", async () => {
5613
- ensureConversation("conv-midcall-verdict-failed");
5614
- const session = createCallSession({
5615
- conversationId: "conv-midcall-verdict-failed",
5616
- provider: "twilio",
5617
- fromNumber: "+15559999999",
5618
- toNumber: "+15551111111",
5619
- });
5620
-
5621
- const secret = createPendingVoiceGuardianChallenge();
5622
-
5623
- // A failed verdict must fall back to local resolution — the call stays up.
5624
- mockMidCallVerdict = {
5625
- trustClass: "unknown",
5626
- canonicalSenderId: null,
5627
- resolutionFailed: true,
5628
- };
5629
-
5630
- mockSendMessage.mockImplementation(
5631
- createMockProviderResponse(["Hello, how can I help you?"]),
5632
- );
5633
-
5634
- const { relay } = createMockWs(session.id);
5635
-
5636
- await relay.handleMessage(
5637
- JSON.stringify({
5638
- type: "setup",
5639
- callSid: "CA_midcall_verdict_failed",
5640
- from: "+15559999999",
5641
- to: "+15551111111",
5642
- }),
5643
- );
5644
-
5645
- for (const digit of secret) {
5646
- await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
5647
- }
5648
- await new Promise((resolve) => setTimeout(resolve, 10));
5649
-
5650
- // Fail-soft: verification still completes and the call connects.
5651
- expect(relay.isVerificationSessionActive()).toBe(false);
5652
- expect(relay.getConnectionState()).toBe("connected");
5653
- expect(readControllerTrustClass(relay)).toBeDefined();
5654
-
5655
- relay.destroy();
5656
- });
5657
-
5658
- test("inbound guardian verification: member-claiming but unusable verdict falls back to local resolution", async () => {
5659
- ensureConversation("conv-midcall-verdict-unusable");
5660
- const session = createCallSession({
5661
- conversationId: "conv-midcall-verdict-unusable",
5662
- provider: "twilio",
5663
- fromNumber: "+15559999999",
5664
- toNumber: "+15551111111",
5665
- });
5666
-
5667
- const secret = createPendingVoiceGuardianChallenge();
5668
-
5669
- // Claims a member (contactId/channelId) but the ACL can't be reassembled
5670
- // (missing status/policy) — mirrors the setup path's unusable condition.
5671
- mockMidCallVerdict = {
5672
- trustClass: "trusted_contact",
5673
- canonicalSenderId: "+15559999999",
5674
- contactId: "ct_unusable",
5675
- channelId: "ch_unusable",
5676
- };
5677
-
5678
- mockSendMessage.mockImplementation(
5679
- createMockProviderResponse(["Hello there."]),
5680
- );
5681
-
5682
- const { relay } = createMockWs(session.id);
5683
-
5684
- await relay.handleMessage(
5685
- JSON.stringify({
5686
- type: "setup",
5687
- callSid: "CA_midcall_verdict_unusable",
5688
- from: "+15559999999",
5689
- to: "+15551111111",
5690
- }),
5691
- );
5692
-
5693
- for (const digit of secret) {
5694
- await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
5695
- }
5696
- await new Promise((resolve) => setTimeout(resolve, 10));
5697
-
5698
- // Fail-soft: unusable verdict does not drop the call; local fallback fires.
5699
- expect(relay.getConnectionState()).toBe("connected");
5700
- expect(readControllerTrustClass(relay)).toBeDefined();
5701
-
5702
- relay.destroy();
5703
- });
5704
-
5705
- test("inbound guardian verification: memberless unknown verdict falls back to local resolution (just-activated invitee not downgraded)", async () => {
5706
- ensureConversation("conv-midcall-verdict-unknown");
5707
- const session = createCallSession({
5708
- conversationId: "conv-midcall-verdict-unknown",
5709
- provider: "twilio",
5710
- fromNumber: "+15559999999",
5711
- toNumber: "+15551111111",
5712
- });
5713
-
5714
- const secret = createPendingVoiceGuardianChallenge();
5715
-
5716
- // Invite redemption writes the channel assistant-side, so right after
5717
- // activation the gateway has no member and returns a memberless unknown
5718
- // verdict. Mid-call re-resolution must treat it as a stale gateway view
5719
- // and fall back to local resolution (which has the fresh channel) rather
5720
- // than downgrade the just-activated invitee to unknown.
5721
- mockMidCallVerdict = {
5722
- trustClass: "unknown",
5723
- canonicalSenderId: "+15559999999",
5724
- };
5725
-
5726
- mockSendMessage.mockImplementation(
5727
- createMockProviderResponse(["Hello there."]),
5728
- );
5729
-
5730
- const { relay } = createMockWs(session.id);
5731
-
5732
- await relay.handleMessage(
5733
- JSON.stringify({
5734
- type: "setup",
5735
- callSid: "CA_midcall_verdict_unknown",
5736
- from: "+15559999999",
5737
- to: "+15551111111",
5738
- }),
5739
- );
5740
-
5741
- for (const digit of secret) {
5742
- await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
5743
- }
5744
- await new Promise((resolve) => setTimeout(resolve, 10));
5745
-
5746
- expect(relay.getConnectionState()).toBe("connected");
5747
- // Local resolver produced the final context; the unknown verdict was not
5748
- // consumed as authoritative.
5749
- expect(trustVerdictMapperUsed).toBe(false);
5750
- expect(readControllerTrustClass(relay)).toBeDefined();
5751
-
5752
- relay.destroy();
5753
- });
5754
-
5755
- function readControllerMemberStatus(
5756
- relay: RelayConnection,
5757
- ): string | undefined {
5758
- return (
5759
- relay.getController() as unknown as {
5760
- trustContext?: { memberStatus?: string };
5761
- }
5762
- )?.trustContext?.memberStatus;
5763
- }
5764
-
5765
- test("inbound guardian verification: memberful blocked unknown verdict is honored (verdict path enforces blocked status)", async () => {
5766
- ensureConversation("conv-midcall-verdict-blocked");
5767
- const session = createCallSession({
5768
- conversationId: "conv-midcall-verdict-blocked",
5769
- provider: "twilio",
5770
- fromNumber: "+15559999999",
5771
- toNumber: "+15551111111",
5772
- });
5773
-
5774
- const secret = createPendingVoiceGuardianChallenge();
5775
-
5776
- mockSendMessage.mockImplementation(
5777
- createMockProviderResponse(["Hello there."]),
5778
- );
5779
-
5780
- const { relay } = createMockWs(session.id);
5781
-
5782
- // Setup resolves locally (no verdict) so the pending guardian challenge
5783
- // drives verification rather than denying at the door.
5784
- await relay.handleMessage(
5785
- JSON.stringify({
5786
- type: "setup",
5787
- callSid: "CA_midcall_verdict_blocked",
5788
- from: "+15559999999",
5789
- to: "+15551111111",
5790
- }),
5791
- );
5792
-
5793
- // The gateway classifies a blocked member as trustClass "unknown" but still
5794
- // carries contactId/channelId and the deny ACL. This memberful unknown must
5795
- // take the verdict path on mid-call re-resolution so its blocked status is
5796
- // enforced — not fall back to local, which could miss a stale block.
5797
- mockMidCallVerdict = {
5798
- trustClass: "unknown",
5799
- canonicalSenderId: "+15559999999",
5800
- contactId: "ct_blocked",
5801
- channelId: "ch_blocked",
5802
- status: "blocked",
5803
- policy: "deny",
5804
- };
5805
-
5806
- for (const digit of secret) {
5807
- await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
5808
- }
5809
- await new Promise((resolve) => setTimeout(resolve, 10));
5810
-
5811
- expect(relay.getConnectionState()).toBe("connected");
5812
- // Verdict path consumed the memberful unknown verdict; blocked status lands.
5813
- expect(trustVerdictMapperUsed).toBe(true);
5814
- expect(readControllerMemberStatus(relay)).toBe("blocked");
5815
-
5816
- relay.destroy();
5817
- });
5818
-
5819
- test("inbound guardian verification: memberful revoked unknown verdict is honored (verdict path enforces revoked status)", async () => {
5820
- ensureConversation("conv-midcall-verdict-revoked");
5821
- const session = createCallSession({
5822
- conversationId: "conv-midcall-verdict-revoked",
5823
- provider: "twilio",
5824
- fromNumber: "+15559999999",
5825
- toNumber: "+15551111111",
5826
- });
5827
-
5828
- const secret = createPendingVoiceGuardianChallenge();
5829
-
5830
- mockSendMessage.mockImplementation(
5831
- createMockProviderResponse(["Hello there."]),
5832
- );
5833
-
5834
- const { relay } = createMockWs(session.id);
5835
-
5836
- await relay.handleMessage(
5837
- JSON.stringify({
5838
- type: "setup",
5839
- callSid: "CA_midcall_verdict_revoked",
5840
- from: "+15559999999",
5841
- to: "+15551111111",
5842
- }),
5843
- );
5844
-
5845
- mockMidCallVerdict = {
5846
- trustClass: "unknown",
5847
- canonicalSenderId: "+15559999999",
5848
- contactId: "ct_revoked",
5849
- channelId: "ch_revoked",
5850
- status: "revoked",
5851
- policy: "deny",
5852
- };
5853
-
5854
- for (const digit of secret) {
5855
- await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
5856
- }
5857
- await new Promise((resolve) => setTimeout(resolve, 10));
5858
-
5859
- expect(relay.getConnectionState()).toBe("connected");
5860
- expect(trustVerdictMapperUsed).toBe(true);
5861
- expect(readControllerMemberStatus(relay)).toBe("revoked");
5862
-
5863
- relay.destroy();
5864
- });
5865
-
5866
- test("a prompt arriving during the mid-call re-resolution await is deferred and runs under the upgraded trust", async () => {
5867
- ensureConversation("conv-midcall-race");
5868
- const session = createCallSession({
5869
- conversationId: "conv-midcall-race",
5870
- provider: "twilio",
5871
- fromNumber: "+15559999999",
5872
- toNumber: "+15551111111",
5873
- });
5874
-
5875
- const secret = createPendingVoiceGuardianChallenge();
5876
-
5877
- // Capture the controller's trust class at the moment a turn actually fires,
5878
- // so we can prove the deferred prompt did not run under the stale context.
5879
- const trustClassAtTurn: Array<string | undefined> = [];
5880
- mockSendMessage.mockImplementation((...args: unknown[]) => {
5881
- trustClassAtTurn.push(readControllerTrustClass(relay));
5882
- return createMockProviderResponse(["Hello, verified guardian!"])(
5883
- ...(args as Parameters<ReturnType<typeof createMockProviderResponse>>),
5884
- );
5885
- });
5886
-
5887
- const { relay } = createMockWs(session.id);
5888
-
5889
- // Setup resolves locally (no verdict) so the pending challenge drives
5890
- // verification; the gated guardian verdict is armed only for the mid-call
5891
- // re-resolution below.
5892
- await relay.handleMessage(
5893
- JSON.stringify({
5894
- type: "setup",
5895
- callSid: "CA_midcall_race",
5896
- from: "+15559999999",
5897
- to: "+15551111111",
5898
- }),
5899
- );
5900
-
5901
- // The gateway verdict upgrades the caller to guardian, but the round-trip is
5902
- // slow — gate it so a prompt can land in the re-resolution await window.
5903
- mockMidCallVerdict = {
5904
- trustClass: "guardian",
5905
- canonicalSenderId: "+15559999999",
5906
- guardianExternalUserId: "+15559999999",
5907
- guardianPrincipalId: "+15559999999",
5908
- };
5909
- let releaseVerdict!: () => void;
5910
- mockMidCallVerdictGate = new Promise<void>((resolve) => {
5911
- releaseVerdict = resolve;
5912
- });
5913
-
5914
- // Enter the gated re-resolution: the final DTMF digit triggers
5915
- // handleVerificationCodeResult, which awaits the slow verdict.
5916
- const digits = secret.split("");
5917
- for (const digit of digits.slice(0, -1)) {
5918
- await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
5919
- }
5920
- const verificationDone = relay.handleMessage(
5921
- JSON.stringify({ type: "dtmf", digit: digits[digits.length - 1] }),
5922
- );
5923
- // Let the verdict await begin (connectionState is now "connected", guard set).
5924
- await new Promise((resolve) => setTimeout(resolve, 0));
5925
- expect(relay.getConnectionState()).toBe("connected");
5926
- // Trust is still stale (verdict gated) — caller is not yet guardian.
5927
- expect(readControllerTrustClass(relay)).not.toBe("guardian");
5928
-
5929
- // Prompt arrives mid-await: it must be deferred, not processed under stale trust.
5930
- await relay.handleMessage(
5931
- JSON.stringify({
5932
- type: "prompt",
5933
- voicePrompt: "Are my appointments confirmed?",
5934
- lang: "en-US",
5935
- last: true,
5936
- }),
5937
- );
5938
- // No turn yet — the prompt was buffered, not run under the stale context.
5939
- expect(trustClassAtTurn).toHaveLength(0);
5940
-
5941
- // Release the verdict; re-resolution installs the upgraded context, then the
5942
- // deferred prompt is flushed and its turn runs under guardian trust.
5943
- releaseVerdict();
5944
- await verificationDone;
5945
- await new Promise((resolve) => setTimeout(resolve, 20));
5946
-
5947
- expect(readControllerTrustClass(relay)).toBe("guardian");
5948
- expect(trustClassAtTurn.length).toBeGreaterThan(0);
5949
- expect(trustClassAtTurn.every((c) => c === "guardian")).toBe(true);
5950
-
5951
- relay.destroy();
5952
- });
5953
-
5954
- test("invite redemption: a prompt buffered during re-resolution runs as a real turn after activation (not dropped)", async () => {
5955
- ensureConversation("conv-midcall-invite-flush");
5956
- const session = createCallSession({
5957
- conversationId: "conv-midcall-invite-flush",
5958
- provider: "twilio",
5959
- fromNumber: "+15558887777",
5960
- toNumber: "+15551111111",
5961
- });
5962
-
5963
- const code = generateVoiceCode(6);
5964
- createInvite({
5965
- sourceChannel: "phone",
5966
- contactId: createTargetContact("Alice"),
5967
- maxUses: 1,
5968
- expectedExternalUserId: "+15558887777",
5969
- voiceCodeHash: hashVoiceCode(code),
5970
- voiceCodeDigits: 6,
5971
- });
5972
-
5973
- const turnCountBefore = mockSendMessage.mock.calls.length;
5974
- mockSendMessage.mockImplementation(
5975
- createMockProviderResponse(["Sure, here you go."]),
5976
- );
5977
-
5978
- const { relay } = createMockWs(session.id);
5979
-
5980
- await relay.handleMessage(
5981
- JSON.stringify({
5982
- type: "setup",
5983
- callSid: "CA_midcall_invite_flush",
5984
- from: "+15558887777",
5985
- to: "+15551111111",
5986
- }),
5987
- );
5988
- expect(relay.getConnectionState()).toBe("verification_pending");
5989
-
5990
- // Gate the mid-call verdict so the prompt lands inside the re-resolution
5991
- // await, after activation flips connectionState to "connected".
5992
- let releaseVerdict!: () => void;
5993
- mockMidCallVerdictGate = new Promise<void>((resolve) => {
5994
- releaseVerdict = resolve;
5995
- });
5996
-
5997
- const digits = code.split("");
5998
- for (const digit of digits.slice(0, -1)) {
5999
- await relay.handleMessage(JSON.stringify({ type: "dtmf", digit }));
6000
- }
6001
- const redemptionDone = relay.handleMessage(
6002
- JSON.stringify({ type: "dtmf", digit: digits[digits.length - 1] }),
6003
- );
6004
- // Let the redemption chain (gateway claim, caller-verdict read, DB write)
6005
- // drain up to activation, which flips the state to "connected" before
6006
- // entering the gated re-resolution.
6007
- await new Promise((resolve) => setTimeout(resolve, 50));
6008
- // Activation already reached the terminal state before re-resolution.
6009
- expect(relay.getConnectionState()).toBe("connected");
6010
-
6011
- await relay.handleMessage(
6012
- JSON.stringify({
6013
- type: "prompt",
6014
- voicePrompt: "What's on my calendar today?",
6015
- lang: "en-US",
6016
- last: true,
6017
- }),
6018
- );
6019
- // Buffered, not yet run (verdict gated).
6020
- expect(mockSendMessage.mock.calls.length).toBe(turnCountBefore);
6021
-
6022
- releaseVerdict();
6023
- await redemptionDone;
6024
- await new Promise((resolve) => setTimeout(resolve, 20));
6025
-
6026
- // Flushed onto the real-turn path: the prompt produced an LLM turn rather
6027
- // than being dropped by the verification-pending branch.
6028
- expect(mockSendMessage.mock.calls.length).toBeGreaterThan(turnCountBefore);
6029
-
6030
- relay.destroy();
6031
- });
6032
-
6033
- test("access-request approval: a prompt buffered during re-resolution runs as a real turn (not misrouted to wait-state)", async () => {
6034
- ensureConversation("conv-midcall-access-flush");
6035
- const session = createCallSession({
6036
- conversationId: "conv-midcall-access-flush",
6037
- provider: "twilio",
6038
- fromNumber: "+15557770003",
6039
- toNumber: "+15551111111",
6040
- });
6041
-
6042
- const turnCountBefore = mockSendMessage.mock.calls.length;
6043
- mockSendMessage.mockImplementation(
6044
- createMockProviderResponse(["Sure, here you go."]),
6045
- );
6046
-
6047
- const { relay } = createMockWs(session.id);
6048
-
6049
- await relay.handleMessage(
6050
- JSON.stringify({
6051
- type: "setup",
6052
- callSid: "CA_midcall_access_flush",
6053
- from: "+15557770003",
6054
- to: "+15551111111",
6055
- }),
6056
- );
6057
-
6058
- await relay.handleMessage(
6059
- JSON.stringify({
6060
- type: "prompt",
6061
- voicePrompt: "Bob Smith",
6062
- lang: "en-US",
6063
- last: true,
6064
- }),
6065
- );
6066
- expect(relay.getConnectionState()).toBe("awaiting_guardian_decision");
6067
-
6068
- // Gate the mid-call verdict so the prompt lands inside the re-resolution
6069
- // await triggered by the approval poll.
6070
- let releaseVerdict!: () => void;
6071
- mockMidCallVerdictGate = new Promise<void>((resolve) => {
6072
- releaseVerdict = resolve;
6073
- });
6074
-
6075
- const pending = listCanonicalGuardianRequests({
6076
- status: "pending",
6077
- requesterExternalUserId: "+15557770003",
6078
- sourceChannel: "phone",
6079
- kind: "access_request",
6080
- });
6081
- expect(pending.length).toBe(1);
6082
- resolveCanonicalGuardianRequest(pending[0].id, "pending", {
6083
- status: "approved",
6084
- answerText: undefined,
6085
- decidedByExternalUserId: undefined,
6086
- });
6087
-
6088
- // Let the poll detect approval and enter the gated re-resolution.
6089
- await new Promise((resolve) => setTimeout(resolve, 200));
6090
- expect(relay.getConnectionState()).toBe("connected");
6091
-
6092
- await relay.handleMessage(
6093
- JSON.stringify({
6094
- type: "prompt",
6095
- voicePrompt: "What's on my calendar today?",
6096
- lang: "en-US",
6097
- last: true,
6098
- }),
6099
- );
6100
- // Buffered, not yet run (verdict gated).
6101
- expect(mockSendMessage.mock.calls.length).toBe(turnCountBefore);
6102
-
6103
- releaseVerdict();
6104
- await new Promise((resolve) => setTimeout(resolve, 20));
6105
-
6106
- // Flushed onto the real-turn path rather than the awaiting-guardian-decision
6107
- // wait-state classifier: the prompt produced an LLM turn.
6108
- expect(mockSendMessage.mock.calls.length).toBeGreaterThan(turnCountBefore);
6109
-
6110
- relay.destroy();
6111
- });
6112
5443
  });