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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (546) hide show
  1. package/bun.lock +20 -0
  2. package/docs/workspace-tools.md +42 -33
  3. package/eslint-rules/cli-no-daemon-internals.js +6 -0
  4. package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
  5. package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
  6. package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +44 -0
  7. package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
  8. package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
  9. package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
  10. package/node_modules/@vellumai/service-contracts/package.json +1 -0
  11. package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
  12. package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
  13. package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
  14. package/openapi.yaml +196 -56
  15. package/package.json +4 -1
  16. package/scripts/test.sh +36 -15
  17. package/src/__tests__/actor-token-service.test.ts +36 -14
  18. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
  19. package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  21. package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
  22. package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
  23. package/src/__tests__/annotate-risk-options.test.ts +2 -0
  24. package/src/__tests__/approval-cascade.test.ts +2 -0
  25. package/src/__tests__/assistant-attachments.test.ts +42 -0
  26. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
  27. package/src/__tests__/btw-routes.test.ts +2 -0
  28. package/src/__tests__/build-persisted-content.test.ts +2 -0
  29. package/src/__tests__/call-controller.test.ts +19 -0
  30. package/src/__tests__/channel-approval-routes.test.ts +21 -26
  31. package/src/__tests__/channel-delivery-store.test.ts +28 -0
  32. package/src/__tests__/channel-guardian.test.ts +164 -78
  33. package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
  34. package/src/__tests__/channel-reply-delivery.test.ts +2 -0
  35. package/src/__tests__/code-search-tool.test.ts +585 -0
  36. package/src/__tests__/compaction-events.test.ts +2 -0
  37. package/src/__tests__/compaction-ledger-store.test.ts +128 -0
  38. package/src/__tests__/compaction.benchmark.test.ts +2 -0
  39. package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
  40. package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
  41. package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
  42. package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
  43. package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
  44. package/src/__tests__/config-loader-backfill.test.ts +123 -10
  45. package/src/__tests__/config-schema.test.ts +1 -0
  46. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
  47. package/src/__tests__/consult-deadline.test.ts +60 -0
  48. package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
  49. package/src/__tests__/contact-store-user-file.test.ts +7 -10
  50. package/src/__tests__/contacts-relay-reads.test.ts +19 -24
  51. package/src/__tests__/contacts-write.test.ts +0 -2
  52. package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
  53. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
  54. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
  55. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
  56. package/src/__tests__/conversation-agent-loop.test.ts +134 -0
  57. package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
  58. package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
  59. package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
  60. package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
  61. package/src/__tests__/conversation-fork-crud.test.ts +354 -24
  62. package/src/__tests__/conversation-history-web-search.test.ts +2 -0
  63. package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
  64. package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
  65. package/src/__tests__/conversation-pairing.test.ts +2 -0
  66. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
  67. package/src/__tests__/conversation-process-callsite.test.ts +2 -0
  68. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
  69. package/src/__tests__/conversation-queue.test.ts +91 -0
  70. package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
  71. package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
  72. package/src/__tests__/conversation-slash-queue.test.ts +2 -0
  73. package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
  74. package/src/__tests__/conversation-speed-override.test.ts +2 -0
  75. package/src/__tests__/conversation-surfaces-task-progress.test.ts +29 -0
  76. package/src/__tests__/conversation-title-service.test.ts +2 -0
  77. package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
  78. package/src/__tests__/conversation-usage.test.ts +2 -0
  79. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
  80. package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
  81. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
  82. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  83. package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
  84. package/src/__tests__/db-migration-rollback.test.ts +205 -171
  85. package/src/__tests__/db-test-helpers.ts +5 -4
  86. package/src/__tests__/delete-propagation.test.ts +5 -3
  87. package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
  88. package/src/__tests__/disk-pressure-guard.test.ts +41 -0
  89. package/src/__tests__/dm-backfill.test.ts +6 -4
  90. package/src/__tests__/dm-persistence.test.ts +2 -0
  91. package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
  92. package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
  93. package/src/__tests__/exploration-drift-hook.test.ts +3 -2
  94. package/src/__tests__/filing-service.test.ts +2 -0
  95. package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
  96. package/src/__tests__/guardian-dispatch.test.ts +140 -1
  97. package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
  98. package/src/__tests__/guardian-outbound-http.test.ts +13 -0
  99. package/src/__tests__/guardian-routing-state.test.ts +6 -10
  100. package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
  101. package/src/__tests__/heartbeat-service.test.ts +2 -0
  102. package/src/__tests__/helpers/channel-test-adapter.ts +46 -19
  103. package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
  104. package/src/__tests__/helpers/mock-logger.ts +1 -0
  105. package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
  106. package/src/__tests__/host-app-control-routes.test.ts +24 -30
  107. package/src/__tests__/host-bash-routes.test.ts +31 -41
  108. package/src/__tests__/host-browser-routes.test.ts +26 -32
  109. package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
  110. package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
  111. package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
  112. package/src/__tests__/http-user-message-parity.test.ts +290 -8
  113. package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
  114. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  115. package/src/__tests__/invite-redemption-service.test.ts +471 -53
  116. package/src/__tests__/invite-routes-http.test.ts +34 -0
  117. package/src/__tests__/invite-service-ipc.test.ts +65 -2
  118. package/src/__tests__/llm-context-normalization.test.ts +105 -0
  119. package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
  120. package/src/__tests__/llm-usage-store.test.ts +25 -0
  121. package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
  122. package/src/__tests__/mcp-health-check.test.ts +2 -1
  123. package/src/__tests__/media-stream-server-integration.test.ts +127 -0
  124. package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
  125. package/src/__tests__/messaging-send-tool.test.ts +2 -0
  126. package/src/__tests__/migration-import-from-url.test.ts +2 -2
  127. package/src/__tests__/mtime-cache.test.ts +275 -5
  128. package/src/__tests__/native-web-search.test.ts +2 -0
  129. package/src/__tests__/non-member-access-request.test.ts +191 -17
  130. package/src/__tests__/notification-broadcaster.test.ts +4 -0
  131. package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
  132. package/src/__tests__/notification-deep-link.test.ts +6 -0
  133. package/src/__tests__/notification-guardian-path.test.ts +19 -0
  134. package/src/__tests__/onboarding-persona-write.test.ts +52 -22
  135. package/src/__tests__/openai-provider.test.ts +22 -12
  136. package/src/__tests__/openai-responses-provider.test.ts +12 -2
  137. package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
  138. package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
  139. package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
  140. package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
  141. package/src/__tests__/persona-resolver.test.ts +75 -45
  142. package/src/__tests__/plugin-bootstrap.test.ts +21 -83
  143. package/src/__tests__/plugin-disabled-state.test.ts +190 -0
  144. package/src/__tests__/plugin-pipeline.test.ts +96 -0
  145. package/src/__tests__/plugin-route-contribution.test.ts +6 -19
  146. package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
  147. package/src/__tests__/plugin-types.test.ts +2 -4
  148. package/src/__tests__/process-message-background-slack.test.ts +2 -0
  149. package/src/__tests__/process-message-display-content.test.ts +2 -0
  150. package/src/__tests__/provider-error-scenarios.test.ts +5 -4
  151. package/src/__tests__/provider-usage-tracking.test.ts +39 -0
  152. package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
  153. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
  154. package/src/__tests__/reaction-persistence.test.ts +51 -4
  155. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
  156. package/src/__tests__/registry.test.ts +4 -1
  157. package/src/__tests__/relay-server.test.ts +758 -32
  158. package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
  159. package/src/__tests__/secret-ingress-http.test.ts +14 -0
  160. package/src/__tests__/send-endpoint-busy.test.ts +30 -8
  161. package/src/__tests__/settings-routes.test.ts +32 -0
  162. package/src/__tests__/skills.test.ts +44 -0
  163. package/src/__tests__/slack-inbound-verification.test.ts +47 -2
  164. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +79 -0
  165. package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
  166. package/src/__tests__/stt-hints.test.ts +49 -15
  167. package/src/__tests__/subagent-detail.test.ts +27 -0
  168. package/src/__tests__/subagent-disposal.test.ts +65 -0
  169. package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
  170. package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
  171. package/src/__tests__/subagent-notify-parent.test.ts +2 -0
  172. package/src/__tests__/subagent-role-registry.test.ts +24 -6
  173. package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
  174. package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
  175. package/src/__tests__/subagent-tools.test.ts +398 -1
  176. package/src/__tests__/suggestion-routes.test.ts +2 -0
  177. package/src/__tests__/thread-backfill.test.ts +3 -3
  178. package/src/__tests__/title-generate-hook.test.ts +2 -0
  179. package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
  180. package/src/__tests__/tool-executor.test.ts +16 -11
  181. package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
  182. package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
  183. package/src/__tests__/tool-start-timestamp.test.ts +2 -0
  184. package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
  185. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
  186. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
  187. package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
  188. package/src/__tests__/trusted-contact-verification.test.ts +79 -54
  189. package/src/__tests__/twilio-routes.test.ts +96 -0
  190. package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
  191. package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
  192. package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
  193. package/src/__tests__/voice-invite-redemption.test.ts +216 -20
  194. package/src/__tests__/web-search-backend-failure.test.ts +2 -0
  195. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
  196. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
  197. package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
  198. package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
  199. package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
  200. package/src/__tests__/workspace-tool-loader.test.ts +195 -2
  201. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
  202. package/src/agent/loop-exclusive-tool.test.ts +154 -0
  203. package/src/agent/loop-native-web-search.test.ts +200 -0
  204. package/src/agent/loop.ts +164 -1
  205. package/src/api/constants/sse-replay.ts +41 -0
  206. package/src/api/events/conversation-notice.ts +26 -0
  207. package/src/api/index.ts +19 -1
  208. package/src/api/responses/llm-request-log-entry.ts +29 -0
  209. package/src/api/responses/subagent-detail.ts +17 -0
  210. package/src/api/surfaces.ts +39 -3
  211. package/src/approvals/guardian-channel-delivery.ts +30 -0
  212. package/src/approvals/guardian-expiry-notifier.ts +148 -0
  213. package/src/approvals/guardian-request-resolvers.ts +17 -15
  214. package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
  215. package/src/calls/call-domain.ts +3 -3
  216. package/src/calls/guardian-dispatch.ts +14 -9
  217. package/src/calls/inbound-trust-reader.ts +24 -2
  218. package/src/calls/media-stream-server.ts +21 -0
  219. package/src/calls/relay-access-wait.ts +6 -6
  220. package/src/calls/relay-server.ts +188 -51
  221. package/src/calls/relay-setup-router.ts +47 -17
  222. package/src/calls/relay-verification.ts +4 -4
  223. package/src/calls/stt-hints.ts +9 -12
  224. package/src/calls/twilio-routes.ts +14 -4
  225. package/src/channels/types.ts +10 -20
  226. package/src/cli/commands/__tests__/cache.test.ts +8 -1
  227. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
  228. package/src/cli/commands/cache.ts +194 -181
  229. package/src/cli/commands/contacts.ts +10 -7
  230. package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
  231. package/src/cli/commands/db/status.ts +37 -1
  232. package/src/cli/commands/mcp.ts +252 -218
  233. package/src/cli/commands/memory/__tests__/worker.test.ts +432 -0
  234. package/src/cli/commands/memory/index.ts +2 -0
  235. package/src/cli/commands/memory/worker.ts +242 -0
  236. package/src/cli/commands/plugins.ts +125 -13
  237. package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
  238. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
  239. package/src/cli/lib/__tests__/publish-plugin.test.ts +404 -0
  240. package/src/cli/lib/list-installed-plugins.ts +179 -1
  241. package/src/cli/lib/publish-plugin.ts +633 -0
  242. package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
  243. package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
  244. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
  245. package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
  246. package/src/config/bundled-skills/subagent/SKILL.md +17 -2
  247. package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
  248. package/src/config/call-site-defaults.ts +0 -6
  249. package/src/config/feature-flag-registry.json +5 -13
  250. package/src/config/llm-resolver.ts +0 -3
  251. package/src/config/loader.ts +38 -5
  252. package/src/config/prune-seeded-callsite-defaults.ts +110 -0
  253. package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
  254. package/src/config/schemas/call-site-catalog.ts +0 -7
  255. package/src/config/schemas/heartbeat.ts +2 -5
  256. package/src/config/schemas/llm.ts +3 -12
  257. package/src/config/schemas/memory-lifecycle.ts +12 -0
  258. package/src/config/schemas/memory-v3.ts +7 -0
  259. package/src/config/schemas/memory.ts +4 -0
  260. package/src/config/schemas/timeouts.ts +8 -0
  261. package/src/config/seed-inference-profiles.ts +30 -34
  262. package/src/config/skills.ts +27 -5
  263. package/src/config/sync-gated-profiles.ts +12 -16
  264. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
  265. package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
  266. package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
  267. package/src/contacts/contact-store.ts +27 -237
  268. package/src/contacts/contacts-write.ts +18 -55
  269. package/src/contacts/gateway-channel-read.ts +51 -0
  270. package/src/contacts/guardian-delivery-reader.ts +223 -0
  271. package/src/contacts/member-write-relay.ts +183 -0
  272. package/src/contacts/types.ts +3 -15
  273. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
  274. package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
  275. package/src/daemon/assistant-attachments.ts +27 -4
  276. package/src/daemon/conversation-agent-loop.ts +28 -0
  277. package/src/daemon/conversation-notices.ts +60 -0
  278. package/src/daemon/conversation-process.ts +35 -16
  279. package/src/daemon/conversation-surfaces.ts +111 -38
  280. package/src/daemon/conversation-tool-setup.ts +71 -30
  281. package/src/daemon/conversation.ts +23 -1
  282. package/src/daemon/disk-pressure-guard.ts +12 -2
  283. package/src/daemon/event-loop-watchdog.ts +28 -1
  284. package/src/daemon/external-plugins-bootstrap.ts +17 -41
  285. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
  287. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
  288. package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
  289. package/src/daemon/handlers/config-a2a.ts +6 -14
  290. package/src/daemon/handlers/config-channels.ts +67 -26
  291. package/src/daemon/handlers/conversations.ts +77 -0
  292. package/src/daemon/lifecycle.ts +16 -0
  293. package/src/daemon/mcp-reload-service.ts +10 -0
  294. package/src/daemon/memory-v2-startup.test.ts +72 -0
  295. package/src/daemon/memory-v2-startup.ts +87 -19
  296. package/src/daemon/message-types/conversations.ts +2 -0
  297. package/src/daemon/message-types/surfaces.ts +12 -12
  298. package/src/daemon/server.ts +0 -4
  299. package/src/daemon/shutdown-handlers.ts +20 -0
  300. package/src/daemon/tool-setup-types.ts +9 -0
  301. package/src/heartbeat/heartbeat-service.ts +5 -0
  302. package/src/home/relationship-state-writer.ts +5 -0
  303. package/src/hooks/hook-loader.ts +341 -0
  304. package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
  305. package/src/ipc/assistant-server.ts +2 -2
  306. package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
  307. package/src/mcp/client.ts +15 -1
  308. package/src/mcp/mcp-auth-orchestrator.ts +6 -1
  309. package/src/mcp/mcp-header-store.ts +134 -0
  310. package/src/mcp/mcp-oauth-provider.ts +19 -8
  311. package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
  312. package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
  313. package/src/memory/__tests__/prompt-override.test.ts +192 -0
  314. package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
  315. package/src/memory/compaction-ledger-store.ts +107 -0
  316. package/src/memory/conversation-crud.ts +129 -61
  317. package/src/memory/db-connection.ts +22 -3
  318. package/src/memory/db-init.ts +37 -503
  319. package/src/memory/db-singleton.ts +6 -4
  320. package/src/memory/jobs-worker.ts +104 -0
  321. package/src/memory/llm-request-log-store.ts +26 -1
  322. package/src/memory/llm-usage-store.ts +48 -20
  323. package/src/memory/memory-retrospective-job.ts +14 -8
  324. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -56
  325. package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
  326. package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
  327. package/src/memory/migrations/302-create-compaction-events.ts +107 -0
  328. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
  329. package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
  330. package/src/memory/migrations/run-migrations.ts +106 -10
  331. package/src/memory/migrations/validate-migration-state.ts +105 -67
  332. package/src/memory/prompt-override.ts +129 -0
  333. package/src/memory/schema/contacts.ts +6 -2
  334. package/src/memory/schema/conversations.ts +37 -0
  335. package/src/memory/schema/infrastructure.ts +20 -0
  336. package/src/memory/steps.ts +1294 -0
  337. package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
  338. package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
  339. package/src/memory/v2/cli-command-store.ts +75 -38
  340. package/src/memory/v2/prompts/consolidation.ts +13 -82
  341. package/src/memory/v2/prompts/router.ts +21 -93
  342. package/src/memory/v2/skill-store.ts +68 -31
  343. package/src/memory/watchdog-events-store.ts +87 -0
  344. package/src/memory/worker-control.ts +204 -0
  345. package/src/memory/worker-process.ts +89 -0
  346. package/src/messaging/channel-binding-metadata.ts +31 -0
  347. package/src/messaging/provider-types.ts +8 -0
  348. package/src/messaging/providers/slack/binding-metadata.ts +60 -0
  349. package/src/notifications/__tests__/broadcaster.test.ts +8 -8
  350. package/src/notifications/__tests__/connected-channels.test.ts +86 -0
  351. package/src/notifications/__tests__/decision-engine.test.ts +78 -9
  352. package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
  353. package/src/notifications/broadcaster.ts +8 -1
  354. package/src/notifications/decision-engine.ts +15 -7
  355. package/src/notifications/destination-resolver.ts +53 -25
  356. package/src/notifications/emit-signal.ts +34 -15
  357. package/src/onboarding/checkin-event.test.ts +222 -0
  358. package/src/onboarding/checkin-event.ts +321 -0
  359. package/src/onboarding/schedule-checkin.ts +190 -0
  360. package/src/permissions/question-prompter.test.ts +1 -1
  361. package/src/permissions/question-prompter.ts +7 -4
  362. package/src/plugin-api/index.ts +12 -12
  363. package/src/plugin-api/types.ts +12 -14
  364. package/src/plugin-api/vision-support.test.ts +28 -4
  365. package/src/plugin-api/vision-support.ts +66 -31
  366. package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
  367. package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
  368. package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
  369. package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
  370. package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
  371. package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
  372. package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
  373. package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
  374. package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
  375. package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
  376. package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
  377. package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
  378. package/src/plugins/defaults/index.ts +0 -35
  379. package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
  380. package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
  381. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
  382. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
  383. package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
  384. package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
  385. package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
  386. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
  387. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
  388. package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
  389. package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
  390. package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
  391. package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +77 -13
  392. package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
  393. package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
  394. package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
  395. package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
  396. package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
  397. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
  398. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
  399. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
  400. package/src/plugins/disabled-state.ts +31 -0
  401. package/src/plugins/external-plugin-loader.ts +2 -2
  402. package/src/plugins/mtime-cache.ts +186 -330
  403. package/src/plugins/pipeline.ts +111 -13
  404. package/src/plugins/registry.ts +59 -16
  405. package/src/plugins/surface-import.ts +121 -0
  406. package/src/plugins/types.ts +7 -9
  407. package/src/prompts/persona-resolver.ts +43 -11
  408. package/src/providers/anthropic/client.ts +5 -0
  409. package/src/providers/call-site-routing.ts +45 -0
  410. package/src/providers/model-catalog.ts +16 -0
  411. package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
  412. package/src/providers/openai/api-error-normalization.ts +270 -0
  413. package/src/providers/openai/chat-completions-provider.ts +37 -83
  414. package/src/providers/openai/responses-provider.ts +50 -46
  415. package/src/providers/openrouter/client.ts +5 -0
  416. package/src/providers/provider-send-message.ts +10 -0
  417. package/src/providers/ratelimit.ts +10 -0
  418. package/src/providers/registry.ts +1 -1
  419. package/src/providers/retry.ts +10 -0
  420. package/src/providers/types.ts +22 -0
  421. package/src/providers/usage-tracking.ts +10 -0
  422. package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
  423. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
  424. package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
  425. package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
  426. package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
  427. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
  428. package/src/runtime/access-request-helper.ts +18 -39
  429. package/src/runtime/actor-trust-resolver.ts +46 -19
  430. package/src/runtime/anchored-guardian.test.ts +109 -0
  431. package/src/runtime/anchored-guardian.ts +86 -0
  432. package/src/runtime/assistant-event-hub.ts +1 -1
  433. package/src/runtime/assistant-stream-state.ts +9 -2
  434. package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
  435. package/src/runtime/auth/require-bound-guardian.ts +21 -11
  436. package/src/runtime/channel-verification-service.ts +56 -31
  437. package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
  438. package/src/runtime/guardian-vellum-migration.ts +69 -8
  439. package/src/runtime/invite-redemption-service.ts +213 -187
  440. package/src/runtime/local-actor-identity.test.ts +108 -0
  441. package/src/runtime/local-actor-identity.ts +85 -13
  442. package/src/runtime/local-principal-trust.ts +52 -0
  443. package/src/runtime/member-verdict-cache.ts +0 -0
  444. package/src/runtime/pending-interactions.ts +11 -1
  445. package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
  446. package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
  447. package/src/runtime/routes/__tests__/contact-routes.test.ts +305 -0
  448. package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
  449. package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
  450. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
  451. package/src/runtime/routes/browser-routes.ts +1 -1
  452. package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
  453. package/src/runtime/routes/channel-verification-routes.ts +3 -3
  454. package/src/runtime/routes/contact-routes.ts +48 -57
  455. package/src/runtime/routes/conversation-cli-routes.ts +4 -5
  456. package/src/runtime/routes/conversation-list-routes.ts +4 -7
  457. package/src/runtime/routes/conversation-query-routes.ts +72 -0
  458. package/src/runtime/routes/conversation-routes.ts +84 -85
  459. package/src/runtime/routes/events-routes.ts +2 -2
  460. package/src/runtime/routes/global-search-routes.ts +3 -1
  461. package/src/runtime/routes/guardian-action-routes.ts +4 -5
  462. package/src/runtime/routes/host-app-control-routes.ts +5 -4
  463. package/src/runtime/routes/host-bash-routes.ts +5 -4
  464. package/src/runtime/routes/host-browser-routes.ts +9 -11
  465. package/src/runtime/routes/host-cu-routes.ts +5 -4
  466. package/src/runtime/routes/host-file-routes.ts +5 -4
  467. package/src/runtime/routes/host-transfer-routes.ts +6 -6
  468. package/src/runtime/routes/http-adapter.ts +16 -1
  469. package/src/runtime/routes/identity-routes.ts +3 -2
  470. package/src/runtime/routes/inbound-message-handler.ts +5 -5
  471. package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
  472. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -59
  473. package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
  474. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
  475. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
  476. package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
  477. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
  478. package/src/runtime/routes/index.ts +2 -0
  479. package/src/runtime/routes/llm-context-normalization.ts +83 -0
  480. package/src/runtime/routes/mcp-auth-routes.ts +171 -19
  481. package/src/runtime/routes/migration-rollback-routes.ts +4 -3
  482. package/src/runtime/routes/migration-routes.ts +4 -1
  483. package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
  484. package/src/runtime/routes/settings-routes.ts +8 -3
  485. package/src/runtime/routes/subagents-routes.ts +5 -0
  486. package/src/runtime/routes/surface-action-routes.ts +42 -56
  487. package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
  488. package/src/runtime/services/conversation-serializer.ts +13 -58
  489. package/src/runtime/tool-grant-request-helper.ts +3 -3
  490. package/src/runtime/trust-verdict-consumer.ts +111 -40
  491. package/src/runtime/verification-outbound-actions.ts +18 -18
  492. package/src/signals/user-message.ts +16 -0
  493. package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
  494. package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
  495. package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
  496. package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
  497. package/src/subagent/index.ts +1 -1
  498. package/src/subagent/manager.ts +254 -33
  499. package/src/subagent/types.ts +11 -4
  500. package/src/telemetry/types.ts +34 -1
  501. package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
  502. package/src/telemetry/usage-telemetry-reporter.ts +87 -3
  503. package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
  504. package/src/tools/ask-question/ask-question-tool.ts +13 -0
  505. package/src/tools/executor.ts +4 -4
  506. package/src/tools/filesystem/search.ts +543 -0
  507. package/src/tools/registry.ts +38 -0
  508. package/src/tools/shared/filesystem/path-policy.ts +12 -5
  509. package/src/tools/subagent/consult-deadline.ts +49 -0
  510. package/src/tools/subagent/spawn.ts +234 -5
  511. package/src/tools/tool-approval-handler.ts +1 -1
  512. package/src/tools/tool-defaults.ts +9 -2
  513. package/src/tools/tool-manifest.ts +3 -0
  514. package/src/tools/types.ts +131 -25
  515. package/src/tools/workspace-tools/loader.ts +348 -244
  516. package/src/util/errors.ts +26 -1
  517. package/src/util/logger.ts +9 -0
  518. package/src/util/platform.ts +19 -0
  519. package/src/util/telemetry-db-path.ts +24 -0
  520. package/src/workflows/library.test.ts +140 -0
  521. package/src/workflows/library.ts +82 -28
  522. package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
  523. package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
  524. package/src/workspace/migrations/031-drop-user-md.ts +232 -148
  525. package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
  526. package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
  527. package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
  528. package/src/workspace/migrations/registry.ts +4 -0
  529. package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
  530. package/src/daemon/workspace-tools-watcher.ts +0 -328
  531. package/src/memory/migrations/registry.ts +0 -573
  532. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
  533. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
  534. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
  535. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
  536. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
  537. package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
  538. package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
  539. package/src/plugins/defaults/advisor/config.ts +0 -21
  540. package/src/plugins/defaults/advisor/consult.ts +0 -93
  541. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
  542. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
  543. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
  544. package/src/plugins/defaults/advisor/package.json +0 -14
  545. package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
  546. package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
