@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
@@ -10,6 +10,7 @@
10
10
 
11
11
  import { z } from "zod";
12
12
 
13
+ import { findConversation } from "../../daemon/conversation-registry.js";
13
14
  import {
14
15
  type Confidence,
15
16
  getAttentionStateByConversationIds,
@@ -17,7 +18,6 @@ import {
17
18
  recordConversationSeenSignal,
18
19
  type SignalType,
19
20
  } from "../../memory/conversation-attention-store.js";
20
- import { isConversationProcessing } from "../../memory/conversation-crud.js";
21
21
  import {
22
22
  type ConversationRow,
23
23
  getDisplayMetaForConversations,
@@ -270,9 +270,12 @@ function handleListConversations({ queryParams = {} }: RouteHandlerArgs) {
270
270
  attentionState: attentionStates.get(conversation.id),
271
271
  displayMeta: displayMeta.get(conversation.id),
272
272
  parentCache,
273
- // Checks in-memory flag first (hot path), falls back to the
274
- // persisted `processing_started_at` column for cold conversations.
275
- isProcessing: isConversationProcessing(conversation.id),
273
+ // Cold (evicted / never-loaded) rows aren't in the in-memory
274
+ // store, so `findConversation` returns `undefined` and they
275
+ // report `isProcessing: false` — by definition they aren't
276
+ // mid-turn since the agent loop only runs on resident convs.
277
+ isProcessing:
278
+ findConversation(conversation.id)?.isProcessing() ?? false,
276
279
  }),
277
280
  ),
278
281
  nextOffset,
@@ -486,74 +486,6 @@ function readPlainObject(value: unknown): Record<string, unknown> | undefined {
486
486
  return value as Record<string, unknown>;
487
487
  }
488
488
 
489
- function stripTransportHeadersRecursively(value: unknown): void {
490
- if (Array.isArray(value)) {
491
- for (const item of value) {
492
- stripTransportHeadersRecursively(item);
493
- }
494
- return;
495
- }
496
-
497
- const object = readPlainObject(value);
498
- if (!object) return;
499
- const transport = readPlainObject(object.transport);
500
- if (transport) delete transport.headers;
501
- for (const child of Object.values(object)) {
502
- stripTransportHeadersRecursively(child);
503
- }
504
- }
505
-
506
- function containsTransportHeadersRecursively(value: unknown): boolean {
507
- if (Array.isArray(value)) {
508
- return value.some((item) => containsTransportHeadersRecursively(item));
509
- }
510
-
511
- const object = readPlainObject(value);
512
- if (!object) return false;
513
- const transport = readPlainObject(object.transport);
514
- if (transport && Object.hasOwn(transport, "headers")) return true;
515
- return Object.values(object).some((child) =>
516
- containsTransportHeadersRecursively(child),
517
- );
518
- }
519
-
520
- function sanitizeMcpTransportHeadersForSettingsRead(config: unknown): void {
521
- const root = readPlainObject(config);
522
- if (!root) return;
523
- const mcp = readPlainObject(root.mcp);
524
- if (!mcp || !Object.hasOwn(mcp, "servers")) return;
525
- if (Array.isArray(mcp.servers)) {
526
- stripTransportHeadersRecursively(mcp.servers);
527
- return;
528
- }
529
- const servers = readPlainObject(mcp.servers);
530
- if (!servers) return;
531
- for (const server of Object.values(servers)) {
532
- stripTransportHeadersRecursively(server);
533
- }
534
- }
535
-
536
- function patchContainsMcpTransportHeaders(patch: unknown): boolean {
537
- const root = readPlainObject(patch);
538
- const mcp = readPlainObject(root?.mcp);
539
- if (!mcp || !Object.hasOwn(mcp, "servers")) return false;
540
- if (Array.isArray(mcp.servers)) {
541
- return containsTransportHeadersRecursively(mcp.servers);
542
- }
543
- const servers = readPlainObject(mcp.servers);
544
- if (!servers) return false;
545
- return Object.values(servers).some((server) =>
546
- containsTransportHeadersRecursively(server),
547
- );
548
- }
549
-
550
- function rejectMcpTransportHeaderWrite(patch: unknown): void {
551
- if (!patchContainsMcpTransportHeaders(patch)) return;
552
- throw new BadRequestError(
553
- "MCP authentication headers must be managed through MCP server add/update APIs, not generic config writes.",
554
- );
555
- }
556
-
557
489
  const WireProfileEntry = ProfileEntry.extend({
558
490
  supportsVision: z.boolean().optional(),
559
491
  })
