@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
@@ -1,223 +0,0 @@
1
- /**
2
- * Gateway-backed guardian binding + delivery reader.
3
- *
4
- * Resolves the active guardian binding(s) and their per-channel delivery
5
- * endpoints from the gateway via the `resolve_guardian_delivery` IPC route,
6
- * validating the response against {@link ResolveGuardianDeliveryResponseSchema}.
7
- *
8
- * Guardian binding is near-static — it only changes on guardian onboarding /
9
- * verification or revocation — yet this reader sits on many hot paths. To keep
10
- * those paths off the IPC, results are cached behind a minutes-scale TTL and
11
- * coalesced single-flight so a cold cache storms the gateway at most once.
12
- *
13
- * Freshness comes from two sources: the {@link invalidateGuardianDeliveryCache}
14
- * subscription to `onContactChange` (contact mutations clear the cache), and
15
- * {@link getGuardianDeliveryFresh} reads on existence guards (gateway-side
16
- * binding writes don't invalidate the daemon cache, so those paths read fresh).
17
- *
18
- * Returns `null` on ANY failure (transport failure, malformed shape, timeout,
19
- * or thrown error); failures are NOT cached, so a recovered gateway is retried
20
- * on the next call.
21
- */
22
-
23
- import {
24
- type GuardianDelivery,
25
- ResolveGuardianDeliveryResponseSchema,
26
- } from "@vellumai/gateway-client";
27
-
28
- import { ipcCall } from "../ipc/gateway-client.js";
29
- import { onContactChange } from "./contact-events.js";
30
-
31
- // Short IPC timeout so the read resolves promptly rather than stalling a hot
32
- // path on a gateway that accepts the socket but hangs.
33
- const GUARDIAN_DELIVERY_IPC_TIMEOUT_MS = 2_000;
34
-
35
- // Guardian binding is near-static, so a minutes-scale TTL is safe; freshness is
36
- // driven primarily by event-based invalidation, not by this backstop expiry.
37
- const GUARDIAN_DELIVERY_CACHE_TTL_MS = 300_000;
38
-
39
- interface CacheEntry {
40
- guardians: GuardianDelivery[];
41
- fetchedAt: number;
42
- }
43
-
44
- const cache = new Map<string, CacheEntry>();
45
- // Tracks whether the in-flight fetch was a force-refresh, so a fresh read never
46
- // coalesces with an older non-force fetch that may predate a gateway-side write.
47
- const inFlight = new Map<
48
- string,
49
- { promise: Promise<GuardianDelivery[] | null>; fresh: boolean }
50
- >();
51
-
52
- // Bumped on every invalidation. A fetch captures the generation when it starts
53
- // and only writes its result to the cache if the generation is unchanged on
54
- // resolve, so an invalidation mid-flight can't repopulate a stale pre-change
55
- // result and mask a guardian-binding change.
56
- let cacheGeneration = 0;
57
-
58
- function cacheKey(channelTypes?: string[]): string {
59
- if (!channelTypes || channelTypes.length === 0) return "ALL";
60
- return [...channelTypes].sort().join(",");
61
- }
62
-
63
- async function fetchGuardianDelivery(
64
- input: { channelTypes?: string[] },
65
- ): Promise<GuardianDelivery[] | null> {
66
- try {
67
- const result = await ipcCall(
68
- "resolve_guardian_delivery",
69
- input,
70
- GUARDIAN_DELIVERY_IPC_TIMEOUT_MS,
71
- );
72
- if (!result) return null;
73
-
74
- const parsed = ResolveGuardianDeliveryResponseSchema.safeParse(result);
75
- return parsed.success ? parsed.data.guardians : null;
76
- } catch {
77
- return null;
78
- }
79
- }
80
-
81
- // Shared fetch path for both the cached and fresh public surfaces. When
82
- // `forceRefresh` is set the cached entry is bypassed; the read is still
83
- // single-flight and still populates the cache with the fresh result.
84
- async function readGuardianDelivery(
85
- input: { channelTypes?: string[]; forceRefresh?: boolean },
86
- ): Promise<GuardianDelivery[] | null> {
87
- const key = cacheKey(input.channelTypes);
88
-
89
- if (!input.forceRefresh) {
90
- const cached = cache.get(key);
91
- if (
92
- cached &&
93
- Date.now() - cached.fetchedAt < GUARDIAN_DELIVERY_CACHE_TTL_MS
94
- ) {
95
- return cached.guardians;
96
- }
97
- }
98
-
99
- // A non-force read may coalesce with any in-flight fetch. A force read may
100
- // only coalesce with another force fetch — never with a non-force fetch that
101
- // could have started before a gateway-side binding write and resolve stale.
102
- const pending = inFlight.get(key);
103
- if (pending && (!input.forceRefresh || pending.fresh)) return pending.promise;
104
-
105
- const startGen = cacheGeneration;
106
- const promise = fetchGuardianDelivery({ channelTypes: input.channelTypes })
107
- .then((guardians) => {
108
- // Skip the write if an invalidation fired during the fetch: the result
109
- // may predate the change. Return it to this caller (freshest it has) but
110
- // leave the cache empty so the next call re-fetches.
111
- if (guardians && cacheGeneration === startGen) {
112
- cache.set(key, { guardians, fetchedAt: Date.now() });
113
- }
114
- return guardians;
115
- })
116
- .finally(() => {
117
- // Only clear the slot if it still holds this fetch — a concurrent force
118
- // read may have replaced a non-force entry (or vice versa).
119
- if (inFlight.get(key)?.promise === promise) inFlight.delete(key);
120
- });
121
-
122
- inFlight.set(key, { promise, fresh: !!input.forceRefresh });
123
- return promise;
124
- }
125
-
126
- /**
127
- * Resolve active guardian deliveries, optionally filtered by channel type.
128
- * Returns the cached list when fresh, otherwise fetches (single-flight) and
129
- * caches on success. Returns `null` on failure without caching.
130
- *
131
- * To force an uncached read, call {@link getGuardianDeliveryFresh} — the only
132
- * public fresh-read entry point.
133
- */
134
- export async function getGuardianDelivery(
135
- input?: { channelTypes?: string[] },
136
- ): Promise<GuardianDelivery[] | null> {
137
- return readGuardianDelivery({ channelTypes: input?.channelTypes });
138
- }
139
-
140
- /**
141
- * Synchronous read of the already-cached guardian deliveries, without any IO.
142
- *
143
- * Returns the fresh cached list for the given channel filter, or `undefined`
144
- * when the cache is cold or expired. Used by sync hot paths (SSE subscribe)
145
- * that cannot await {@link getGuardianDelivery} but must resolve the SAME
146
- * gateway-owned principal the async paths land on. A cold/expired return lets
147
- * the caller fall back to the local store as before.
148
- */
149
- export function peekCachedGuardianDelivery(
150
- input?: { channelTypes?: string[] },
151
- ): GuardianDelivery[] | undefined {
152
- const cached = cache.get(cacheKey(input?.channelTypes));
153
- if (!cached) return undefined;
154
- if (Date.now() - cached.fetchedAt >= GUARDIAN_DELIVERY_CACHE_TTL_MS) {
155
- return undefined;
156
- }
157
- return cached.guardians;
158
- }
159
-
160
- /**
161
- * Fresh (uncached) variant of {@link getGuardianDelivery}. Existence guards read
162
- * fresh because gateway-side binding writes don't invalidate the daemon cache.
163
- * Still single-flight, and still populates the cache with the fresh result.
164
- */
165
- export async function getGuardianDeliveryFresh(
166
- input?: { channelTypes?: string[] },
167
- ): Promise<GuardianDelivery[] | null> {
168
- return readGuardianDelivery({
169
- channelTypes: input?.channelTypes,
170
- forceRefresh: true,
171
- });
172
- }
173
-
174
- /**
175
- * Clear ALL cached guardian deliveries. Subscribed to `onContactChange` so
176
- * contact mutations refetch on the next read; also exported for any caller that
177
- * wants to invalidate explicitly.
178
- */
179
- export function invalidateGuardianDeliveryCache(): void {
180
- cacheGeneration += 1;
181
- cache.clear();
182
- inFlight.clear();
183
- }
184
-
185
- onContactChange(invalidateGuardianDeliveryCache);
186
-
187
- /** First active guardian delivery for the given channel type, if any. */
188
- export function guardianForChannel(
189
- list: GuardianDelivery[],
190
- channelType: string,
191
- ): GuardianDelivery | undefined {
192
- return list.find(
193
- (g) => g.channelType === channelType && g.status === "active",
194
- );
195
- }
196
-
197
- /** First guardian delivery overall — the `listGuardianChannels` fallback. */
198
- export function anyGuardian(
199
- list: GuardianDelivery[],
200
- ): GuardianDelivery | undefined {
201
- return list[0];
202
- }
203
-
204
- /**
205
- * Resolve a guardian displayName for voice surfaces: prefer the phone-channel
206
- * guardian, falling back to any guardian. Returns `undefined` when the list is
207
- * absent or no guardian carries a displayName.
208
- */
209
- export function voiceGuardianDisplayName(
210
- list: GuardianDelivery[] | null,
211
- ): string | undefined {
212
- const guardian = list
213
- ? (guardianForChannel(list, "phone") ?? anyGuardian(list))
214
- : undefined;
215
- return guardian?.displayName ?? undefined;
216
- }
217
-
218
- /** Test-only: reset cache + in-flight state for deterministic test runs. */
219
- export function __resetGuardianDeliveryCacheForTest(): void {
220
- cache.clear();
221
- inFlight.clear();
222
- cacheGeneration = 0;
223
- }
@@ -1,189 +0,0 @@
1
- /**
2
- * Gateway-first member-channel write relay.
3
- *
4
- * The gateway is the ACL source of truth. Each write goes to the gateway first;
5
- * the assistant-DB write in contacts-write.ts is a best-effort local mirror that
6
- * never throws and never gates the gateway-owned outcome.
7
- */
8
-
9
- import {
10
- MarkChannelRevokedIpcResponseSchema,
11
- UpsertVerifiedChannelIpcResponseSchema,
12
- } from "@vellumai/gateway-client/gateway-ipc-contracts";
13
-
14
- import { log } from "../daemon/handlers/shared.js";
15
- import { ipcCallPersistent } from "../ipc/gateway-client.js";
16
- import { revokeMember, upsertContactChannel } from "./contacts-write.js";
17
- import type { ContactWriteResult } from "./types.js";
18
-
19
- // ── Activate ─────────────────────────────────────────────────────────
20
-
21
- export interface ActivateMemberChannelParams {
22
- sourceChannel: string;
23
- externalUserId?: string;
24
- externalChatId?: string;
25
- contactId?: string;
26
- displayName?: string;
27
- username?: string;
28
- inviteId?: string;
29
- verifiedAt?: number;
30
- verifiedVia?: string;
31
- policy?: string;
32
- }
33
-
34
- /**
35
- * Outcome of a gateway-first member activation. The gateway owns the ACL
36
- * verdict; `activated` carries a stable memberId (gateway channel id, or the
37
- * local mirror's id when present) regardless of whether the best-effort local
38
- * mirror produced a row. `refused` means the gateway authoritatively denied the
39
- * actor (blocked/revoked) — callers must NOT treat it as an activation.
40
- */
41
- export type ActivateMemberOutcome =
42
- | { status: "activated"; memberId: string; member: ContactWriteResult | null }
43
- | { status: "refused" };
44
-
45
- /**
46
- * Activate a member channel gateway-first, then mirror the activation to the
47
- * assistant DB best-effort. The gateway owns the ACL outcome; the local mirror
48
- * supplies the native contact/channel callers still wire downstream.
49
- *
50
- * A gateway failure fails open with a logged warning (matching the redemption
51
- * service's record_invite_redemption posture) so a transient gateway outage
52
- * never drops a legitimate activation — the local mirror still stands.
53
- *
54
- * Returns an `activated` outcome with a stable memberId on success, or
55
- * `refused` when the gateway denies the actor. A best-effort local-mirror
56
- * failure never downgrades a verified gateway activation.
57
- */
58
- export async function activateMemberChannel(
59
- params: ActivateMemberChannelParams,
60
- ): Promise<ActivateMemberOutcome> {
61
- const address = params.externalUserId ?? params.externalChatId;
62
- const externalChatId = params.externalChatId ?? params.externalUserId;
63
-
64
- let gatewayChannelId: string | undefined;
65
-
66
- if (address && externalChatId) {
67
- try {
68
- const result = await ipcCallPersistent("upsert_verified_channel", {
69
- type: params.sourceChannel,
70
- address,
71
- externalChatId,
72
- displayName: params.displayName,
73
- username: params.username,
74
- verifiedVia: params.verifiedVia ?? "invite",
75
- contactId: params.contactId,
76
- // Invite redemption may reactivate a revoked member; blocked actors are
77
- // still refused by the gateway guard.
78
- allowRevokedReactivation: true,
79
- });
80
- const parsed = UpsertVerifiedChannelIpcResponseSchema.parse(result);
81
- // The gateway refused the actor (blocked/revoked): do NOT mirror an
82
- // active local channel for an actor the gateway has denied.
83
- if (!parsed.verified) {
84
- log.warn(
85
- { sourceChannel: params.sourceChannel },
86
- "Gateway refused the channel activation; skipping the local mirror",
87
- );
88
- return { status: "refused" };
89
- }
90
- gatewayChannelId = parsed.channel?.id;
91
- } catch (err) {
92
- // Fail-open: the gateway write may or may not have landed. Proceed to the
93
- // local mirror so a transient outage never drops a legitimate activation.
94
- log.warn(
95
- { err, sourceChannel: params.sourceChannel },
96
- "upsert_verified_channel relay unavailable — failing open (local mirror still applies)",
97
- );
98
- }
99
- }
100
-
101
- const member = mirrorLocalActivation(params);
102
- const memberId = member?.channel.id ?? gatewayChannelId;
103
- if (!memberId) {
104
- // No gateway channel (the relay threw or was skipped) AND no local mirror —
105
- // no stable id to hand the caller. Surface as refused so the caller maps it
106
- // to a non-redeemed outcome instead of crashing on a missing memberId.
107
- log.error(
108
- { sourceChannel: params.sourceChannel },
109
- "Member activation produced no gateway channel and no local mirror; no memberId to return",
110
- );
111
- return { status: "refused" };
112
- }
113
- return { status: "activated", memberId, member };
114
- }
115
-
116
- /** Best-effort local mirror of the activation. Swallows failures. */
117
- function mirrorLocalActivation(
118
- params: ActivateMemberChannelParams,
119
- ): ContactWriteResult | null {
120
- try {
121
- return upsertContactChannel({
122
- sourceChannel: params.sourceChannel,
123
- externalUserId: params.externalUserId,
124
- externalChatId: params.externalChatId,
125
- displayName: params.displayName,
126
- username: params.username,
127
- role: "contact",
128
- status: "active",
129
- policy: params.policy ?? "allow",
130
- inviteId: params.inviteId,
131
- verifiedAt: params.verifiedAt,
132
- verifiedVia: params.verifiedVia ?? "invite",
133
- contactId: params.contactId,
134
- });
135
- } catch (err) {
136
- log.error(
137
- { err, sourceChannel: params.sourceChannel },
138
- "Local activation mirror failed after gateway activation; gateway outcome stands",
139
- );
140
- return null;
141
- }
142
- }
143
-
144
- // ── Revoke ───────────────────────────────────────────────────────────
145
-
146
- /**
147
- * Revoke a member channel gateway-first, then mirror the downgrade to the
148
- * assistant DB best-effort. The memberId may be a plain channel ID or the
149
- * composite contactId:channelId form revokeMember accepts.
150
- *
151
- * Returns the local ContactWriteResult so callers still get the native
152
- * contact/channel, or null when the local mirror produces no result.
153
- */
154
- export async function revokeMemberChannel(
155
- memberId: string,
156
- reason?: string,
157
- ): Promise<ContactWriteResult | null> {
158
- const channelId = memberId.includes(":") ? memberId.split(":")[1] : memberId;
159
-
160
- // Always relay; the gateway owns the ACL outcome and mark_channel_revoked is
161
- // idempotent (already-revoked → didWrite:false). Skipping on the local mirror
162
- // status would suppress a needed revoke when the mirror lags the gateway.
163
- const result = await ipcCallPersistent("mark_channel_revoked", {
164
- contactChannelId: channelId,
165
- reason,
166
- });
167
- const parsed = MarkChannelRevokedIpcResponseSchema.parse(result);
168
- if (!parsed.ok) {
169
- throw new Error("mark_channel_revoked relay returned ok: false");
170
- }
171
-
172
- return mirrorLocalRevoke(memberId, reason);
173
- }
174
-
175
- /** Best-effort local mirror of the revoke. Swallows failures. */
176
- function mirrorLocalRevoke(
177
- memberId: string,
178
- reason?: string,
179
- ): ContactWriteResult | null {
180
- try {
181
- return revokeMember(memberId, reason);
182
- } catch (err) {
183
- log.error(
184
- { err, memberId },
185
- "Local revoke mirror failed after gateway revoke; gateway downgrade stands",
186
- );
187
- return null;
188
- }
189
- }
@@ -1,60 +0,0 @@
1
- import type { ConversationNoticeEvent } from "../api/events/conversation-notice.js";
2
-
3
- export type PendingConversationNotice = Omit<
4
- ConversationNoticeEvent,
5
- "type" | "conversationId"
6
- >;
7
-
8
- const MAX_TRACKED_CONVERSATIONS = 256;
9
-
10
- const pendingNotices = new Map<
11
- string,
12
- Map<string, PendingConversationNotice>
13
- >();
14
-
15
- function touchConversation(
16
- conversationId: string,
17
- ): Map<string, PendingConversationNotice> {
18
- const existing = pendingNotices.get(conversationId);
19
- if (existing) {
20
- pendingNotices.delete(conversationId);
21
- pendingNotices.set(conversationId, existing);
22
- return existing;
23
- }
24
- if (pendingNotices.size >= MAX_TRACKED_CONVERSATIONS) {
25
- const oldest = pendingNotices.keys().next().value;
26
- if (oldest !== undefined) pendingNotices.delete(oldest);
27
- }
28
- const next = new Map<string, PendingConversationNotice>();
29
- pendingNotices.set(conversationId, next);
30
- return next;
31
- }
32
-
33
- export function queueConversationNotice(
34
- conversationId: string,
35
- key: string,
36
- notice: PendingConversationNotice,
37
- ): void {
38
- touchConversation(conversationId).set(key, notice);
39
- }
40
-
41
- export function drainConversationNotices(
42
- conversationId: string,
43
- ): ConversationNoticeEvent[] {
44
- const notices = pendingNotices.get(conversationId);
45
- if (!notices) return [];
46
- pendingNotices.delete(conversationId);
47
- return Array.from(notices.values(), (notice) => ({
48
- type: "conversation_notice" as const,
49
- conversationId,
50
- ...notice,
51
- }));
52
- }
53
-
54
- export function clearConversationNotices(conversationId: string): void {
55
- pendingNotices.delete(conversationId);
56
- }
57
-
58
- export function resetConversationNoticesForTests(): void {
59
- pendingNotices.clear();
60
- }
@@ -1,225 +0,0 @@
1
- import { beforeEach, describe, expect, mock, test } from "bun:test";
2
-
3
- import type { GuardianDelivery } from "@vellumai/gateway-client";
4
-
5
- import type { ContactChannel } from "../../../contacts/types.js";
6
-
7
- let mockGuardians: GuardianDelivery[] | null = null;
8
- let mockBinding: { guardianExternalUserId: string; guardianDeliveryChatId: string } | null = null;
9
- let mockContactChannel: { channel: ContactChannel } | null = null;
10
- let mockChannel: ContactChannel | null = null;
11
- let mockGwContactChannels: Array<{ id: string; status: string; verifiedAt: number | null }> | null =
12
- null;
13
- let ipcCalls: Array<{ method: string; payload: unknown }> = [];
14
-
15
- mock.module("../../../contacts/guardian-delivery-reader.js", () => ({
16
- getGuardianDelivery: async (input?: { channelTypes?: string[] }) => {
17
- if (mockGuardians == null) return null;
18
- if (!input?.channelTypes) return mockGuardians;
19
- return mockGuardians.filter((g) =>
20
- input.channelTypes!.includes(g.channelType),
21
- );
22
- },
23
- }));
24
-
25
- mock.module("../../../contacts/contact-store.js", () => ({
26
- findContactChannel: () => mockContactChannel,
27
- findGuardianForChannel: () => null,
28
- getChannelById: () => mockChannel,
29
- getContact: () => ({ id: "contact-1", displayName: "Pat" }),
30
- }));
31
-
32
- mock.module("../../../contacts/contact-events.js", () => ({
33
- emitContactChange: () => {},
34
- onContactChange: () => {},
35
- }));
36
-
37
- mock.module("../../../ipc/gateway-client.js", () => ({
38
- ipcCallPersistent: async (method: string, payload: unknown) => {
39
- ipcCalls.push({ method, payload });
40
- if (method === "contacts_get_rich") {
41
- if (mockGwContactChannels == null) return { ok: true, contact: null };
42
- return {
43
- ok: true,
44
- contact: {
45
- id: "contact-1",
46
- displayName: "Pat",
47
- role: "contact",
48
- interactionCount: 0,
49
- createdAt: 0,
50
- updatedAt: 0,
51
- channels: mockGwContactChannels.map((c) => ({
52
- id: c.id,
53
- contactId: "contact-1",
54
- type: "telegram",
55
- address: "user-123",
56
- isPrimary: true,
57
- externalUserId: null,
58
- status: c.status,
59
- policy: "allow",
60
- verifiedAt: c.verifiedAt,
61
- verifiedVia: null,
62
- lastSeenAt: null,
63
- interactionCount: 0,
64
- lastInteraction: null,
65
- revokedReason: null,
66
- blockedReason: null,
67
- })),
68
- },
69
- };
70
- }
71
- return {
72
- ok: true,
73
- didWrite: true,
74
- channel: {
75
- id: "ch-1",
76
- contactId: "contact-1",
77
- type: "telegram",
78
- address: "user-123",
79
- status: "revoked",
80
- revokedReason: "guardian_binding_revoked",
81
- },
82
- };
83
- },
84
- ipcCall: async () => null,
85
- }));
86
-
87
- mock.module("../../../runtime/channel-verification-service.js", () => ({
88
- getGuardianBinding: () => mockBinding,
89
- revokeBinding: () => true,
90
- revokePendingSessions: () => {},
91
- createOutboundSession: () => ({
92
- sessionId: "sess",
93
- secret: "code",
94
- expiresAt: Date.now() + 1000,
95
- }),
96
- countRecentSendsToDestination: () => 0,
97
- isGuardianBoundForChannel: async () => false,
98
- updateSessionDelivery: () => {},
99
- }));
100
-
101
- mock.module("../../../runtime/verification-outbound-actions.js", () => ({
102
- cancelOutbound: () => {},
103
- deliverVerificationEmail: () => {},
104
- deliverVerificationSlack: () => {},
105
- deliverVerificationTelegram: () => {},
106
- DESTINATION_RATE_WINDOW_MS: 1000,
107
- MAX_SENDS_PER_DESTINATION_WINDOW: 5,
108
- normalizeTelegramDestination: (d: string) => d,
109
- resendOutbound: () => ({}),
110
- startOutbound: async () => ({}),
111
- }));
112
-
113
- import {
114
- revokeVerificationForChannel,
115
- verifyTrustedContact,
116
- } from "../config-channels.js";
117
-
118
- function channel(overrides: Partial<ContactChannel> = {}): ContactChannel {
119
- return {
120
- id: "ch-1",
121
- contactId: "contact-1",
122
- type: "telegram",
123
- address: "user-123",
124
- isPrimary: true,
125
- externalChatId: "chat-123",
126
- // DB columns are intentionally a terminal state to prove the gates ignore
127
- // them and read from the gateway delivery instead.
128
- status: "revoked",
129
- policy: {} as ContactChannel["policy"],
130
- verifiedAt: null,
131
- verifiedVia: null,
132
- inviteId: null,
133
- revokedReason: null,
134
- blockedReason: null,
135
- lastSeenAt: null,
136
- interactionCount: 0,
137
- lastInteraction: null,
138
- updatedAt: null,
139
- createdAt: 0,
140
- ...overrides,
141
- };
142
- }
143
-
144
- function delivery(overrides: Partial<GuardianDelivery> = {}): GuardianDelivery {
145
- return {
146
- channelType: "telegram",
147
- contactId: "contact-1",
148
- address: "user-123",
149
- externalChatId: "chat-123",
150
- status: "active",
151
- verifiedAt: 1700000000,
152
- ...overrides,
153
- };
154
- }
155
-
156
- describe("revokeVerificationForChannel", () => {
157
- beforeEach(() => {
158
- mockGuardians = null;
159
- mockBinding = { guardianExternalUserId: "user-123", guardianDeliveryChatId: "chat-123" };
160
- mockContactChannel = { channel: channel() };
161
- ipcCalls = [];
162
- });
163
-
164
- test("relays mark_channel_revoked when the gateway delivery is live", async () => {
165
- mockGuardians = [delivery({ status: "active" })];
166
- await revokeVerificationForChannel("telegram");
167
- expect(ipcCalls.map((c) => c.method)).toContain("mark_channel_revoked");
168
- });
169
-
170
- test("skips a redundant revoke when the gateway delivery is already revoked", async () => {
171
- // Local DB status is the live "active" here, but the gateway (SoT) says
172
- // revoked — the gate must follow the gateway and not relay.
173
- mockContactChannel = { channel: channel({ status: "active" }) };
174
- mockGuardians = [delivery({ status: "revoked" })];
175
- await revokeVerificationForChannel("telegram");
176
- expect(ipcCalls.map((c) => c.method)).not.toContain("mark_channel_revoked");
177
- });
178
-
179
- test("skips the relay when the gateway has no delivery for the channel", async () => {
180
- mockGuardians = [];
181
- await revokeVerificationForChannel("telegram");
182
- expect(ipcCalls.map((c) => c.method)).not.toContain("mark_channel_revoked");
183
- });
184
-
185
- test("skips the relay when the gateway is unreachable", async () => {
186
- mockGuardians = null;
187
- await revokeVerificationForChannel("telegram");
188
- expect(ipcCalls.map((c) => c.method)).not.toContain("mark_channel_revoked");
189
- });
190
- });
191
-
192
- describe("verifyTrustedContact already-verified gate", () => {
193
- beforeEach(() => {
194
- mockGuardians = null;
195
- mockGwContactChannels = null;
196
- mockChannel = channel();
197
- });
198
-
199
- test("short-circuits when the gateway contact channel is active and verified", async () => {
200
- // Arbitrary trusted contact (non-guardian) — read from the contact-channel
201
- // gateway read, which covers all contacts.
202
- mockGwContactChannels = [
203
- { id: "ch-1", status: "active", verifiedAt: 1700000000 },
204
- ];
205
- const result = await verifyTrustedContact("ch-1", "assistant-1");
206
- expect(result.success).toBe(false);
207
- expect(result.error).toBe("already_verified");
208
- });
209
-
210
- test("does not short-circuit when the gateway channel has no verifiedAt", async () => {
211
- // DB column says verified, but the gateway channel is unverified — proceed.
212
- mockChannel = channel({ status: "active", verifiedAt: 1700000000 });
213
- mockGwContactChannels = [
214
- { id: "ch-1", status: "pending", verifiedAt: null },
215
- ];
216
- const result = await verifyTrustedContact("ch-1", "assistant-1");
217
- expect(result.error).not.toBe("already_verified");
218
- });
219
-
220
- test("does not short-circuit when the gateway has no matching channel", async () => {
221
- mockGwContactChannels = [];
222
- const result = await verifyTrustedContact("ch-1", "assistant-1");
223
- expect(result.error).not.toBe("already_verified");
224
- });
225
- });