@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5

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 (546) hide show
  1. package/bun.lock +20 -0
  2. package/docs/workspace-tools.md +42 -33
  3. package/eslint-rules/cli-no-daemon-internals.js +6 -0
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
  5. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
  6. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
  7. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
  8. package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
  9. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
  12. package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
  13. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/openapi.yaml +196 -56
  15. package/package.json +4 -1
  16. package/scripts/test.sh +36 -15
  17. package/src/__tests__/actor-token-service.test.ts +36 -14
  18. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
  19. package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  21. package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
  22. package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
  23. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  24. package/src/__tests__/approval-cascade.test.ts +2 -0
  25. package/src/__tests__/assistant-attachments.test.ts +42 -0
  26. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
  27. package/src/__tests__/btw-routes.test.ts +2 -0
  28. package/src/__tests__/build-persisted-content.test.ts +2 -0
  29. package/src/__tests__/call-controller.test.ts +19 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +21 -26
  31. package/src/__tests__/channel-delivery-store.test.ts +28 -0
  32. package/src/__tests__/channel-guardian.test.ts +164 -78
  33. package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
  34. package/src/__tests__/channel-reply-delivery.test.ts +2 -0
  35. package/src/__tests__/code-search-tool.test.ts +585 -0
  36. package/src/__tests__/compaction-events.test.ts +2 -0
  37. package/src/__tests__/compaction-ledger-store.test.ts +128 -0
  38. package/src/__tests__/compaction.benchmark.test.ts +2 -0
  39. package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
  40. package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
  41. package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
  43. package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
  44. package/src/__tests__/config-loader-backfill.test.ts +123 -10
  45. package/src/__tests__/config-schema.test.ts +1 -0
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
  47. package/src/__tests__/consult-deadline.test.ts +60 -0
  48. package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
  49. package/src/__tests__/contact-store-user-file.test.ts +7 -10
  50. package/src/__tests__/contacts-relay-reads.test.ts +19 -24
  51. package/src/__tests__/contacts-write.test.ts +0 -2
  52. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  53. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +134 -0
  57. package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
  58. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
  61. package/src/__tests__/conversation-fork-crud.test.ts +354 -24
  62. package/src/__tests__/conversation-history-web-search.test.ts +2 -0
  63. package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
  64. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
  65. package/src/__tests__/conversation-pairing.test.ts +2 -0
  66. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  67. package/src/__tests__/conversation-process-callsite.test.ts +2 -0
  68. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  69. package/src/__tests__/conversation-queue.test.ts +91 -0
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
  72. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  73. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  74. package/src/__tests__/conversation-speed-override.test.ts +2 -0
  75. package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
  76. package/src/__tests__/conversation-title-service.test.ts +2 -0
  77. package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
  78. package/src/__tests__/conversation-usage.test.ts +2 -0
  79. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
  80. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  81. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  82. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  83. package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
  84. package/src/__tests__/db-migration-rollback.test.ts +205 -171
  85. package/src/__tests__/db-test-helpers.ts +5 -4
  86. package/src/__tests__/delete-propagation.test.ts +5 -3
  87. package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
  88. package/src/__tests__/disk-pressure-guard.test.ts +41 -0
  89. package/src/__tests__/dm-backfill.test.ts +6 -4
  90. package/src/__tests__/dm-persistence.test.ts +2 -0
  91. package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
  92. package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
  93. package/src/__tests__/exploration-drift-hook.test.ts +3 -2
  94. package/src/__tests__/filing-service.test.ts +2 -0
  95. package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
  96. package/src/__tests__/guardian-dispatch.test.ts +140 -1
  97. package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
  98. package/src/__tests__/guardian-outbound-http.test.ts +13 -0
  99. package/src/__tests__/guardian-routing-state.test.ts +6 -10
  100. package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
  101. package/src/__tests__/heartbeat-service.test.ts +2 -0
  102. package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
  103. package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
  104. package/src/__tests__/helpers/mock-logger.ts +1 -0
  105. package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
  106. package/src/__tests__/host-app-control-routes.test.ts +24 -30
  107. package/src/__tests__/host-bash-routes.test.ts +31 -41
  108. package/src/__tests__/host-browser-routes.test.ts +26 -32
  109. package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
  110. package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
  111. package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
  112. package/src/__tests__/http-user-message-parity.test.ts +290 -8
  113. package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
  114. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  115. package/src/__tests__/invite-redemption-service.test.ts +471 -53
  116. package/src/__tests__/invite-routes-http.test.ts +34 -0
  117. package/src/__tests__/invite-service-ipc.test.ts +65 -2
  118. package/src/__tests__/llm-context-normalization.test.ts +105 -0
  119. package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
  120. package/src/__tests__/llm-usage-store.test.ts +25 -0
  121. package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
  122. package/src/__tests__/mcp-health-check.test.ts +2 -1
  123. package/src/__tests__/media-stream-server-integration.test.ts +127 -0
  124. package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
  125. package/src/__tests__/messaging-send-tool.test.ts +2 -0
  126. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  127. package/src/__tests__/mtime-cache.test.ts +275 -5
  128. package/src/__tests__/native-web-search.test.ts +2 -0
  129. package/src/__tests__/non-member-access-request.test.ts +191 -17
  130. package/src/__tests__/notification-broadcaster.test.ts +4 -0
  131. package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
  132. package/src/__tests__/notification-deep-link.test.ts +6 -0
  133. package/src/__tests__/notification-guardian-path.test.ts +19 -0
  134. package/src/__tests__/onboarding-persona-write.test.ts +52 -22
  135. package/src/__tests__/openai-provider.test.ts +22 -12
  136. package/src/__tests__/openai-responses-provider.test.ts +12 -2
  137. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  138. package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
  139. package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
  140. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  141. package/src/__tests__/persona-resolver.test.ts +75 -45
  142. package/src/__tests__/plugin-bootstrap.test.ts +21 -83
  143. package/src/__tests__/plugin-disabled-state.test.ts +190 -0
  144. package/src/__tests__/plugin-pipeline.test.ts +96 -0
  145. package/src/__tests__/plugin-route-contribution.test.ts +6 -19
  146. package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
  147. package/src/__tests__/plugin-types.test.ts +2 -4
  148. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  149. package/src/__tests__/process-message-display-content.test.ts +2 -0
  150. package/src/__tests__/provider-error-scenarios.test.ts +5 -4
  151. package/src/__tests__/provider-usage-tracking.test.ts +39 -0
  152. package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
  153. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
  154. package/src/__tests__/reaction-persistence.test.ts +51 -4
  155. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
  156. package/src/__tests__/registry.test.ts +4 -1
  157. package/src/__tests__/relay-server.test.ts +758 -32
  158. package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
  159. package/src/__tests__/secret-ingress-http.test.ts +14 -0
  160. package/src/__tests__/send-endpoint-busy.test.ts +30 -8
  161. package/src/__tests__/settings-routes.test.ts +32 -0
  162. package/src/__tests__/skills.test.ts +44 -0
  163. package/src/__tests__/slack-inbound-verification.test.ts +47 -2
  164. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
  165. package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
  166. package/src/__tests__/stt-hints.test.ts +49 -15
  167. package/src/__tests__/subagent-detail.test.ts +27 -0
  168. package/src/__tests__/subagent-disposal.test.ts +65 -0
  169. package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
  170. package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
  171. package/src/__tests__/subagent-notify-parent.test.ts +2 -0
  172. package/src/__tests__/subagent-role-registry.test.ts +24 -6
  173. package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
  174. package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
  175. package/src/__tests__/subagent-tools.test.ts +398 -1
  176. package/src/__tests__/suggestion-routes.test.ts +2 -0
  177. package/src/__tests__/thread-backfill.test.ts +3 -3
  178. package/src/__tests__/title-generate-hook.test.ts +2 -0
  179. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
  180. package/src/__tests__/tool-executor.test.ts +16 -11
  181. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  182. package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
  183. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  184. package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
  185. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
  186. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
  187. package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
  188. package/src/__tests__/trusted-contact-verification.test.ts +79 -54
  189. package/src/__tests__/twilio-routes.test.ts +96 -0
  190. package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
  191. package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
  192. package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
  193. package/src/__tests__/voice-invite-redemption.test.ts +216 -20
  194. package/src/__tests__/web-search-backend-failure.test.ts +2 -0
  195. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
  196. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
  197. package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
  198. package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
  199. package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
  200. package/src/__tests__/workspace-tool-loader.test.ts +195 -2
  201. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
  202. package/src/agent/loop-exclusive-tool.test.ts +154 -0
  203. package/src/agent/loop-native-web-search.test.ts +200 -0
  204. package/src/agent/loop.ts +164 -1
  205. package/src/api/constants/sse-replay.ts +41 -0
  206. package/src/api/events/conversation-notice.ts +26 -0
  207. package/src/api/index.ts +19 -1
  208. package/src/api/responses/llm-request-log-entry.ts +29 -0
  209. package/src/api/responses/subagent-detail.ts +17 -0
  210. package/src/api/surfaces.ts +39 -3
  211. package/src/approvals/guardian-channel-delivery.ts +30 -0
  212. package/src/approvals/guardian-expiry-notifier.ts +148 -0
  213. package/src/approvals/guardian-request-resolvers.ts +17 -15
  214. package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
  215. package/src/calls/call-domain.ts +3 -3
  216. package/src/calls/guardian-dispatch.ts +14 -9
  217. package/src/calls/inbound-trust-reader.ts +24 -2
  218. package/src/calls/media-stream-server.ts +21 -0
  219. package/src/calls/relay-access-wait.ts +6 -6
  220. package/src/calls/relay-server.ts +188 -51
  221. package/src/calls/relay-setup-router.ts +47 -17
  222. package/src/calls/relay-verification.ts +4 -4
  223. package/src/calls/stt-hints.ts +9 -12
  224. package/src/calls/twilio-routes.ts +14 -4
  225. package/src/channels/types.ts +10 -20
  226. package/src/cli/commands/__tests__/cache.test.ts +8 -1
  227. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
  228. package/src/cli/commands/cache.ts +194 -181
  229. package/src/cli/commands/contacts.ts +10 -7
  230. package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
  231. package/src/cli/commands/db/status.ts +37 -1
  232. package/src/cli/commands/mcp.ts +252 -218
  233. package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
  234. package/src/cli/commands/memory/index.ts +2 -0
  235. package/src/cli/commands/memory/worker.ts +242 -0
  236. package/src/cli/commands/plugins.ts +125 -13
  237. package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
  238. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
  239. package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
  240. package/src/cli/lib/list-installed-plugins.ts +179 -1
  241. package/src/cli/lib/publish-plugin.ts +633 -0
  242. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
  243. package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
  244. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
  245. package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
  246. package/src/config/bundled-skills/subagent/SKILL.md +17 -2
  247. package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
  248. package/src/config/call-site-defaults.ts +0 -6
  249. package/src/config/feature-flag-registry.json +5 -13
  250. package/src/config/llm-resolver.ts +0 -3
  251. package/src/config/loader.ts +38 -5
  252. package/src/config/prune-seeded-callsite-defaults.ts +110 -0
  253. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  254. package/src/config/schemas/call-site-catalog.ts +0 -7
  255. package/src/config/schemas/heartbeat.ts +2 -5
  256. package/src/config/schemas/llm.ts +3 -12
  257. package/src/config/schemas/memory-lifecycle.ts +12 -0
  258. package/src/config/schemas/memory-v3.ts +7 -0
  259. package/src/config/schemas/memory.ts +4 -0
  260. package/src/config/schemas/timeouts.ts +8 -0
  261. package/src/config/seed-inference-profiles.ts +30 -34
  262. package/src/config/skills.ts +27 -5
  263. package/src/config/sync-gated-profiles.ts +12 -16
  264. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
  265. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
  266. package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
  267. package/src/contacts/contact-store.ts +27 -237
  268. package/src/contacts/contacts-write.ts +18 -55
  269. package/src/contacts/gateway-channel-read.ts +51 -0
  270. package/src/contacts/guardian-delivery-reader.ts +223 -0
  271. package/src/contacts/member-write-relay.ts +183 -0
  272. package/src/contacts/types.ts +3 -15
  273. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
  274. package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
  275. package/src/daemon/assistant-attachments.ts +27 -4
  276. package/src/daemon/conversation-agent-loop.ts +28 -0
  277. package/src/daemon/conversation-notices.ts +60 -0
  278. package/src/daemon/conversation-process.ts +35 -16
  279. package/src/daemon/conversation-surfaces.ts +111 -38
  280. package/src/daemon/conversation-tool-setup.ts +71 -30
  281. package/src/daemon/conversation.ts +23 -1
  282. package/src/daemon/disk-pressure-guard.ts +12 -2
  283. package/src/daemon/event-loop-watchdog.ts +28 -1
  284. package/src/daemon/external-plugins-bootstrap.ts +17 -41
  285. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
  287. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
  288. package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
  289. package/src/daemon/handlers/config-a2a.ts +6 -14
  290. package/src/daemon/handlers/config-channels.ts +67 -26
  291. package/src/daemon/handlers/conversations.ts +77 -0
  292. package/src/daemon/lifecycle.ts +16 -0
  293. package/src/daemon/mcp-reload-service.ts +10 -0
  294. package/src/daemon/memory-v2-startup.test.ts +72 -0
  295. package/src/daemon/memory-v2-startup.ts +87 -19
  296. package/src/daemon/message-types/conversations.ts +2 -0
  297. package/src/daemon/message-types/surfaces.ts +12 -12
  298. package/src/daemon/server.ts +0 -4
  299. package/src/daemon/shutdown-handlers.ts +20 -0
  300. package/src/daemon/tool-setup-types.ts +9 -0
  301. package/src/heartbeat/heartbeat-service.ts +5 -0
  302. package/src/home/relationship-state-writer.ts +5 -0
  303. package/src/hooks/hook-loader.ts +341 -0
  304. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  305. package/src/ipc/assistant-server.ts +2 -2
  306. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
  307. package/src/mcp/client.ts +15 -1
  308. package/src/mcp/mcp-auth-orchestrator.ts +6 -1
  309. package/src/mcp/mcp-header-store.ts +134 -0
  310. package/src/mcp/mcp-oauth-provider.ts +19 -8
  311. package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
  312. package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
  313. package/src/memory/__tests__/prompt-override.test.ts +192 -0
  314. package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
  315. package/src/memory/compaction-ledger-store.ts +107 -0
  316. package/src/memory/conversation-crud.ts +129 -61
  317. package/src/memory/db-connection.ts +22 -3
  318. package/src/memory/db-init.ts +37 -503
  319. package/src/memory/db-singleton.ts +6 -4
  320. package/src/memory/jobs-worker.ts +104 -0
  321. package/src/memory/llm-request-log-store.ts +26 -1
  322. package/src/memory/llm-usage-store.ts +48 -20
  323. package/src/memory/memory-retrospective-job.ts +14 -8
  324. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
  325. package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
  326. package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
  327. package/src/memory/migrations/302-create-compaction-events.ts +107 -0
  328. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
  329. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  330. package/src/memory/migrations/run-migrations.ts +106 -10
  331. package/src/memory/migrations/validate-migration-state.ts +105 -67
  332. package/src/memory/prompt-override.ts +129 -0
  333. package/src/memory/schema/contacts.ts +6 -2
  334. package/src/memory/schema/conversations.ts +37 -0
  335. package/src/memory/schema/infrastructure.ts +20 -0
  336. package/src/memory/steps.ts +1294 -0
  337. package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
  338. package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
  339. package/src/memory/v2/cli-command-store.ts +75 -38
  340. package/src/memory/v2/prompts/consolidation.ts +13 -82
  341. package/src/memory/v2/prompts/router.ts +21 -93
  342. package/src/memory/v2/skill-store.ts +68 -31
  343. package/src/memory/watchdog-events-store.ts +87 -0
  344. package/src/memory/worker-control.ts +204 -0
  345. package/src/memory/worker-process.ts +89 -0
  346. package/src/messaging/channel-binding-metadata.ts +31 -0
  347. package/src/messaging/provider-types.ts +8 -0
  348. package/src/messaging/providers/slack/binding-metadata.ts +60 -0
  349. package/src/notifications/__tests__/broadcaster.test.ts +8 -8
  350. package/src/notifications/__tests__/connected-channels.test.ts +86 -0
  351. package/src/notifications/__tests__/decision-engine.test.ts +78 -9
  352. package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
  353. package/src/notifications/broadcaster.ts +8 -1
  354. package/src/notifications/decision-engine.ts +15 -7
  355. package/src/notifications/destination-resolver.ts +53 -25
  356. package/src/notifications/emit-signal.ts +34 -15
  357. package/src/onboarding/checkin-event.test.ts +222 -0
  358. package/src/onboarding/checkin-event.ts +321 -0
  359. package/src/onboarding/schedule-checkin.ts +190 -0
  360. package/src/permissions/question-prompter.test.ts +1 -1
  361. package/src/permissions/question-prompter.ts +7 -4
  362. package/src/plugin-api/index.ts +12 -12
  363. package/src/plugin-api/types.ts +12 -14
  364. package/src/plugin-api/vision-support.test.ts +28 -4
  365. package/src/plugin-api/vision-support.ts +66 -31
  366. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
  367. package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
  368. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
  369. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
  370. package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
  371. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
  372. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
  373. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
  374. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
  375. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
  376. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
  377. package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
  378. package/src/plugins/defaults/index.ts +0 -35
  379. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
  380. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
  381. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
  382. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
  384. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
  385. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
  386. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
  387. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
  388. package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
  389. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
  390. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
  391. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
  392. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
  393. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
  394. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
  395. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
  396. package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
  397. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
  398. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
  399. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
  400. package/src/plugins/disabled-state.ts +31 -0
  401. package/src/plugins/external-plugin-loader.ts +2 -2
  402. package/src/plugins/mtime-cache.ts +186 -330
  403. package/src/plugins/pipeline.ts +111 -13
  404. package/src/plugins/registry.ts +59 -16
  405. package/src/plugins/surface-import.ts +121 -0
  406. package/src/plugins/types.ts +7 -9
  407. package/src/prompts/persona-resolver.ts +43 -11
  408. package/src/providers/anthropic/client.ts +5 -0
  409. package/src/providers/call-site-routing.ts +45 -0
  410. package/src/providers/model-catalog.ts +16 -0
  411. package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
  412. package/src/providers/openai/api-error-normalization.ts +270 -0
  413. package/src/providers/openai/chat-completions-provider.ts +37 -83
  414. package/src/providers/openai/responses-provider.ts +50 -46
  415. package/src/providers/openrouter/client.ts +5 -0
  416. package/src/providers/provider-send-message.ts +10 -0
  417. package/src/providers/ratelimit.ts +10 -0
  418. package/src/providers/registry.ts +1 -1
  419. package/src/providers/retry.ts +10 -0
  420. package/src/providers/types.ts +22 -0
  421. package/src/providers/usage-tracking.ts +10 -0
  422. package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
  423. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
  424. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
  425. package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
  426. package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
  427. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
  428. package/src/runtime/access-request-helper.ts +18 -39
  429. package/src/runtime/actor-trust-resolver.ts +46 -19
  430. package/src/runtime/anchored-guardian.test.ts +109 -0
  431. package/src/runtime/anchored-guardian.ts +86 -0
  432. package/src/runtime/assistant-event-hub.ts +1 -1
  433. package/src/runtime/assistant-stream-state.ts +9 -2
  434. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
  435. package/src/runtime/auth/require-bound-guardian.ts +21 -11
  436. package/src/runtime/channel-verification-service.ts +56 -31
  437. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  438. package/src/runtime/guardian-vellum-migration.ts +69 -8
  439. package/src/runtime/invite-redemption-service.ts +213 -187
  440. package/src/runtime/local-actor-identity.test.ts +108 -0
  441. package/src/runtime/local-actor-identity.ts +85 -13
  442. package/src/runtime/local-principal-trust.ts +52 -0
  443. package/src/runtime/member-verdict-cache.ts +0 -0
  444. package/src/runtime/pending-interactions.ts +11 -1
  445. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
  446. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  447. package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
  448. package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
  449. package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
  450. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
  451. package/src/runtime/routes/browser-routes.ts +1 -1
  452. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
  453. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  454. package/src/runtime/routes/contact-routes.ts +48 -57
  455. package/src/runtime/routes/conversation-cli-routes.ts +4 -5
  456. package/src/runtime/routes/conversation-list-routes.ts +4 -7
  457. package/src/runtime/routes/conversation-query-routes.ts +72 -0
  458. package/src/runtime/routes/conversation-routes.ts +84 -85
  459. package/src/runtime/routes/events-routes.ts +2 -2
  460. package/src/runtime/routes/global-search-routes.ts +3 -1
  461. package/src/runtime/routes/guardian-action-routes.ts +4 -5
  462. package/src/runtime/routes/host-app-control-routes.ts +5 -4
  463. package/src/runtime/routes/host-bash-routes.ts +5 -4
  464. package/src/runtime/routes/host-browser-routes.ts +9 -11
  465. package/src/runtime/routes/host-cu-routes.ts +5 -4
  466. package/src/runtime/routes/host-file-routes.ts +5 -4
  467. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  468. package/src/runtime/routes/http-adapter.ts +16 -1
  469. package/src/runtime/routes/identity-routes.ts +3 -2
  470. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  471. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
  472. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
  473. package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
  474. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  475. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
  476. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
  477. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
  478. package/src/runtime/routes/index.ts +2 -0
  479. package/src/runtime/routes/llm-context-normalization.ts +83 -0
  480. package/src/runtime/routes/mcp-auth-routes.ts +171 -19
  481. package/src/runtime/routes/migration-rollback-routes.ts +4 -3
  482. package/src/runtime/routes/migration-routes.ts +4 -1
  483. package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
  484. package/src/runtime/routes/settings-routes.ts +8 -3
  485. package/src/runtime/routes/subagents-routes.ts +5 -0
  486. package/src/runtime/routes/surface-action-routes.ts +42 -56
  487. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  488. package/src/runtime/services/conversation-serializer.ts +13 -58
  489. package/src/runtime/tool-grant-request-helper.ts +3 -3
  490. package/src/runtime/trust-verdict-consumer.ts +111 -40
  491. package/src/runtime/verification-outbound-actions.ts +18 -18
  492. package/src/signals/user-message.ts +16 -0
  493. package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
  494. package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
  495. package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
  496. package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
  497. package/src/subagent/index.ts +1 -1
  498. package/src/subagent/manager.ts +254 -33
  499. package/src/subagent/types.ts +11 -4
  500. package/src/telemetry/types.ts +34 -1
  501. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  502. package/src/telemetry/usage-telemetry-reporter.ts +87 -3
  503. package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
  504. package/src/tools/ask-question/ask-question-tool.ts +13 -0
  505. package/src/tools/executor.ts +4 -4
  506. package/src/tools/filesystem/search.ts +543 -0
  507. package/src/tools/registry.ts +38 -0
  508. package/src/tools/shared/filesystem/path-policy.ts +12 -5
  509. package/src/tools/subagent/consult-deadline.ts +49 -0
  510. package/src/tools/subagent/spawn.ts +234 -5
  511. package/src/tools/tool-approval-handler.ts +1 -1
  512. package/src/tools/tool-defaults.ts +9 -2
  513. package/src/tools/tool-manifest.ts +3 -0
  514. package/src/tools/types.ts +131 -25
  515. package/src/tools/workspace-tools/loader.ts +348 -244
  516. package/src/util/errors.ts +26 -1
  517. package/src/util/logger.ts +9 -0
  518. package/src/util/platform.ts +19 -0
  519. package/src/util/telemetry-db-path.ts +24 -0
  520. package/src/workflows/library.test.ts +140 -0
  521. package/src/workflows/library.ts +82 -28
  522. package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
  523. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
  524. package/src/workspace/migrations/031-drop-user-md.ts +232 -148
  525. package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
  526. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
  527. package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
  528. package/src/workspace/migrations/registry.ts +4 -0
  529. package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
  530. package/src/daemon/workspace-tools-watcher.ts +0 -328
  531. package/src/memory/migrations/registry.ts +0 -573
  532. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
  533. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
  534. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
  535. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
  536. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
  537. package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
  538. package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
  539. package/src/plugins/defaults/advisor/config.ts +0 -21
  540. package/src/plugins/defaults/advisor/consult.ts +0 -93
  541. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
  542. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
  543. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
  544. package/src/plugins/defaults/advisor/package.json +0 -14
  545. package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
  546. package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Unit tests for the global-search contacts ordering source.