@@ -756,7 +688,6 @@ const ConfigPatchRequestSchema = z
756
688
  function handleGetConfig() {
757
689
  try {
758
690
  const config = applyContextDefaultsToRawConfig(loadRawConfig());
759
- sanitizeMcpTransportHeadersForSettingsRead(config);
760
691
  enrichProfilesWithVisionFlag(config);
761
692
  return config;
762
693
  } catch (err) {
@@ -909,7 +840,6 @@ async function handlePatchConfig({ body }: RouteHandlerArgs) {
909
840
  throw new BadRequestError("Body must be a non-empty JSON object");
910
841
  }
911
842
  rejectManagedProfileDeletion(body as Record<string, unknown>);
912
- rejectMcpTransportHeaderWrite(body);
913
843
 
914
844
  const raw = loadRawConfig();
915
845
  const patch = body as Record<string, unknown>;
@@ -918,7 +848,6 @@ async function handlePatchConfig({ body }: RouteHandlerArgs) {
918
848
  await commitConfigWrite(raw, "patch");
919
849
 
920
850
  const merged = applyContextDefaultsToRawConfig(loadRawConfig());
921
- sanitizeMcpTransportHeadersForSettingsRead(merged);
922
851
  enrichProfilesWithVisionFlag(merged);
923
852
  return merged;
924
853
  }
@@ -963,7 +892,6 @@ async function handleSetConfig({ body }: RouteHandlerArgs) {
963
892
  const patchShape: Record<string, unknown> = {};
964
893
  setNestedValue(patchShape, path, value);
965
894
  rejectManagedProfileDeletion(patchShape);
966
- rejectMcpTransportHeaderWrite(patchShape);
967
895
 
968
896
  const raw = loadRawConfig();
969
897
  setNestedValue(raw, path, value);
@@ -50,7 +50,6 @@ import {
50
50
  getCannedFirstGreeting,
51
51
  isWakeUpGreeting,
52
52
  } from "../../daemon/first-greeting.js";
53
- import { supersedePendingInteractionsOnEnqueue } from "../../daemon/handlers/conversations.js";
54
53
  import {
55
54
  collectAttachmentRefs,
56
55
  type HistoryAttachmentRef,
@@ -68,7 +67,6 @@ import type {
68
67
  HostProxyTransportMetadata,
69
68
  NonHostProxyTransportMetadata,
70
69
  } from "../../daemon/message-types/conversations.js";
71
- import type { TrustContext } from "../../daemon/trust-context.js";
72
70
  import { HeartbeatService } from "../../heartbeat/heartbeat-service.js";
73
71
  import {
74
72
  writeOnboardingSidecar,
@@ -122,30 +120,30 @@ import {
122
120
  } from "../../util/platform.js";
123
121
  import { silentlyWithLog } from "../../util/silently.js";
124
122
  import { assistantEventHub, broadcastMessage } from "../assistant-event-hub.js";
123
+ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
125
124
  import { getPersistedSeq } from "../assistant-stream-state.js";
126
125
  import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
127
126
  import {
128
127
  type GuardianPendingScope,
129
128
  routeGuardianReply,
130
129
  } from "../guardian-reply-router.js";
131
- import { reResolveTrustOnResetDrift } from "../guardian-vellum-migration.js";
130
+ import { healGuardianBindingDrift } from "../guardian-vellum-migration.js";
132
131
  import type {
133
132
  ApprovalConversationGenerator,
134
133
  RuntimeAttachmentMetadata,
135
134
  RuntimeMessagePayload,
136
135
  SendMessageDeps,
137
136
  } from "../http-types.js";
138
- import {
139
- findLocalGuardianPrincipalId,
140
- resolveActorPrincipalIdForLocalGuardian,
141
- } from "../local-actor-identity.js";
142
- import { resolveLocalPrincipalTrustContext } from "../local-principal-trust.js";
137
+ import { resolveLocalTrustContext } from "../local-actor-identity.js";
143
138
  import * as pendingInteractions from "../pending-interactions.js";
144
139
  import {
145
140
  publishConversationListAndMetadataChanged,
146
141
  publishConversationMessagesChanged,
147
142
  } from "../sync/resource-sync-events.js";
148
- import { withSourceChannel } from "../trust-context-resolver.js";
143
+ import {
144
+ resolveTrustContext,
145
+ withSourceChannel,
146
+ } from "../trust-context-resolver.js";
149
147
  import {
150
148
  BadRequestError,
151
149
  InternalError,
@@ -948,28 +946,25 @@ export function handleListMessages({
948
946
  .filter((block) => block.type !== "text" || block.text.length > 0);
949
947
  }
950
948
 
951
- // Ensure every hydrated attachment has a corresponding content block.
952
- // renderHistoryContent inlines attachment blocks only when it has
953
- // file-block refs with matching DB rows; directives (assistant-authored
954
- // <vellum-attachment/> tags) don't leave a file block after stripping,
955
- // so their attachments end up in the flat `attachments` array but not in
956
- // `contentBlocks`. Append any that are missing so the canonical
957
- // projection is complete.
958
- const existingAttachmentIds = new Set(
959
- contentBlocks
960
- .filter(
961
- (b): b is Extract<ConversationContentBlock, { type: "attachment" }> =>
962
- b.type === "attachment",
963
- )
964
- .map((b) => b.attachment.id),
965
- );
966
- for (const att of msgAttachments) {
967
- if (!existingAttachmentIds.has(att.id)) {
968
- contentBlocks.push({ type: "attachment", attachment: att });
969
- }
949
+ // Ensure every hydrated attachment has a corresponding content block.
950
+ // renderHistoryContent inlines attachment blocks only when it has
951
+ // file-block refs with matching DB rows; directives (assistant-authored
952
+ // <vellum-attachment/> tags) don't leave a file block after stripping,
953
+ // so their attachments end up in the flat `attachments` array but not in
954
+ // `contentBlocks`. Append any that are missing so the canonical
955
+ // projection is complete.
956
+ const existingAttachmentIds = new Set(
957
+ contentBlocks
958
+ .filter((b): b is Extract<ConversationContentBlock, { type: "attachment" }> => b.type === "attachment")
959
+ .map((b) => b.attachment.id),
960
+ );
961
+ for (const att of msgAttachments) {
962
+ if (!existingAttachmentIds.has(att.id)) {
963
+ contentBlocks.push({ type: "attachment", attachment: att });
970
964
  }
965
+ }
971
966
 
972
- const alignedContentOrder = aligned.rewriteContentOrder(contentOrder);
967
+ const alignedContentOrder = aligned.rewriteContentOrder(contentOrder);
973
968
 
974
969
  // Use sentAt (actual event time) for the display timestamp when available,
975
970
  // falling back to createdAt (persistence time). Clients use this display
@@ -1445,65 +1440,56 @@ export async function handleSendMessage(
1445
1440
  conversation.setOnboardingContext(body.onboarding!);
1446
1441
  }
1447
1442
 
1448
- // Resolve guardian context from the AuthContext's actorPrincipalId via the
1449
- // gateway guardian binding: a vellum principal is the guardian or nobody.
1443
+ // Resolve guardian context from the AuthContext's actorPrincipalId.
1444
+ // The JWT-verified principal is used as the sender identity through
1445
+ // the same trust resolution pipeline that channel ingress uses.
1450
1446
  if (actorPrincipalId) {
1451
1447
  // Dev bypass (HTTP auth disabled): the synthetic "dev-bypass" principal
1452
- // won't match any guardian binding. Resolve the real guardian principal and
1453
- // map that through, failing closed to unknown on an empty gateway.
1448
+ // won't match any guardian binding. Resolve from the local guardian
1449
+ // binding instead, which produces the correct guardian trust context.
1454
1450
  if (isHttpAuthDisabled() && actorPrincipalId === "dev-bypass") {
1455
- const guardianPrincipalId = await findLocalGuardianPrincipalId();
1456
- let trustCtx: TrustContext = guardianPrincipalId
1457
- ? withSourceChannel(
1458
- sourceChannel,
1459
- await resolveLocalPrincipalTrustContext({
1460
- actorPrincipalId: guardianPrincipalId,
1461
- sourceChannel: "vellum",
1462
- conversationExternalId: "local",
1463
- }),
1464
- )
1465
- : { trustClass: "unknown", sourceChannel };
1466
- if (guardianPrincipalId && trustCtx.trustClass === "unknown") {
1467
- const healed = await reResolveTrustOnResetDrift(
1468
- guardianPrincipalId,
1469
- sourceChannel,
1470
- );
1471
- if (healed) trustCtx = healed;
1472
- }
1473
- conversation.setTrustContext(trustCtx);
1451
+ conversation.setTrustContext(resolveLocalTrustContext(sourceChannel));
1474
1452
  } else {
1475
- let trustCtx = withSourceChannel(
1476
- sourceChannel,
1477
- await resolveLocalPrincipalTrustContext({
1478
- actorPrincipalId,
1479
- sourceChannel: "vellum",
1480
- conversationExternalId: "local",
1481
- }),
1482
- );
1453
+ const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
1454
+ let trustCtx = resolveTrustContext({
1455
+ assistantId,
1456
+ sourceChannel: "vellum",
1457
+ conversationExternalId: "local",
1458
+ actorExternalId: actorPrincipalId,
1459
+ });
1483
1460
  if (trustCtx.trustClass === "unknown") {
1484
- const healed = await reResolveTrustOnResetDrift(
1485
- actorPrincipalId,
1486
- sourceChannel,
1487
- );
1488
- if (healed && healed.trustClass !== "unknown") {
1489
- trustCtx = healed;
1461
+ // Attempt to heal guardian binding drift: after a DB reset the
1462
+ // guardian binding gets a new vellum-principal-* UUID while the
1463
+ // client still holds a valid JWT with the old one. The signing
1464
+ // key survives the reset, so the JWT is authentic — just stale.
1465
+ const healed = healGuardianBindingDrift(actorPrincipalId);
1466
+ if (healed) {
1467
+ trustCtx = resolveTrustContext({
1468
+ assistantId,
1469
+ sourceChannel: "vellum",
1470
+ conversationExternalId: "local",
1471
+ actorExternalId: actorPrincipalId,
1472
+ });
1490
1473
  log.info(
1491
- { actorPrincipalId, trustClass: trustCtx.trustClass },
1492
- "Trust re-resolved from local mirror after gateway returned unknown",
1474
+ {
1475
+ actorPrincipalId: actorPrincipalId,
1476
+ trustClass: trustCtx.trustClass,
1477
+ },
1478
+ "Trust re-resolved after guardian binding drift heal",
1493
1479
  );
1494
1480
  } else {
1495
1481
  log.warn(
1496
1482
  {
1497
- actorPrincipalId,
1483
+ actorPrincipalId: actorPrincipalId,
1498
1484
  sourceChannel,
1499
- trustClass: "unknown",
1500
- principalType,
1485
+ trustClass: trustCtx.trustClass,
1486
+ principalType: principalType,
1501
1487
  },
1502
1488
  "JWT-verified actor resolved to unknown trust class — possible guardian binding drift (e.g. DB reset without re-bootstrap)",
1503
1489
  );
1504
1490
  }
1505
1491
  }
1506
- conversation.setTrustContext(trustCtx);
1492
+ conversation.setTrustContext(withSourceChannel(sourceChannel, trustCtx));
1507
1493
  }
1508
1494
  } else {
1509
1495
  // Service principals (svc_gateway) or tokens without an actor ID
@@ -1512,13 +1498,10 @@ export async function handleSendMessage(
1512
1498
  }
1513
1499
 
1514
1500
  const isInteractive = isInteractiveInterface(sourceInterface);
1515
- // Translate the dev-bypass actor principal to the real guardian principal
1516
- // before the same-actor host-proxy gate so web/iOS turns match the macOS
1517
- // client's SSE-registered principal. No-op for real JWT principals in
1518
- // non-dev-bypass deployments.
1519
- const sourceActorPrincipalId = await resolveActorPrincipalIdForLocalGuardian(
1520
- actorPrincipalId ?? undefined,
1521
- );
1501
+ // Use the JWT-verified requester principal not guardianPrincipalId,
1502
+ // which is the workspace owner and would let a trusted contact's web
1503
+ // turn match against the guardian's macOS client.
1504
+ const sourceActorPrincipalId = actorPrincipalId ?? undefined;
1522
1505
  // Bash/File/Transfer singletons are globally available via isAvailable() —
1523
1506
  // no per-conversation gating needed. CU is per-conversation (owns step
1524
1507
  // count, AX tree history, loop detection).
@@ -1831,11 +1814,29 @@ export async function handleSendMessage(
1831
1814
  // the client showing "Failed to send" for a message the daemon will
1832
1815
  // process from the queue.
1833
1816
  try {
1834
- // Supersede interactions left pending by the in-flight turn: auto-deny
1835
- // confirmations (with canonical/client sync) and steer to the enqueued
1836
- // message if an ask_question is parked. Centralized so the CLI signal
1837
- // path (signals/user-message.ts) gets identical handling.
1838
- supersedePendingInteractionsOnEnqueue(mapping.conversationId, requestId);
1817
+ if (conversation.hasAnyPendingConfirmation()) {
1818
+ // Emit authoritative denial state for each pending request.
1819
+ // sendToClient (wired to the SSE hub) delivers these to the client.
1820
+ for (const interaction of pendingInteractions.getByConversation(
1821
+ mapping.conversationId,
1822
+ )) {
1823
+ if (interaction.kind === "confirmation") {
1824
+ conversation.emitConfirmationStateChanged({
1825
+ conversationId: mapping.conversationId,
1826
+ requestId: interaction.requestId,
1827
+ state: "denied" as const,
1828
+ source: "auto_deny" as const,
1829
+ });
1830
+ // Sync canonical guardian request status so stale "pending" DB
1831
+ // records don't get matched by later guardian reply routing.
1832
+ resolveCanonicalGuardianRequest(interaction.requestId, "pending", {
1833
+ status: "denied",
1834
+ });
1835
+ }
1836
+ }
1837
+ conversation.denyAllPendingConfirmations();
1838
+ pendingInteractions.removeByConversation(mapping.conversationId);
1839
+ }
1839
1840
 
1840
1841
  // Expire any orphaned canonical requests that survived without a
1841
1842
  // matching in-memory pending interaction (e.g. prompter timeouts).
@@ -43,7 +43,7 @@ import type { ReplaySubscriber } from "../assistant-stream-state.js";
43
43
  import { getReplayWindow } from "../assistant-stream-state.js";
44
44
  import { ACTOR_PRINCIPALS, GATEWAY_PRINCIPALS } from "../auth/route-policy.js";
45
45
  import { DEFAULT_HEARTBEAT_INTERVAL_MS } from "../client-health.js";
46
- import { resolveActorPrincipalIdForLocalGuardianSync } from "../local-actor-identity.js";
46
+ import { resolveActorPrincipalIdForLocalGuardian } from "../local-actor-identity.js";
47
47
  import {
48
48
  BadRequestError,
49
49
  NotFoundError,
@@ -311,7 +311,7 @@ export function handleSubscribeAssistantEvents(
311
311
  // bearer token's AuthContext. May be absent for legacy / service-token
312
312
  // connections that have no principal. See `resolveActorPrincipalId` for the
313
313
  // dev-bypass translation rationale.
314
- const actorPrincipalId = resolveActorPrincipalIdForLocalGuardianSync(
314
+ const actorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
315
315
  rawActorPrincipalId?.trim() || undefined,
316
316
  );
317
317
 
@@ -263,9 +263,7 @@ async function handleGlobalSearch({
263
263
  id: c.id,
264
264
  displayName: c.displayName,
265
265
  notes: c.notes,
266
- // Daemon-native search has no gateway-relayed read; recency orders on
267
- // contacts.updatedAt, not the channel-derived lastInteraction column.
268
- lastInteraction: c.updatedAt,
266
+ lastInteraction: c.lastInteraction,
269
267
  }));
270
268
  }
271
269
 
@@ -14,6 +14,7 @@
14
14
  import { z } from "zod";
15
15
 
16
16
  import { isHttpAuthDisabled } from "../../config/env.js";
17
+ import { findGuardianForChannel } from "../../contacts/contact-store.js";
17
18
  import {
18
19
  type CanonicalGuardianRequest,
19
20
  listPendingRequestsByConversationScope,
@@ -22,7 +23,6 @@ import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
22
23
  import { processGuardianDecision } from "../guardian-action-service.js";
23
24
  import type { GuardianDecisionPrompt } from "../guardian-decision-types.js";
24
25
  import { buildOneTimeDecisionActions } from "../guardian-decision-types.js";
25
- import { findLocalGuardianPrincipalId } from "../local-actor-identity.js";
26
26
  import { BadRequestError, NotFoundError } from "./errors.js";
27
27
  import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
28
28
 
@@ -73,15 +73,16 @@ async function handleGuardianActionDecision({
73
73
  // Resolve the actor's guardian principal ID. The HTTP adapter injects it
74
74
  // from the AuthContext via the x-vellum-actor-principal-id header.
75
75
  // For dev bypass (HTTP auth disabled) the synthetic "dev-bypass" principal
76
- // won't match the real guardian binding, so resolve the local guardian
77
- // principal to avoid identity_mismatch.
76
+ // won't match the real guardian binding, so fall back to the local guardian
77
+ // binding to avoid identity_mismatch.
78
78
  let guardianPrincipalId: string | undefined =
79
79
  headers["x-vellum-actor-principal-id"] ?? undefined;
80
80
  if (
81
81
  isHttpAuthDisabled() &&
82
82
  headers["x-vellum-actor-principal-id"] === "dev-bypass"
83
83
  ) {
84
- guardianPrincipalId = (await findLocalGuardianPrincipalId()) ?? undefined;
84
+ const binding = findGuardianForChannel("vellum");
85
+ guardianPrincipalId = binding?.contact.principalId ?? undefined;
85
86
  }
86
87
 
87
88
  const result = await processGuardianDecision({
@@ -39,7 +39,7 @@ const VALID_STATES: ReadonlySet<HostAppControlState> = new Set([
39
39
  // POST /v1/host-app-control-result
40
40
  // ---------------------------------------------------------------------------
41
41
 
42
- async function handleHostAppControlResult({ body, headers }: RouteHandlerArgs) {
42
+ function handleHostAppControlResult({ body, headers }: RouteHandlerArgs) {
43
43
  if (!body || typeof body !== "object") {
44
44
  throw new BadRequestError("Request body is required");
45
45
  }
@@ -95,10 +95,9 @@ async function handleHostAppControlResult({ body, headers }: RouteHandlerArgs) {
95
95
  `Client "${submittingClientId}" is not the target for this request (expected "${peeked.targetClientId}"). The targeted client must submit the result.`,
96
96
  );
97
97
  }
98
- const submittingActorPrincipalId =
99
- await resolveActorPrincipalIdForLocalGuardian(
100
- headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
101
- );
98
+ const submittingActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
99
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
100
+ );
102
101
  enforceSameActorOrThrow({
103
102
  sourceActorPrincipalId: submittingActorPrincipalId,
104
103
  targetActorPrincipalId: peeked.targetActorPrincipalId,
@@ -26,7 +26,7 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
26
26
  // POST /v1/host-bash-result
27
27
  // ---------------------------------------------------------------------------
28
28
 
29
- async function handleHostBashResult({ body, headers }: RouteHandlerArgs) {
29
+ function handleHostBashResult({ body, headers }: RouteHandlerArgs) {
30
30
  if (!body || typeof body !== "object") {
31
31
  throw new BadRequestError("Request body is required");
32
32
  }
@@ -45,10 +45,9 @@ async function handleHostBashResult({ body, headers }: RouteHandlerArgs) {
45
45
 
46
46
  const submittingClientId =
47
47
  headers?.["x-vellum-client-id"]?.trim() || undefined;
48
- const submittingActorPrincipalId =
49
- await resolveActorPrincipalIdForLocalGuardian(
50
- headers?.["x-vellum-actor-principal-id"]?.trim() || undefined,
51
- );
48
+ const submittingActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
49
+ headers?.["x-vellum-actor-principal-id"]?.trim() || undefined,
50
+ );
52
51
 
53
52
  const peeked = pendingInteractions.get(requestId);
54
53
  if (!peeked) {
@@ -61,14 +61,14 @@ export type HostBrowserResultResolution =
61
61
  * already authenticated the caller (the HTTP route uses
62
62
  * `requireBoundGuardian`).
63
63
  */
64
- export async function resolveHostBrowserResultByRequestId(
64
+ export function resolveHostBrowserResultByRequestId(
65
65
  frame: {
66
66
  requestId?: unknown;
67
67
  content?: unknown;
68
68
  isError?: unknown;
69
69
  },
70
70
  headers?: Record<string, string | undefined>,
71
- ): Promise<HostBrowserResultResolution> {
71
+ ): HostBrowserResultResolution {
72
72
  const { requestId, content, isError } = frame;
73
73
 
74
74
  if (!requestId || typeof requestId !== "string") {
@@ -127,10 +127,9 @@ export async function resolveHostBrowserResultByRequestId(
127
127
  // stream. This prevents a different authenticated user with knowledge of
128
128
  // both the requestId and target clientId from submitting a result on
129
129
  // behalf of the targeted client.
130
- const submittingActorPrincipalId =
131
- await resolveActorPrincipalIdForLocalGuardian(
132
- headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
133
- );
130
+ const submittingActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
131
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
132
+ );
134
133
  try {
135
134
  enforceSameActorOrThrow({
136
135
  sourceActorPrincipalId: submittingActorPrincipalId,
@@ -261,12 +260,12 @@ export function resolveHostBrowserSessionInvalidated(frame: {
261
260
  // POST /v1/host-browser-result
262
261
  // ---------------------------------------------------------------------------
263
262
 
264
- async function handleHostBrowserResult({ body, headers }: RouteHandlerArgs) {
263
+ function handleHostBrowserResult({ body, headers }: RouteHandlerArgs) {
265
264
  if (!body || typeof body !== "object") {
266
265
  throw new BadRequestError("Request body is required");
267
266
  }
268
267
 
269
- const resolution = await resolveHostBrowserResultByRequestId(
268
+ const resolution = resolveHostBrowserResultByRequestId(
270
269
  body,
271
270
  headers as Record<string, string | undefined> | undefined,
272
271
  );
@@ -400,7 +399,10 @@ export const ROUTES: RouteDefinition[] = [
400
399
  "Marks the target as invalidated in the runtime-side browser session registry.",
401
400
  tags: ["host"],
402
401
  requestBody: z.object({
403
- targetId: z.string().optional().describe("CDP target that was detached"),
402
+ targetId: z
403
+ .string()
404
+ .optional()
405
+ .describe("CDP target that was detached"),
404
406
  reason: z.string().optional().describe("Detach reason"),
405
407
  clientId: z
406
408
  .string()
@@ -26,7 +26,7 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
26
26
  // POST /v1/host-cu-result
27
27
  // ---------------------------------------------------------------------------
28
28
 
29
- async function handleHostCuResult({ body, headers }: RouteHandlerArgs) {
29
+ function handleHostCuResult({ body, headers }: RouteHandlerArgs) {
30
30
  if (!body || typeof body !== "object") {
31
31
  throw new BadRequestError("Request body is required");
32
32
  }
@@ -95,10 +95,9 @@ async function handleHostCuResult({ body, headers }: RouteHandlerArgs) {
95
95
  // stream. This prevents a different authenticated user with knowledge of
96
96
  // both the requestId and target clientId from submitting a result on
97
97
  // behalf of the targeted client.
98
- const submittingActorPrincipalId =
99
- await resolveActorPrincipalIdForLocalGuardian(
100
- headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
101
- );
98
+ const submittingActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
99
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
100
+ );
102
101
  enforceSameActorOrThrow({
103
102
  sourceActorPrincipalId: submittingActorPrincipalId,
104
103
  targetActorPrincipalId: peeked.targetActorPrincipalId,
@@ -26,7 +26,7 @@ import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
26
26
  // POST /v1/host-file-result
27
27
  // ---------------------------------------------------------------------------
28
28
 
29
- async function handleHostFileResult({ body, headers }: RouteHandlerArgs) {
29
+ function handleHostFileResult({ body, headers }: RouteHandlerArgs) {
30
30
  if (!body || typeof body !== "object") {
31
31
  throw new BadRequestError("Request body is required");
32
32
  }
@@ -76,10 +76,9 @@ async function handleHostFileResult({ body, headers }: RouteHandlerArgs) {
76
76
  // match the actor that opened the target client's SSE stream. This blocks
77
77
  // cross-user submissions even if a different user somehow obtains the
78
78
  // target client id.
79
- const submittingActorPrincipalId =
80
- await resolveActorPrincipalIdForLocalGuardian(
81
- headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
82
- );
79
+ const submittingActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
80
+ headerMap["x-vellum-actor-principal-id"]?.trim() || undefined,
81
+ );
83
82
  enforceSameActorOrThrow({
84
83
  sourceActorPrincipalId: submittingActorPrincipalId,
85
84
  targetActorPrincipalId: peeked.targetActorPrincipalId,