@@ -65,22 +65,90 @@ mock.module("../runtime/assistant-event-hub.js", () => ({
65
65
 
66
66
  // Gateway relay mock — the revoke path relays the ACL downgrade over IPC and
67
67
  // validates the response; return a well-formed mark_channel_revoked result.
68
+ // The gateway owns the revoke and dual-writes the local assistant row to
69
+ // "revoked"; mirror that dual-write here so guardian-resolution reads under
70
+ // test observe the downgrade (the assistant-side teardown is now a no-op shim).
68
71
  mock.module("../ipc/gateway-client.js", () => ({
69
72
  ipcCallPersistent: async (
70
- _method: string,
73
+ method: string,
71
74
  params?: Record<string, unknown>,
72
- ) => ({
73
- ok: true,
74
- didWrite: true,
75
- channel: {
76
- id: (params?.contactChannelId as string) ?? "ch1",
77
- contactId: "c1",
78
- type: "phone",
79
- address: "addr",
80
- status: "revoked",
81
- revokedReason: (params?.reason as string) ?? null,
82
- },
83
- }),
75
+ ) => {
76
+ if (method === "mark_channel_revoked") {
77
+ const { getDb } = await import("../memory/db-connection.js");
78
+ const { contactChannels } = await import("../memory/schema.js");
79
+ const { eq } = await import("drizzle-orm");
80
+ const channelId = params?.contactChannelId as string | undefined;
81
+ if (channelId) {
82
+ getDb()
83
+ .update(contactChannels)
84
+ .set({ status: "revoked" })
85
+ .where(eq(contactChannels.id, channelId))
86
+ .run();
87
+ }
88
+ }
89
+ return {
90
+ ok: true,
91
+ didWrite: true,
92
+ channel: {
93
+ id: (params?.contactChannelId as string) ?? "ch1",
94
+ contactId: "c1",
95
+ type: "phone",
96
+ address: "addr",
97
+ status: "revoked",
98
+ revokedReason: (params?.reason as string) ?? null,
99
+ },
100
+ };
101
+ },
102
+ }));
103
+
104
+ // Guardian-delivery reader mock — the inbound challenge guard reads guardian
105
+ // existence from the gateway. Derive the list from the local binding state so
106
+ // the gateway-backed presence guard mirrors the DB the rest of the test sets up.
107
+ const resolveGuardianList = async (input?: { channelTypes?: string[] }) => {
108
+ const { getDb } = await import("../memory/db-connection.js");
109
+ const { contacts, contactChannels } = await import("../memory/schema.js");
110
+ const { and, eq } = await import("drizzle-orm");
111
+ const channels = input?.channelTypes ?? [];
112
+ return channels
113
+ .map((channelType) => {
114
+ const row = getDb()
115
+ .select({ contact: contacts, channel: contactChannels })
116
+ .from(contacts)
117
+ .innerJoin(
118
+ contactChannels,
119
+ eq(contacts.id, contactChannels.contactId),
120
+ )
121
+ .where(
122
+ and(
123
+ eq(contacts.role, "guardian"),
124
+ eq(contactChannels.type, channelType),
125
+ eq(contactChannels.status, "active"),
126
+ ),
127
+ )
128
+ .get();
129
+ if (!row) return null;
130
+ return {
131
+ channelType,
132
+ contactId: row.contact.id,
133
+ principalId: row.contact.principalId ?? null,
134
+ displayName: row.contact.displayName ?? null,
135
+ address: row.channel.address,
136
+ externalChatId: row.channel.externalChatId ?? null,
137
+ status: "active",
138
+ verifiedAt: row.channel.verifiedAt ?? null,
139
+ };
140
+ })
141
+ .filter((g) => g !== null);
142
+ };
143
+
144
+ mock.module("../contacts/guardian-delivery-reader.js", () => ({
145
+ getGuardianDelivery: resolveGuardianList,
146
+ getGuardianDeliveryFresh: resolveGuardianList,
147
+ guardianForChannel: (
148
+ list: Array<{ channelType: string; status: string }>,
149
+ channelType: string,
150
+ ) =>
151
+ list.find((g) => g.channelType === channelType && g.status === "active"),
84
152
  }));
85
153
 
86
154
  import { handleChannelVerificationSession } from "../daemon/handlers/config-channels.js";
@@ -111,6 +179,7 @@ import {
111
179
  } from "../memory/guardian-rate-limits.js";
112
180
  import {
113
181
  channelVerificationSessions,
182
+ contactChannels,
114
183
  conversations,
115
184
  } from "../memory/schema.js";
116
185
  import {
@@ -156,6 +225,19 @@ function resetTables(): void {
156
225
  mockBotUsername = "test_bot";
157
226
  }
158
227
 
228
+ /**
229
+ * Revoke a guardian channel's local ACL state directly. The production revoke
230
+ * is gateway-owned (relayed via mark_channel_revoked); this stamps the local
231
+ * mirror so the guardian-resolution reads still under test see the downgrade.
232
+ */
233
+ function revokeGuardianChannelLocally(channelType: string): void {
234
+ getDb()
235
+ .update(contactChannels)
236
+ .set({ status: "revoked" })
237
+ .where(eq(contactChannels.type, channelType))
238
+ .run();
239
+ }
240
+
159
241
  // ═══════════════════════════════════════════════════════════════════════════
160
242
  // 2. Verification Challenge Lifecycle (Store)
161
243
  // ═══════════════════════════════════════════════════════════════════════════
@@ -319,12 +401,12 @@ describe("guardian service challenge validation", () => {
319
401
  }
320
402
  });
321
403
 
322
- test("validateAndConsumeVerification does not create a guardian binding (caller responsibility)", () => {
404
+ test("validateAndConsumeVerification does not create a guardian binding (caller responsibility)", async () => {
323
405
  const { secret } = createInboundVerificationSession("telegram");
324
406
 
325
407
  validateAndConsumeVerification("telegram", secret, "user-42", "chat-42");
326
408
 
327
- const binding = getGuardianBinding("asst-1", "telegram");
409
+ const binding = await getGuardianBinding("asst-1", "telegram");
328
410
  expect(binding).toBeNull();
329
411
  });
330
412
 
@@ -396,7 +478,7 @@ describe("guardian service challenge validation", () => {
396
478
  expect(result2.success).toBe(false);
397
479
  });
398
480
 
399
- test("validateAndConsumeVerification succeeds with voice channel", () => {
481
+ test("validateAndConsumeVerification succeeds with voice channel", async () => {
400
482
  const { secret } = createInboundVerificationSession("phone");
401
483
 
402
484
  const result = validateAndConsumeVerification(
@@ -413,7 +495,7 @@ describe("guardian service challenge validation", () => {
413
495
 
414
496
  // validateAndConsumeVerification no longer creates bindings — that is
415
497
  // now handled by the gateway's verification intercepts.
416
- const binding = getGuardianBinding("asst-1", "phone");
498
+ const binding = await getGuardianBinding("asst-1", "phone");
417
499
  expect(binding).toBeNull();
418
500
  });
419
501
 
@@ -449,7 +531,7 @@ describe("guardian service challenge validation", () => {
449
531
  expect(telegramResult.success).toBe(true);
450
532
  });
451
533
 
452
- test("validateAndConsumeVerification succeeds even with existing binding (conflict check is caller responsibility)", () => {
534
+ test("validateAndConsumeVerification succeeds even with existing binding (conflict check is caller responsibility)", async () => {
453
535
  // Create initial guardian binding
454
536
  createGuardianBinding({
455
537
  channel: "telegram",
@@ -458,7 +540,7 @@ describe("guardian service challenge validation", () => {
458
540
  guardianDeliveryChatId: "old-chat",
459
541
  });
460
542
 
461
- const oldBinding = getGuardianBinding("asst-1", "telegram");
543
+ const oldBinding = await getGuardianBinding("asst-1", "telegram");
462
544
  expect(oldBinding).not.toBeNull();
463
545
  expect(oldBinding!.guardianExternalUserId).toBe("old-user");
464
546
 
@@ -473,7 +555,7 @@ describe("guardian service challenge validation", () => {
473
555
  // Challenge validation succeeds — the caller decides how to handle binding conflicts
474
556
  expect(result.success).toBe(true);
475
557
 
476
- const binding = getGuardianBinding("asst-1", "telegram");
558
+ const binding = await getGuardianBinding("asst-1", "telegram");
477
559
  expect(binding).not.toBeNull();
478
560
  expect(binding!.guardianExternalUserId).toBe("old-user");
479
561
  });
@@ -488,7 +570,7 @@ describe("guardian identity check", () => {
488
570
  resetTables();
489
571
  });
490
572
 
491
- test("isGuardian returns true for matching user", () => {
573
+ test("isGuardian returns true for matching user", async () => {
492
574
  createGuardianBinding({
493
575
  channel: "telegram",
494
576
  guardianExternalUserId: "user-42",
@@ -496,10 +578,10 @@ describe("guardian identity check", () => {
496
578
  guardianDeliveryChatId: "chat-42",
497
579
  });
498
580
 
499
- expect(isGuardian("asst-1", "telegram", "user-42")).toBe(true);
581
+ expect(await isGuardian("asst-1", "telegram", "user-42")).toBe(true);
500
582
  });
501
583
 
502
- test("isGuardian returns false for non-matching user", () => {
584
+ test("isGuardian returns false for non-matching user", async () => {
503
585
  createGuardianBinding({
504
586
  channel: "telegram",
505
587
  guardianExternalUserId: "user-42",
@@ -507,14 +589,14 @@ describe("guardian identity check", () => {
507
589
  guardianDeliveryChatId: "chat-42",
508
590
  });
509
591
 
510
- expect(isGuardian("asst-1", "telegram", "user-99")).toBe(false);
592
+ expect(await isGuardian("asst-1", "telegram", "user-99")).toBe(false);
511
593
  });
512
594
 
513
- test("isGuardian returns false when no binding exists", () => {
514
- expect(isGuardian("asst-1", "telegram", "user-42")).toBe(false);
595
+ test("isGuardian returns false when no binding exists", async () => {
596
+ expect(await isGuardian("asst-1", "telegram", "user-42")).toBe(false);
515
597
  });
516
598
 
517
- test("isGuardian returns false after binding is revoked", () => {
599
+ test("isGuardian returns false after binding is revoked", async () => {
518
600
  createGuardianBinding({
519
601
  channel: "telegram",
520
602
  guardianExternalUserId: "user-42",
@@ -522,12 +604,12 @@ describe("guardian identity check", () => {
522
604
  guardianDeliveryChatId: "chat-42",
523
605
  });
524
606
 
525
- serviceRevokeBinding("asst-1", "telegram");
607
+ revokeGuardianChannelLocally("telegram");
526
608
 
527
- expect(isGuardian("asst-1", "telegram", "user-42")).toBe(false);
609
+ expect(await isGuardian("asst-1", "telegram", "user-42")).toBe(false);
528
610
  });
529
611
 
530
- test("getGuardianBinding returns the active binding", () => {
612
+ test("getGuardianBinding returns the active binding", async () => {
531
613
  createGuardianBinding({
532
614
  channel: "telegram",
533
615
  guardianExternalUserId: "user-42",
@@ -535,17 +617,17 @@ describe("guardian identity check", () => {
535
617
  guardianDeliveryChatId: "chat-42",
536
618
  });
537
619
 
538
- const binding = getGuardianBinding("asst-1", "telegram");
620
+ const binding = await getGuardianBinding("asst-1", "telegram");
539
621
  expect(binding).not.toBeNull();
540
622
  expect(binding!.guardianExternalUserId).toBe("user-42");
541
623
  });
542
624
 
543
- test("getGuardianBinding returns null when no binding exists", () => {
544
- const binding = getGuardianBinding("asst-1", "telegram");
625
+ test("getGuardianBinding returns null when no binding exists", async () => {
626
+ const binding = await getGuardianBinding("asst-1", "telegram");
545
627
  expect(binding).toBeNull();
546
628
  });
547
629
 
548
- test("isGuardian works for voice channel", () => {
630
+ test("isGuardian works for voice channel", async () => {
549
631
  createGuardianBinding({
550
632
  channel: "phone",
551
633
  guardianExternalUserId: "phone-user-1",
@@ -553,13 +635,13 @@ describe("guardian identity check", () => {
553
635
  guardianDeliveryChatId: "voice-chat-1",
554
636
  });
555
637
 
556
- expect(isGuardian("asst-1", "phone", "phone-user-1")).toBe(true);
557
- expect(isGuardian("asst-1", "phone", "phone-user-2")).toBe(false);
638
+ expect(await isGuardian("asst-1", "phone", "phone-user-1")).toBe(true);
639
+ expect(await isGuardian("asst-1", "phone", "phone-user-2")).toBe(false);
558
640
  // Telegram guardian should not match voice channel
559
- expect(isGuardian("asst-1", "telegram", "phone-user-1")).toBe(false);
641
+ expect(await isGuardian("asst-1", "telegram", "phone-user-1")).toBe(false);
560
642
  });
561
643
 
562
- test("serviceRevokeBinding revokes the active binding", () => {
644
+ test("guardian binding read reflects a gateway-owned revoke", async () => {
563
645
  createGuardianBinding({
564
646
  channel: "telegram",
565
647
  guardianExternalUserId: "user-42",
@@ -567,9 +649,11 @@ describe("guardian identity check", () => {
567
649
  guardianDeliveryChatId: "chat-42",
568
650
  });
569
651
 
570
- const result = serviceRevokeBinding("asst-1", "telegram");
571
- expect(result).toBe(true);
572
- expect(getGuardianBinding("asst-1", "telegram")).toBeNull();
652
+ // The revoke is gateway-owned; serviceRevokeBinding's local teardown is a
653
+ // no-op shim. Stamp the local downgrade and assert the read reflects it.
654
+ serviceRevokeBinding("asst-1", "telegram");
655
+ revokeGuardianChannelLocally("telegram");
656
+ expect(await getGuardianBinding("asst-1", "telegram")).toBeNull();
573
657
  });
574
658
  });
575
659
 
@@ -860,7 +944,7 @@ describe("channel-scoped guardian resolution", () => {
860
944
  resetTables();
861
945
  });
862
946
 
863
- test("isGuardian resolves independently per channel", () => {
947
+ test("isGuardian resolves independently per channel", async () => {
864
948
  // Create guardian binding on telegram
865
949
  createGuardianBinding({
866
950
  channel: "telegram",
@@ -877,15 +961,15 @@ describe("channel-scoped guardian resolution", () => {
877
961
  });
878
962
 
879
963
  // user-alpha is guardian for telegram but not voice
880
- expect(isGuardian("self", "telegram", "user-alpha")).toBe(true);
881
- expect(isGuardian("self", "phone", "user-alpha")).toBe(false);
964
+ expect(await isGuardian("self", "telegram", "user-alpha")).toBe(true);
965
+ expect(await isGuardian("self", "phone", "user-alpha")).toBe(false);
882
966
 
883
967
  // user-beta is guardian for voice but not telegram
884
- expect(isGuardian("self", "phone", "user-beta")).toBe(true);
885
- expect(isGuardian("self", "telegram", "user-beta")).toBe(false);
968
+ expect(await isGuardian("self", "phone", "user-beta")).toBe(true);
969
+ expect(await isGuardian("self", "telegram", "user-beta")).toBe(false);
886
970
  });
887
971
 
888
- test("getGuardianBinding returns different bindings for different channels", () => {
972
+ test("getGuardianBinding returns different bindings for different channels", async () => {
889
973
  createGuardianBinding({
890
974
  channel: "telegram",
891
975
  guardianExternalUserId: "user-alpha",
@@ -899,8 +983,8 @@ describe("channel-scoped guardian resolution", () => {
899
983
  guardianDeliveryChatId: "chat-beta",
900
984
  });
901
985
 
902
- const bindingTelegram = getGuardianBinding("self", "telegram");
903
- const bindingVoice = getGuardianBinding("self", "phone");
986
+ const bindingTelegram = await getGuardianBinding("self", "telegram");
987
+ const bindingVoice = await getGuardianBinding("self", "phone");
904
988
 
905
989
  expect(bindingTelegram).not.toBeNull();
906
990
  expect(bindingVoice).not.toBeNull();
@@ -908,7 +992,7 @@ describe("channel-scoped guardian resolution", () => {
908
992
  expect(bindingVoice!.guardianExternalUserId).toBe("user-beta");
909
993
  });
910
994
 
911
- test("revoking binding for one channel does not affect another", () => {
995
+ test("revoking binding for one channel does not affect another", async () => {
912
996
  createGuardianBinding({
913
997
  channel: "telegram",
914
998
  guardianExternalUserId: "user-alpha",
@@ -922,13 +1006,13 @@ describe("channel-scoped guardian resolution", () => {
922
1006
  guardianDeliveryChatId: "chat-beta",
923
1007
  });
924
1008
 
925
- serviceRevokeBinding("self", "telegram");
1009
+ revokeGuardianChannelLocally("telegram");
926
1010
 
927
- expect(getGuardianBinding("self", "telegram")).toBeNull();
928
- expect(getGuardianBinding("self", "phone")).not.toBeNull();
1011
+ expect(await getGuardianBinding("self", "telegram")).toBeNull();
1012
+ expect(await getGuardianBinding("self", "phone")).not.toBeNull();
929
1013
  });
930
1014
 
931
- test("validateAndConsumeVerification scoped to channel", () => {
1015
+ test("validateAndConsumeVerification scoped to channel", async () => {
932
1016
  // Create challenge on telegram
933
1017
  const { secret: secretTelegram } =
934
1018
  createInboundVerificationSession("telegram");
@@ -961,8 +1045,8 @@ describe("channel-scoped guardian resolution", () => {
961
1045
  );
962
1046
  expect(resultVoice.success).toBe(true);
963
1047
 
964
- const bindingTelegram = getGuardianBinding("self", "telegram");
965
- const bindingVoice = getGuardianBinding("self", "phone");
1048
+ const bindingTelegram = await getGuardianBinding("self", "telegram");
1049
+ const bindingVoice = await getGuardianBinding("self", "phone");
966
1050
  expect(bindingTelegram).toBeNull();
967
1051
  expect(bindingVoice).toBeNull();
968
1052
  });
@@ -1277,7 +1361,7 @@ describe("voice guardian challenge validation", () => {
1277
1361
  }
1278
1362
  });
1279
1363
 
1280
- test("validateAndConsumeVerification does not create a guardian binding for voice (caller responsibility)", () => {
1364
+ test("validateAndConsumeVerification does not create a guardian binding for voice (caller responsibility)", async () => {
1281
1365
  const { secret } = createInboundVerificationSession("phone");
1282
1366
 
1283
1367
  validateAndConsumeVerification(
@@ -1287,7 +1371,7 @@ describe("voice guardian challenge validation", () => {
1287
1371
  "voice-chat-1",
1288
1372
  );
1289
1373
 
1290
- const binding = getGuardianBinding("asst-1", "phone");
1374
+ const binding = await getGuardianBinding("asst-1", "phone");
1291
1375
  expect(binding).toBeNull();
1292
1376
  });
1293
1377
 
@@ -1361,7 +1445,7 @@ describe("voice guardian challenge validation", () => {
1361
1445
  expect(result2.success).toBe(false);
1362
1446
  });
1363
1447
 
1364
- test("validateAndConsumeVerification succeeds even with existing voice binding (conflict check is caller responsibility)", () => {
1448
+ test("validateAndConsumeVerification succeeds even with existing voice binding (conflict check is caller responsibility)", async () => {
1365
1449
  createGuardianBinding({
1366
1450
  channel: "phone",
1367
1451
  guardianExternalUserId: "old-voice-user",
@@ -1369,7 +1453,7 @@ describe("voice guardian challenge validation", () => {
1369
1453
  guardianDeliveryChatId: "old-voice-chat",
1370
1454
  });
1371
1455
 
1372
- const oldBinding = getGuardianBinding("asst-1", "phone");
1456
+ const oldBinding = await getGuardianBinding("asst-1", "phone");
1373
1457
  expect(oldBinding).not.toBeNull();
1374
1458
  expect(oldBinding!.guardianExternalUserId).toBe("old-voice-user");
1375
1459
 
@@ -1385,7 +1469,7 @@ describe("voice guardian challenge validation", () => {
1385
1469
  expect(result.success).toBe(true);
1386
1470
 
1387
1471
  // The original binding is untouched (no side effects)
1388
- const binding = getGuardianBinding("asst-1", "phone");
1472
+ const binding = await getGuardianBinding("asst-1", "phone");
1389
1473
  expect(binding).not.toBeNull();
1390
1474
  expect(binding!.guardianExternalUserId).toBe("old-voice-user");
1391
1475
  });
@@ -1400,7 +1484,7 @@ describe("voice guardian identity and revocation", () => {
1400
1484
  resetTables();
1401
1485
  });
1402
1486
 
1403
- test("isGuardian works for voice channel", () => {
1487
+ test("isGuardian works for voice channel", async () => {
1404
1488
  createGuardianBinding({
1405
1489
  channel: "phone",
1406
1490
  guardianExternalUserId: "voice-user-1",
@@ -1408,13 +1492,13 @@ describe("voice guardian identity and revocation", () => {
1408
1492
  guardianDeliveryChatId: "voice-chat-1",
1409
1493
  });
1410
1494
 
1411
- expect(isGuardian("asst-1", "phone", "voice-user-1")).toBe(true);
1412
- expect(isGuardian("asst-1", "phone", "voice-user-2")).toBe(false);
1495
+ expect(await isGuardian("asst-1", "phone", "voice-user-1")).toBe(true);
1496
+ expect(await isGuardian("asst-1", "phone", "voice-user-2")).toBe(false);
1413
1497
  // Voice guardian should not match telegram channel
1414
- expect(isGuardian("asst-1", "telegram", "voice-user-1")).toBe(false);
1498
+ expect(await isGuardian("asst-1", "telegram", "voice-user-1")).toBe(false);
1415
1499
  });
1416
1500
 
1417
- test("getGuardianBinding returns voice binding", () => {
1501
+ test("getGuardianBinding returns voice binding", async () => {
1418
1502
  createGuardianBinding({
1419
1503
  channel: "phone",
1420
1504
  guardianExternalUserId: "voice-user-1",
@@ -1422,13 +1506,13 @@ describe("voice guardian identity and revocation", () => {
1422
1506
  guardianDeliveryChatId: "voice-chat-1",
1423
1507
  });
1424
1508
 
1425
- const binding = getGuardianBinding("asst-1", "phone");
1509
+ const binding = await getGuardianBinding("asst-1", "phone");
1426
1510
  expect(binding).not.toBeNull();
1427
1511
  expect(binding!.channel).toBe("phone");
1428
1512
  expect(binding!.guardianExternalUserId).toBe("voice-user-1");
1429
1513
  });
1430
1514
 
1431
- test("revokeBinding clears active voice guardian binding", () => {
1515
+ test("revokeBinding clears active voice guardian binding", async () => {
1432
1516
  createGuardianBinding({
1433
1517
  channel: "phone",
1434
1518
  guardianExternalUserId: "voice-user-1",
@@ -1436,12 +1520,14 @@ describe("voice guardian identity and revocation", () => {
1436
1520
  guardianDeliveryChatId: "voice-chat-1",
1437
1521
  });
1438
1522
 
1439
- const result = serviceRevokeBinding("asst-1", "phone");
1440
- expect(result).toBe(true);
1441
- expect(getGuardianBinding("asst-1", "phone")).toBeNull();
1523
+ // The revoke is gateway-owned; serviceRevokeBinding's local teardown is a
1524
+ // no-op shim. Stamp the local downgrade and assert the read reflects it.
1525
+ serviceRevokeBinding("asst-1", "phone");
1526
+ revokeGuardianChannelLocally("phone");
1527
+ expect(await getGuardianBinding("asst-1", "phone")).toBeNull();
1442
1528
  });
1443
1529
 
1444
- test("revokeBinding for voice does not affect telegram binding", () => {
1530
+ test("revokeBinding for voice does not affect telegram binding", async () => {
1445
1531
  createGuardianBinding({
1446
1532
  channel: "phone",
1447
1533
  guardianExternalUserId: "voice-user-1",
@@ -1455,10 +1541,10 @@ describe("voice guardian identity and revocation", () => {
1455
1541
  guardianDeliveryChatId: "tg-chat-1",
1456
1542
  });
1457
1543
 
1458
- serviceRevokeBinding("asst-1", "phone");
1544
+ revokeGuardianChannelLocally("phone");
1459
1545
 
1460
- expect(getGuardianBinding("asst-1", "phone")).toBeNull();
1461
- expect(getGuardianBinding("asst-1", "telegram")).not.toBeNull();
1546
+ expect(await getGuardianBinding("asst-1", "phone")).toBeNull();
1547
+ expect(await getGuardianBinding("asst-1", "telegram")).not.toBeNull();
1462
1548
  });
1463
1549
  });
1464
1550
 
@@ -1716,7 +1802,7 @@ describe("HTTP handler voice guardian verification", () => {
1716
1802
  expect(resp!.bound).toBe(false);
1717
1803
 
1718
1804
  // Verify binding is actually revoked
1719
- expect(getGuardianBinding("self", "phone")).toBeNull();
1805
+ expect(await getGuardianBinding("self", "phone")).toBeNull();
1720
1806
  });
1721
1807
 
1722
1808
  test("revoke for voice does not affect telegram binding", async () => {
@@ -1742,8 +1828,8 @@ describe("HTTP handler voice guardian verification", () => {
1742
1828
 
1743
1829
  await handleChannelVerificationSession(msg);
1744
1830
 
1745
- expect(getGuardianBinding("self", "phone")).toBeNull();
1746
- expect(getGuardianBinding("self", "telegram")).not.toBeNull();
1831
+ expect(await getGuardianBinding("self", "phone")).toBeNull();
1832
+ expect(await getGuardianBinding("self", "telegram")).not.toBeNull();
1747
1833
  });
1748
1834
  });
1749
1835
 
@@ -59,7 +59,6 @@ mock.module("../daemon/disk-pressure-guard.js", () => ({
59
59
  diskPressureStatusSequence?.shift() ?? diskPressureStatus,
60
60
  }));
61
61
 
62
- import { upsertContact } from "../contacts/contact-store.js";
63
62
  import { getDb } from "../memory/db-connection.js";
64
63
  import { initializeDb } from "../memory/db-init.js";
65
64
  import * as deliveryCrud from "../memory/delivery-crud.js";
@@ -71,6 +70,7 @@ import {
71
70
  import { sweepFailedEvents } from "../runtime/channel-retry-sweep.js";
72
71
  import {
73
72
  handleChannelInbound,
73
+ seedContactChannel,
74
74
  setAdapterProcessMessage,
75
75
  } from "./helpers/channel-test-adapter.js";
76
76
  import { createGuardianBinding } from "./helpers/create-guardian-binding.js";
@@ -90,16 +90,12 @@ function resetTables(): void {
90
90
  }
91
91
 
92
92
  function seedTrustedContact(policy: "allow" | "escalate" = "allow"): void {
93
- upsertContact({
93
+ seedContactChannel({
94
+ sourceChannel: "telegram",
95
+ externalUserId: "telegram-user-1",
94
96
  displayName: "Example User",
95
- channels: [
96
- {
97
- type: "telegram",
98
- address: "telegram-user-1",
99
- status: "active",
100
- policy,
101
- },
102
- ],
97
+ status: "active",
98
+ policy,
103
99
  });
104
100
  }
105
101
 
@@ -236,16 +232,12 @@ describe("channel inbound disk pressure gate", () => {
236
232
  });
237
233
 
238
234
  test("blocks non-guardian Slack reactions silently (no reply) before persistence while locked", async () => {
239
- upsertContact({
235
+ seedContactChannel({
236
+ sourceChannel: "slack",
237
+ externalUserId: "slack-user-1",
240
238
  displayName: "Example Slack User",
241
- channels: [
242
- {
243
- type: "slack",
244
- address: "slack-user-1",
245
- status: "active",
246
- policy: "allow",
247
- },
248
- ],
239
+ status: "active",
240
+ policy: "allow",
249
241
  });
250
242
  const processMessage = mock(async () => {
251
243
  throw new Error("processMessage should not run");
@@ -81,6 +81,8 @@ mock.module("../runtime/gateway-client.js", () => ({
81
81
  }));
82
82
 
83
83
  mock.module("../memory/conversation-crud.js", () => ({
84
+ setConversationProcessingStartedAt: () => {},
85
+ isConversationProcessing: () => false,
84
86
  setConversationOriginChannelIfUnset: () => {},
85
87
  updateConversationContextWindow: () => {},
86
88
  deleteMessageById: () => {},