3
+ *
4
+ * Recency ordering for contacts surfaces `contacts.updatedAt`, never the
5
+ * channel-derived `lastInteraction` column. Daemon-native contact search
6
+ * carries no gateway-relayed ContactRead, so `updatedAt` is the deterministic
7
+ * ordering key.
8
+ */
9
+
10
+ import { afterEach, describe, expect, mock, test } from "bun:test";
11
+
12
+ import type { ContactWithChannels } from "../../../contacts/types.js";
13
+
14
+ let searchContactsResult: ContactWithChannels[] = [];
15
+
16
+ const searchContactsMock = mock(() => searchContactsResult);
17
+
18
+ const actualContactStore = await import("../../../contacts/contact-store.js");
19
+
20
+ mock.module("../../../contacts/contact-store.js", () => ({
21
+ ...actualContactStore,
22
+ searchContacts: searchContactsMock,
23
+ }));
24
+
25
+ const { ROUTES } = await import("../global-search-routes.js");
26
+
27
+ const handler = ROUTES.find((r) => r.operationId === "search_global")!.handler;
28
+
29
+ function makeContact(
30
+ overrides: Partial<ContactWithChannels>,
31
+ ): ContactWithChannels {
32
+ return {
33
+ id: "ct_1",
34
+ displayName: "Alice",
35
+ notes: null,
36
+ role: "contact",
37
+ lastInteraction: null,
38
+ interactionCount: 0,
39
+ createdAt: 1,
40
+ updatedAt: 1,
41
+ contactType: "human",
42
+ userFile: null,
43
+ channels: [],
44
+ ...overrides,
45
+ };
46
+ }
47
+
48
+ afterEach(() => {
49
+ searchContactsResult = [];
50
+ searchContactsMock.mockClear();
51
+ });
52
+
53
+ describe("global-search contacts recency source", () => {
54
+ test("surfaces contacts.updatedAt as lastInteraction, not the channel column", async () => {
55
+ searchContactsResult = [
56
+ makeContact({
57
+ id: "ct_1",
58
+ displayName: "Alice",
59
+ updatedAt: 5000,
60
+ lastInteraction: 9999,
61
+ }),
62
+ ];
63
+
64
+ const result = (await handler({
65
+ queryParams: { q: "ali", categories: "contacts" },
66
+ })) as { results: { contacts: { id: string; lastInteraction: number }[] } };
67
+
68
+ expect(result.results.contacts).toHaveLength(1);
69
+ expect(result.results.contacts[0].lastInteraction).toBe(5000);
70
+ });
71
+
72
+ test("preserves searchContacts ordering deterministically", async () => {
73
+ searchContactsResult = [
74
+ makeContact({ id: "ct_a", displayName: "A", updatedAt: 300 }),
75
+ makeContact({ id: "ct_b", displayName: "B", updatedAt: 200 }),
76
+ makeContact({ id: "ct_c", displayName: "C", updatedAt: 100 }),
77
+ ];
78
+
79
+ const result = (await handler({
80
+ queryParams: { q: "x", categories: "contacts" },
81
+ })) as { results: { contacts: { id: string; lastInteraction: number }[] } };
82
+
83
+ expect(result.results.contacts.map((c) => c.id)).toEqual([
84
+ "ct_a",
85
+ "ct_b",
86
+ "ct_c",
87
+ ]);
88
+ expect(result.results.contacts.map((c) => c.lastInteraction)).toEqual([
89
+ 300, 200, 100,
90
+ ]);
91
+ });
92
+ });
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Regression tests for actor/principal header sanitization in the HTTP
3
+ * adapter.
4
+ *
5
+ * The HTTP adapter must derive the actor identity headers
6
+ * (`x-vellum-actor-principal-id`, `x-vellum-principal-type`) exclusively from
7
+ * the verified AuthContext, never from caller-supplied request headers. A
8
+ * caller whose token carries no actorPrincipalId (svc_gateway / svc_daemon /
9
+ * local principals) must not be able to spoof another principal — e.g. the
10
+ * guardian — by setting the header explicitly. This protects principal-gated
11
+ * handlers (surface action `apr:*` guardian decisions, guardian actions, host
12
+ * proxies) from approval-token bypass via a forged actor principal.
13
+ */
14
+
15
+ import { describe, expect, test } from "bun:test";
16
+
17
+ import { resolveScopeProfile } from "../../auth/scopes.js";
18
+ import type { AuthContext } from "../../auth/types.js";
19
+ import { routeDefinitionsToHTTPRoutes } from "../http-adapter.js";
20
+ import type { RouteDefinition } from "../types.js";
21
+
22
+ // Echo route: returns the identity headers the handler actually observed.
23
+ const ECHO_ROUTE: RouteDefinition = {
24
+ operationId: "echo_identity_headers",
25
+ endpoint: "test/echo-identity",
26
+ method: "POST",
27
+ policy: null,
28
+ handler: ({ headers }) => ({
29
+ actorPrincipalId: headers?.["x-vellum-actor-principal-id"] ?? null,
30
+ principalType: headers?.["x-vellum-principal-type"] ?? null,
31
+ }),
32
+ };
33
+
34
+ function buildAuthContext(overrides: Partial<AuthContext>): AuthContext {
35
+ return {
36
+ subject: "svc:gateway:self",
37
+ principalType: "svc_gateway",
38
+ assistantId: "self",
39
+ scopeProfile: "gateway_service_v1",
40
+ scopes: resolveScopeProfile("gateway_service_v1"),
41
+ policyEpoch: 0,
42
+ ...overrides,
43
+ };
44
+ }
45
+
46
+ async function invokeEcho(params: {
47
+ spoofedPrincipalId?: string;
48
+ spoofedPrincipalType?: string;
49
+ authContext: AuthContext;
50
+ }): Promise<{ actorPrincipalId: string | null; principalType: string | null }> {
51
+ const [httpRoute] = routeDefinitionsToHTTPRoutes([ECHO_ROUTE]);
52
+
53
+ const reqHeaders: Record<string, string> = {
54
+ "content-type": "application/json",
55
+ };
56
+ if (params.spoofedPrincipalId !== undefined) {
57
+ reqHeaders["x-vellum-actor-principal-id"] = params.spoofedPrincipalId;
58
+ }
59
+ if (params.spoofedPrincipalType !== undefined) {
60
+ reqHeaders["x-vellum-principal-type"] = params.spoofedPrincipalType;
61
+ }
62
+
63
+ const req = new Request("http://daemon.local/v1/test/echo-identity", {
64
+ method: "POST",
65
+ headers: reqHeaders,
66
+ body: JSON.stringify({}),
67
+ });
68
+
69
+ const response = await httpRoute.handler({
70
+ req,
71
+ url: new URL(req.url),
72
+ // The echo handler doesn't touch `server`; a stub is fine.
73
+ server: undefined as never,
74
+ authContext: params.authContext,
75
+ params: {},
76
+ });
77
+
78
+ return (await response.json()) as {
79
+ actorPrincipalId: string | null;
80
+ principalType: string | null;
81
+ };
82
+ }
83
+
84
+ describe("http-adapter actor/principal header sanitization", () => {
85
+ test("drops a spoofed actor-principal header when the context has no actorPrincipalId", async () => {
86
+ // A service token (svc_gateway) has no actorPrincipalId. The forged header
87
+ // must be stripped, not passed through to the handler.
88
+ const result = await invokeEcho({
89
+ spoofedPrincipalId: "guardian-principal-victim",
90
+ authContext: buildAuthContext({ actorPrincipalId: undefined }),
91
+ });
92
+
93
+ expect(result.actorPrincipalId).toBeNull();
94
+ // principalType is always re-derived from the verified context.
95
+ expect(result.principalType).toBe("svc_gateway");
96
+ });
97
+
98
+ test("overrides a spoofed actor-principal header with the verified context value", async () => {
99
+ const result = await invokeEcho({
100
+ spoofedPrincipalId: "guardian-principal-victim",
101
+ spoofedPrincipalType: "actor",
102
+ authContext: buildAuthContext({
103
+ subject: "actor:self:real-actor",
104
+ principalType: "actor",
105
+ scopeProfile: "actor_client_v1",
106
+ scopes: resolveScopeProfile("actor_client_v1"),
107
+ actorPrincipalId: "real-actor",
108
+ }),
109
+ });
110
+
111
+ expect(result.actorPrincipalId).toBe("real-actor");
112
+ expect(result.principalType).toBe("actor");
113
+ });
114
+
115
+ test("passes through the genuine actor identity when no spoof is present", async () => {
116
+ const result = await invokeEcho({
117
+ authContext: buildAuthContext({
118
+ subject: "actor:self:real-actor",
119
+ principalType: "actor",
120
+ scopeProfile: "actor_client_v1",
121
+ scopes: resolveScopeProfile("actor_client_v1"),
122
+ actorPrincipalId: "real-actor",
123
+ }),
124
+ });
125
+
126
+ expect(result.actorPrincipalId).toBe("real-actor");
127
+ expect(result.principalType).toBe("actor");
128
+ });
129
+ });
@@ -25,6 +25,7 @@ interface StubConversation {
25
25
  handleSurfaceActionThrows?: Error;
26
26
  handleSurfaceUndoCalled?: boolean;
27
27
  handleSurfaceUndoThrows?: Error;
28
+ trustContext?: { trustClass: string; sourceChannel: string };
28
29
  surfaceActionCalls: Array<{
29
30
  surfaceId: string;
30
31
  actionId: string;
@@ -43,6 +44,48 @@ const findBySurfaceCalls: string[] = [];
43
44
  const getOrCreateCalls: string[] = [];
44
45
  const rawGetCalls: Array<{ sql: string; params: unknown[] }> = [];
45
46
 
47
+ // Gateway guardian-delivery list (shared by the route's dev-bypass lookup and
48
+ // the local-principal-trust mapper): null = unreachable, [] = no guardian.
49
+ let mockGuardianList: Array<Record<string, unknown>> | null = [];
50
+ let httpAuthDisabled = false;
51
+
52
+ // Stub for the shared reset-drift helper. The route under test only consumes
53
+ // its result (a guardian TrustContext or null); the gate itself is covered in
54
+ // runtime/__tests__/guardian-vellum-migration.test.ts. Tests set
55
+ // `mockReResolve` per case and read `reResolveCalls` to assert routing.
56
+ const reResolveCalls: string[] = [];
57
+ let mockReResolve: { trustClass: string; sourceChannel: string } | null = null;
58
+
59
+ mock.module("../../../contacts/guardian-delivery-reader.js", () => ({
60
+ getGuardianDelivery: (_input?: { channelTypes?: string[] }) =>
61
+ Promise.resolve(mockGuardianList),
62
+ peekCachedGuardianDelivery: () => mockGuardianList ?? undefined,
63
+ guardianForChannel: (
64
+ list: Array<Record<string, unknown>>,
65
+ channelType: string,
66
+ ) => list.find((g) => g.channelType === channelType && g.status === "active"),
67
+ }));
68
+
69
+ // Member ACL rides on memberRecord via the member-verdict cache; no local
70
+ // contact here.
71
+ mock.module("../../../contacts/contact-store.js", () => ({
72
+ findContactByAddress: () => null,
73
+ }));
74
+
75
+ mock.module("../../../config/env.js", () => ({
76
+ isHttpAuthDisabled: () => httpAuthDisabled,
77
+ }));
78
+
79
+ mock.module("../../guardian-vellum-migration.js", () => ({
80
+ reResolveTrustOnResetDrift: async (
81
+ incomingPrincipalId: string,
82
+ _sourceChannel: string,
83
+ ) => {
84
+ reResolveCalls.push(incomingPrincipalId);
85
+ return mockReResolve;
86
+ },
87
+ }));
88
+
46
89
  mock.module("../../../daemon/conversation-registry.js", () => ({
47
90
  findConversation: (id: string) => {
48
91
  findConvCalls.push(id);
@@ -122,6 +165,9 @@ function makeStub(id: string): StubConversation {
122
165
  stub.handleSurfaceUndoCalled = true;
123
166
  if (stub.handleSurfaceUndoThrows) throw stub.handleSurfaceUndoThrows;
124
167
  },
168
+ setTrustContext: (ctx: { trustClass: string; sourceChannel: string }) => {
169
+ stub.trustContext = ctx;
170
+ },
125
171
  });
126
172
  return stub;
127
173
  }
@@ -139,6 +185,10 @@ beforeEach(() => {
139
185
  findBySurfaceCalls.length = 0;
140
186
  getOrCreateCalls.length = 0;
141
187
  rawGetCalls.length = 0;
188
+ mockGuardianList = [];
189
+ httpAuthDisabled = false;
190
+ reResolveCalls.length = 0;
191
+ mockReResolve = null;
142
192
  });
143
193
 
144
194
  // ---------------------------------------------------------------------------
@@ -337,6 +387,30 @@ describe("triggerSurfaceAction handler", () => {
337
387
  ]);
338
388
  });
339
389
 
390
+ test("resolves dev-bypass to the guardian principal before threading the turn", async () => {
391
+ httpAuthDisabled = true;
392
+ mockGuardianList = [guardianDelivery(GUARDIAN_PRINCIPAL)];
393
+ const live = makeStub("conv-dev-thread");
394
+ memoryBySurface = live;
395
+
396
+ const handler = findHandler("triggerSurfaceAction");
397
+ await handler({
398
+ body: { surfaceId: "surf-dt", actionId: "act-dt" },
399
+ headers: { "x-vellum-actor-principal-id": "dev-bypass" },
400
+ });
401
+
402
+ // dev-bypass is translated so the surface turn matches the SSE host-proxy
403
+ // client's registered guardian principal (CU/app-control same-actor check).
404
+ expect(live.surfaceActionCalls).toEqual([
405
+ {
406
+ surfaceId: "surf-dt",
407
+ actionId: "act-dt",
408
+ data: undefined,
409
+ sourceActorPrincipalId: GUARDIAN_PRINCIPAL,
410
+ },
411
+ ]);
412
+ });
413
+
340
414
  test("propagates accepted=false rejection as BadRequestError", async () => {
341
415
  const live = makeStub("conv-reject");
342
416
  live.handleSurfaceActionResult = {
@@ -362,6 +436,120 @@ describe("triggerSurfaceAction handler", () => {
362
436
  });
363
437
  });
364
438
 
439
+ // ---------------------------------------------------------------------------
440
+ // Trust context resolution
441
+ // ---------------------------------------------------------------------------
442
+
443
+ const GUARDIAN_PRINCIPAL = "principal-guardian";
444
+ // Daemon-minted vellum-principal-* ids: the DB-reset drift signature. The
445
+ // gateway rebinds to a fresh id while the client still holds a JWT for the old.
446
+ const VELLUM_PRINCIPAL_OLD = "vellum-principal-old";
447
+ const VELLUM_PRINCIPAL_NEW = "vellum-principal-new";
448
+
449
+ function guardianDelivery(principalId: string): Record<string, unknown> {
450
+ return {
451
+ channelType: "vellum",
452
+ contactId: "contact-1",
453
+ principalId,
454
+ address: "guardian-address",
455
+ externalChatId: "guardian-chat",
456
+ status: "active",
457
+ };
458
+ }
459
+
460
+ // A guardian TrustContext the stubbed helper hands back on a recovered drift.
461
+ function guardianCtx(): { trustClass: string; sourceChannel: string } {
462
+ return { trustClass: "guardian", sourceChannel: "vellum" };
463
+ }
464
+
465
+ describe("triggerSurfaceAction trust context", () => {
466
+ test("guardian principal → guardian from the gateway binding, helper not called", async () => {
467
+ mockGuardianList = [guardianDelivery(GUARDIAN_PRINCIPAL)];
468
+ const live = makeStub("conv-guardian");
469
+ memoryBySurface = live;
470
+
471
+ const handler = findHandler("triggerSurfaceAction");
472
+ await handler({
473
+ body: { surfaceId: "surf-g", actionId: "act-g" },
474
+ headers: { "x-vellum-actor-principal-id": GUARDIAN_PRINCIPAL },
475
+ });
476
+
477
+ expect(live.trustContext?.trustClass).toBe("guardian");
478
+ expect(live.trustContext?.sourceChannel).toBe("vellum");
479
+ // First-pass resolve already granted guardian, so the drift helper is skipped.
480
+ expect(reResolveCalls).toEqual([]);
481
+ });
482
+
483
+ test("unknown principal: helper consulted, null result stays unknown (fail closed)", async () => {
484
+ mockGuardianList = [guardianDelivery(GUARDIAN_PRINCIPAL)];
485
+ mockReResolve = null;
486
+ const live = makeStub("conv-unknown");
487
+ memoryBySurface = live;
488
+
489
+ const handler = findHandler("triggerSurfaceAction");
490
+ await handler({
491
+ body: { surfaceId: "surf-u", actionId: "act-u" },
492
+ headers: { "x-vellum-actor-principal-id": VELLUM_PRINCIPAL_OLD },
493
+ });
494
+
495
+ expect(reResolveCalls).toEqual([VELLUM_PRINCIPAL_OLD]);
496
+ expect(live.trustContext?.trustClass).toBe("unknown");
497
+ });
498
+
499
+ test("reset drift: helper returns guardian → route adopts it", async () => {
500
+ mockGuardianList = [guardianDelivery(VELLUM_PRINCIPAL_NEW)];
501
+ mockReResolve = guardianCtx();
502
+ const live = makeStub("conv-drift");
503
+ memoryBySurface = live;
504
+
505
+ const handler = findHandler("triggerSurfaceAction");
506
+ await handler({
507
+ body: { surfaceId: "surf-d", actionId: "act-d" },
508
+ headers: { "x-vellum-actor-principal-id": VELLUM_PRINCIPAL_OLD },
509
+ });
510
+
511
+ expect(reResolveCalls).toEqual([VELLUM_PRINCIPAL_OLD]);
512
+ expect(live.trustContext?.trustClass).toBe("guardian");
513
+ });
514
+
515
+ test("dev-bypass resolves the real guardian principal from the gateway, helper not called", async () => {
516
+ httpAuthDisabled = true;
517
+ mockGuardianList = [guardianDelivery(GUARDIAN_PRINCIPAL)];
518
+ const live = makeStub("conv-dev");
519
+ memoryBySurface = live;
520
+
521
+ const handler = findHandler("triggerSurfaceAction");
522
+ await handler({
523
+ body: { surfaceId: "surf-dev", actionId: "act-dev" },
524
+ headers: { "x-vellum-actor-principal-id": "dev-bypass" },
525
+ });
526
+
527
+ // The synthetic dev-bypass principal is translated to the real guardian,
528
+ // yielding a guardian trust context without consulting the drift helper.
529
+ expect(live.trustContext?.trustClass).toBe("guardian");
530
+ expect(reResolveCalls).toEqual([]);
531
+ });
532
+
533
+ test("dev-bypass with an empty gateway: helper null result → unknown (fail closed)", async () => {
534
+ httpAuthDisabled = true;
535
+ // The gateway has no active binding, so dev-bypass cannot translate to a
536
+ // real guardian; the first-pass resolve is unknown and the helper, returning
537
+ // null, leaves trust unknown.
538
+ mockGuardianList = [];
539
+ mockReResolve = null;
540
+ const live = makeStub("conv-dev-fallback");
541
+ memoryBySurface = live;
542
+
543
+ const handler = findHandler("triggerSurfaceAction");
544
+ await handler({
545
+ body: { surfaceId: "surf-devf", actionId: "act-devf" },
546
+ headers: { "x-vellum-actor-principal-id": "dev-bypass" },
547
+ });
548
+
549
+ expect(live.trustContext?.trustClass).toBe("unknown");
550
+ });
551
+ });
552
+
365
553
  // ---------------------------------------------------------------------------
366
554
  // undoSurfaceAction
367
555
  // ---------------------------------------------------------------------------
@@ -92,7 +92,7 @@ async function handleBrowserExecute({
92
92
  // register with (dev-bypass otherwise mismatches).
93
93
  const headerActor =
94
94
  headers["x-vellum-actor-principal-id"]?.trim() || undefined;
95
- const sourceActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
95
+ const sourceActorPrincipalId = await resolveActorPrincipalIdForLocalGuardian(
96
96
  conversation?.getTurnActorPrincipalId() ?? headerActor,
97
97
  );
98
98
 
@@ -8,13 +8,16 @@
8
8
  * requester.
9
9
  *
10
10
  * This sweep operates on the unified canonical domain
11
- * (`canonical_guardian_requests`) and does not auto-deny pending interactions
12
- * or deliver channel notices the canonical request status transition is the
13
- * single source of truth, and consumers (resolvers, clients polling prompts)
14
- * observe the expired status directly.
11
+ * (`canonical_guardian_requests`). On expiry it transitions the request status
12
+ * (the single source of truth), withdraws the approval cards on every surface,
13
+ * notifies the requester that their request expired, and releases any in-memory
14
+ * pending interaction. Requester notices are delivered straight to the
15
+ * requester's channel — not the guardian-facing notification pipeline — and the
16
+ * guardian stays passive, since the withdrawn card already reflects expiry.
15
17
  */
16
18
 
17
19
  import { withdrawGuardianRequestCards } from "../../approvals/guardian-card-withdrawal.js";
20
+ import { notifyExpiredGuardianRequest } from "../../approvals/guardian-expiry-notifier.js";
18
21
  import {
19
22
  listCanonicalGuardianRequests,
20
23
  resolveCanonicalGuardianRequest,
@@ -39,7 +42,7 @@ let sweepInProgress = false;
39
42
  * concurrent decision that wins the race is never overwritten by the
40
43
  * sweep. Returns the count of requests transitioned to expired.
41
44
  */
42
- async function sweepExpiredCanonicalGuardianRequests(): Promise<number> {
45
+ export async function sweepExpiredCanonicalGuardianRequests(): Promise<number> {
43
46
  const pending = listCanonicalGuardianRequests({ status: "pending" });
44
47
  const now = Date.now();
45
48
  let expiredCount = 0;
@@ -76,6 +79,11 @@ async function sweepExpiredCanonicalGuardianRequests(): Promise<number> {
76
79
  request: resolved,
77
80
  status: "expired",
78
81
  });
82
+
83
+ // Notify the requester their request expired and release any in-memory
84
+ // pending interaction. Best-effort and non-throwing, like the card
85
+ // withdrawal above.
86
+ await notifyExpiredGuardianRequest(resolved);
79
87
  }
80
88
  }
81
89
 
@@ -179,7 +179,7 @@ export async function handleCreateVerificationSession({
179
179
  }
180
180
 
181
181
  // Inbound challenge path
182
- const result = createInboundChallenge(channel, rebind, conversationId);
182
+ const result = await createInboundChallenge(channel, rebind, conversationId);
183
183
  if (!result.success) {
184
184
  throw new BadRequestError(
185
185
  (result as { message?: string }).message ??
@@ -192,12 +192,12 @@ export async function handleCreateVerificationSession({
192
192
  /**
193
193
  * GET /v1/channel-verification-sessions/status
194
194
  */
195
- function handleGetVerificationStatus({
195
+ async function handleGetVerificationStatus({
196
196
  queryParams = {},
197
197
  body = {},
198
198
  }: RouteHandlerArgs) {
199
199
  const channel = (queryParams.channel ?? (body as Record<string, unknown>).channel) as ChannelId | undefined;
200
- return getVerificationStatus(channel);
200
+ return await getVerificationStatus(channel);
201
201
  }
202
202
 
203
203
  /**