@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
@@ -7,12 +7,18 @@
7
7
  * and are discovered through the same path as every other channel.
8
8
  *
9
9
  * This suite verifies that address-based lookup returns the correct
10
- * `memberRecord` with the right channel/status so relay-setup-router
10
+ * `memberRecord` with the right ACL status so relay-setup-router
11
11
  * can emit the appropriate outcome (e.g. `unverified_caller`).
12
12
  */
13
13
 
14
14
  import { beforeEach, describe, expect, mock, test } from "bun:test";
15
15
 
16
+ import type {
17
+ ChannelPolicy,
18
+ ChannelStatus,
19
+ ContactRole,
20
+ } from "../contacts/types.js";
21
+
16
22
  // ── Logger mock (suppress output) ───────────────────────────────────────────
17
23
  mock.module("../util/logger.js", () => ({
18
24
  getLogger: () =>
@@ -23,18 +29,45 @@ mock.module("../util/logger.js", () => ({
23
29
  let _byAddress: ReturnType<
24
30
  (typeof import("../contacts/contact-store.js"))["findContactByAddress"]
25
31
  > = null;
26
- let _guardian: ReturnType<
27
- (typeof import("../contacts/contact-store.js"))["findGuardianForChannel"]
28
- > = null;
32
+ // ACL view is carried on memberRecord, sourced from the member-verdict cache.
33
+ // Seed it per test instead of seeding the DB.
34
+ let _acl: { status: ChannelStatus; policy: ChannelPolicy; role: ContactRole } =
35
+ { status: "unverified", policy: "allow", role: "contact" };
29
36
 
30
37
  mock.module("../contacts/contact-store.js", () => ({
31
38
  findContactByAddress: (_type: string, _addr: string) => _byAddress,
32
- findGuardianForChannel: (_channel: string) => _guardian,
39
+ }));
40
+
41
+ // Guardian resolution reads the gateway delivery cache; these suites only
42
+ // exercise the member/address path, so the cache peek stays empty.
43
+ mock.module("../contacts/guardian-delivery-reader.js", () => ({
44
+ peekCachedGuardianDelivery: () => undefined,
45
+ guardianForChannel: () => undefined,
33
46
  }));
34
47
 
35
48
  // ── Real import after mocks ───────────────────────────────────────────────────
36
49
  import type { ContactWithChannels } from "../contacts/types.js";
37
50
  import { resolveActorTrust } from "../runtime/actor-trust-resolver.js";
51
+ import {
52
+ __resetMemberVerdictCacheForTest,
53
+ setMemberVerdict,
54
+ } from "../runtime/member-verdict-cache.js";
55
+
56
+ const CHANNEL_ID = "ch-test";
57
+ const CONTACT_ID = "contact-test";
58
+
59
+ // Seed the member-verdict cache so the sync fallback resolves the member with
60
+ // the configured ACL view (mirrors a warmed gateway verdict).
61
+ function seedAcl(): void {
62
+ setMemberVerdict("phone", PHONE, {
63
+ trustClass: _acl.role === "guardian" ? "guardian" : "unknown",
64
+ canonicalSenderId: PHONE,
65
+ contactId: CONTACT_ID,
66
+ channelId: CHANNEL_ID,
67
+ status: _acl.status,
68
+ policy: _acl.policy,
69
+ });
70
+ }
38
71
 
39
72
  // ── Helpers ──────────────────────────────────────────────────────────────────
40
73
 
@@ -44,13 +77,14 @@ function makeContact(
44
77
  role: "guardian" | "contact" = "contact",
45
78
  status: "unverified" | "active" = "unverified",
46
79
  ): ContactWithChannels {
47
- const channelId = "ch-test";
80
+ // ACL lives on memberRecord (carrier), sourced from the member-verdict cache —
81
+ // record the intended view so seedAcl() warms it before resolution.
82
+ _acl = { status, policy: "allow", role };
48
83
  return {
49
- id: "contact-test",
84
+ id: CONTACT_ID,
50
85
  displayName: "Patrick Test",
51
- role,
52
- principalId: null,
53
86
  notes: null,
87
+ role,
54
88
  lastInteraction: null,
55
89
  interactionCount: 0,
56
90
  contactType: "human" as const,
@@ -59,22 +93,16 @@ function makeContact(
59
93
  updatedAt: 0,
60
94
  channels: [
61
95
  {
62
- id: channelId,
63
- contactId: "contact-test",
96
+ id: CHANNEL_ID,
97
+ contactId: CONTACT_ID,
64
98
  type: "phone",
65
99
  address: PHONE,
66
100
  externalChatId: null,
67
101
  isPrimary: true,
68
- status,
69
- policy: "allow",
70
- verifiedAt: null,
71
- verifiedVia: null,
72
- revokedReason: null,
73
- blockedReason: null,
102
+ inviteId: null,
103
+ lastSeenAt: null,
74
104
  interactionCount: 0,
75
105
  lastInteraction: null,
76
- lastSeenAt: null,
77
- inviteId: null,
78
106
  createdAt: 0,
79
107
  updatedAt: 0,
80
108
  },
@@ -87,12 +115,14 @@ function makeContact(
87
115
  describe("resolveActorTrust — address fallback", () => {
88
116
  beforeEach(() => {
89
117
  _byAddress = null;
90
- _guardian = null;
118
+ _acl = { status: "unverified", policy: "allow", role: "contact" };
119
+ __resetMemberVerdictCacheForTest();
91
120
  });
92
121
 
93
122
  test("finds unverified channel via address when externalUserId is null", () => {
94
123
  // Simulate a contact registered by name-capture: address set, externalUserId null.
95
124
  _byAddress = makeContact("contact", "unverified");
125
+ seedAcl();
96
126
 
97
127
  const result = resolveActorTrust({
98
128
  assistantId: "asst-1",
@@ -103,7 +133,7 @@ describe("resolveActorTrust — address fallback", () => {
103
133
 
104
134
  expect(result.memberRecord).not.toBeNull();
105
135
  expect(result.memberRecord?.contact.displayName).toBe("Patrick Test");
106
- expect(result.memberRecord?.channel.status).toBe("unverified");
136
+ expect(result.memberRecord?.status).toBe("unverified");
107
137
  // trustClass is 'unverified_contact' for a member whose channel is
108
138
  // pending or unverified — known to the guardian but not yet verified.
109
139
  expect(result.trustClass).toBe("unverified_contact");
@@ -111,6 +141,7 @@ describe("resolveActorTrust — address fallback", () => {
111
141
 
112
142
  test("address lookup is the sole member resolution path", () => {
113
143
  _byAddress = makeContact("contact", "active");
144
+ seedAcl();
114
145
 
115
146
  const result = resolveActorTrust({
116
147
  assistantId: "asst-1",
@@ -119,7 +150,7 @@ describe("resolveActorTrust — address fallback", () => {
119
150
  actorExternalId: PHONE,
120
151
  });
121
152
 
122
- expect(result.memberRecord?.channel.status).toBe("active");
153
+ expect(result.memberRecord?.status).toBe("active");
123
154
  expect(result.memberRecord?.channel.address).toBe(PHONE);
124
155
  });
125
156
 
@@ -137,10 +168,28 @@ describe("resolveActorTrust — address fallback", () => {
137
168
  expect(result.trustClass).toBe("unknown");
138
169
  });
139
170
 
171
+ test("fail-closed: contact found but verdict cache miss → unknown", () => {
172
+ // The sync fallback runs only when there is no live verdict; with no cached
173
+ // verdict either, the member stays unresolved and trust is fail-closed.
174
+ _byAddress = makeContact("contact", "active");
175
+ // No seedAcl() — the cache is a miss for this sender.
176
+
177
+ const result = resolveActorTrust({
178
+ assistantId: "asst-1",
179
+ sourceChannel: "phone",
180
+ conversationExternalId: PHONE,
181
+ actorExternalId: PHONE,
182
+ });
183
+
184
+ expect(result.memberRecord).toBeNull();
185
+ expect(result.trustClass).toBe("unknown");
186
+ });
187
+
140
188
  test("address-found active channel elevates trust to trusted_contact", () => {
141
189
  // An active channel found via address (e.g. after manual verify without externalUserId set)
142
190
  // should still yield trusted_contact trust class.
143
191
  _byAddress = makeContact("contact", "active");
192
+ seedAcl();
144
193
 
145
194
  const result = resolveActorTrust({
146
195
  assistantId: "asst-1",
@@ -150,7 +199,7 @@ describe("resolveActorTrust — address fallback", () => {
150
199
  });
151
200
 
152
201
  expect(result.memberRecord).not.toBeNull();
153
- expect(result.memberRecord?.channel.status).toBe("active");
202
+ expect(result.memberRecord?.status).toBe("active");
154
203
  expect(result.trustClass).toBe("trusted_contact");
155
204
  });
156
205
 
@@ -158,9 +207,10 @@ describe("resolveActorTrust — address fallback", () => {
158
207
  // Mirrors the unverified branch but for `pending` status (e.g. a phone
159
208
  // contact registered by name-capture awaiting the DTMF challenge).
160
209
  const contact = makeContact("contact", "unverified");
161
- // Override status to "pending" — makeContact only accepts unverified/active
162
- contact.channels[0]!.status = "pending";
210
+ // Override ACL status to "pending" — makeContact only accepts unverified/active.
211
+ _acl = { ..._acl, status: "pending" };
163
212
  _byAddress = contact;
213
+ seedAcl();
164
214
 
165
215
  const result = resolveActorTrust({
166
216
  assistantId: "asst-1",
@@ -169,16 +219,17 @@ describe("resolveActorTrust — address fallback", () => {
169
219
  actorExternalId: PHONE,
170
220
  });
171
221
 
172
- expect(result.memberRecord?.channel.status).toBe("pending");
222
+ expect(result.memberRecord?.status).toBe("pending");
173
223
  expect(result.trustClass).toBe("unverified_contact");
174
224
  });
175
225
 
176
226
  test("blocked-status member is classified as unknown (not unverified_contact)", () => {
177
227
  // Hard-deny statuses (blocked, revoked) stay `unknown` — admission-layer
178
- // re-checks channel.status and emits the hard-deny reasons.
228
+ // re-checks channel status and emits the hard-deny reasons.
179
229
  const contact = makeContact("contact", "unverified");
180
- contact.channels[0]!.status = "blocked";
230
+ _acl = { ..._acl, status: "blocked" };
181
231
  _byAddress = contact;
232
+ seedAcl();
182
233
 
183
234
  const result = resolveActorTrust({
184
235
  assistantId: "asst-1",
@@ -187,14 +238,15 @@ describe("resolveActorTrust — address fallback", () => {
187
238
  actorExternalId: PHONE,
188
239
  });
189
240
 
190
- expect(result.memberRecord?.channel.status).toBe("blocked");
241
+ expect(result.memberRecord?.status).toBe("blocked");
191
242
  expect(result.trustClass).toBe("unknown");
192
243
  });
193
244
 
194
245
  test("revoked-status member is classified as unknown", () => {
195
246
  const contact = makeContact("contact", "unverified");
196
- contact.channels[0]!.status = "revoked";
247
+ _acl = { ..._acl, status: "revoked" };
197
248
  _byAddress = contact;
249
+ seedAcl();
198
250
 
199
251
  const result = resolveActorTrust({
200
252
  assistantId: "asst-1",
@@ -203,7 +255,7 @@ describe("resolveActorTrust — address fallback", () => {
203
255
  actorExternalId: PHONE,
204
256
  });
205
257
 
206
- expect(result.memberRecord?.channel.status).toBe("revoked");
258
+ expect(result.memberRecord?.status).toBe("revoked");
207
259
  expect(result.trustClass).toBe("unknown");
208
260
  });
209
261
  });
@@ -384,6 +384,7 @@ describe("SubagentManager.spawn — overrideProfile inheritance", () => {
384
384
  // inheritance chain breaks at the second nesting level.
385
385
 
386
386
  mock.module("../memory/conversation-crud.js", () => ({
387
+ setConversationProcessingStartedAt: () => {},
387
388
  // Always return undefined for the row read so the test fails fast unless
388
389
  // executeSubagentSpawn reads from context.overrideProfile first.
389
390
  getConversationOverrideProfile: () => undefined,
@@ -21,6 +21,8 @@ import type {
21
21
  import type { Message } from "../providers/types.js";
22
22
 
23
23
  mock.module("../memory/conversation-crud.js", () => ({
24
+ setConversationProcessingStartedAt: () => {},
25
+ isConversationProcessing: () => false,
24
26
  getConversationOverrideProfile: () => undefined,
25
27
  }));
26
28
 
@@ -31,6 +31,8 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
31
31
  let mockOverrideProfile: string | undefined = undefined;
32
32
 
33
33
  mock.module("../memory/conversation-crud.js", () => ({
34
+ setConversationProcessingStartedAt: () => {},
35
+ isConversationProcessing: () => false,
34
36
  getConversationOverrideProfile: (_id: string) => mockOverrideProfile,
35
37
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
36
38
  }));
@@ -55,6 +55,8 @@ let mockedRowContent = "";
55
55
  const updates: Array<{ id: string; content: string }> = [];
56
56
 
57
57
  mock.module("../memory/conversation-crud.js", () => ({
58
+ setConversationProcessingStartedAt: () => {},
59
+ isConversationProcessing: () => false,
58
60
  addMessage: () => ({ id: "mock-msg-id" }),
59
61
  getMessageById: (id: string) =>
60
62
  mockedRowContent ? { id, content: mockedRowContent } : null,
@@ -50,6 +50,8 @@ let mockedRowContent = "";
50
50
  const updates: Array<{ id: string; content: string }> = [];
51
51
 
52
52
  mock.module("../memory/conversation-crud.js", () => ({
53
+ setConversationProcessingStartedAt: () => {},
54
+ isConversationProcessing: () => false,
53
55
  addMessage: () => ({ id: "mock-msg-id" }),
54
56
  getMessageById: (id: string) =>
55
57
  mockedRowContent ? { id, content: mockedRowContent } : null,
@@ -121,6 +121,8 @@ mock.module("../security/secret-allowlist.js", () => ({
121
121
  }));
122
122
 
123
123
  mock.module("../memory/conversation-crud.js", () => ({
124
+ setConversationProcessingStartedAt: () => {},
125
+ isConversationProcessing: () => false,
124
126
  setConversationOriginChannelIfUnset: () => {},
125
127
  updateConversationContextWindow: () => {},
126
128
  deleteMessageById: () => {},
@@ -373,6 +373,48 @@ describe("extractVellumLinks", () => {
373
373
  expect(result.directiveRequests[1].path).toBe("/tmp/b.pdf");
374
374
  });
375
375
 
376
+ test("decodes URL-encoded spaces in workspace paths", () => {
377
+ const text =
378
+ "[file with spaces.txt](vellum://workspace/scratch/file%20with%20spaces.txt)";
379
+ const result = extractVellumLinks(text);
380
+
381
+ expect(result.directiveRequests).toHaveLength(1);
382
+ expect(result.directiveRequests[0].source).toBe("sandbox");
383
+ expect(result.directiveRequests[0].path).toBe(
384
+ "scratch/file with spaces.txt",
385
+ );
386
+ });
387
+
388
+ test("decodes URL-encoded spaces in host paths", () => {
389
+ const text =
390
+ "[my file.pdf](vellum://host/Users/me/my%20file.pdf)";
391
+ const result = extractVellumLinks(text);
392
+
393
+ expect(result.directiveRequests).toHaveLength(1);
394
+ expect(result.directiveRequests[0].source).toBe("host");
395
+ expect(result.directiveRequests[0].path).toBe("/Users/me/my file.pdf");
396
+ });
397
+
398
+ test("warns on malformed percent-encoding instead of throwing", () => {
399
+ const text =
400
+ "[100% complete.txt](vellum://workspace/scratch/100%25complete.txt)";
401
+ const result = extractVellumLinks(text);
402
+
403
+ // %25 decodes to %, so this should succeed
404
+ expect(result.directiveRequests).toHaveLength(1);
405
+ expect(result.directiveRequests[0].path).toBe("scratch/100%complete.txt");
406
+ });
407
+
408
+ test("warns on malformed percent-encoding and skips the link", () => {
409
+ const text =
410
+ "[bad file](vellum://workspace/scratch/100%complete.txt)";
411
+ const result = extractVellumLinks(text);
412
+
413
+ expect(result.directiveRequests).toHaveLength(0);
414
+ expect(result.parseWarnings).toHaveLength(1);
415
+ expect(result.parseWarnings[0]).toContain("malformed percent-encoding");
416
+ });
417
+
376
418
  test("warns on empty workspace path", () => {
377
419
  const text = "[file](vellum://workspace/)";
378
420
  const result = extractVellumLinks(text);
@@ -100,6 +100,8 @@ mock.module("../daemon/process-message.js", () => ({
100
100
 
101
101
  const createdConversations: Array<{ conversationType: string }> = [];
102
102
  mock.module("../memory/conversation-crud.js", () => ({
103
+ setConversationProcessingStartedAt: () => {},
104
+ isConversationProcessing: () => false,
103
105
  addMessage: mock(() => ({ id: "msg-1" })),
104
106
  archiveConversation: mock(() => true),
105
107
  batchSetDisplayOrders: mock(() => {}),
@@ -43,6 +43,8 @@ mock.module("../memory/conversation-key-store.js", () => ({
43
43
  const mockAddMessage = mock(() => {});
44
44
 
45
45
  mock.module("../memory/conversation-crud.js", () => ({
46
+ setConversationProcessingStartedAt: () => {},
47
+ isConversationProcessing: () => false,
46
48
  addMessage: mockAddMessage,
47
49
  reserveMessage: mock(async () => ({ id: "msg-reserve" })),
48
50
  }));
@@ -41,6 +41,8 @@ mock.module("../config/loader.js", () => ({
41
41
  }));
42
42
 
43
43
  mock.module("../memory/conversation-crud.js", () => ({
44
+ setConversationProcessingStartedAt: () => {},
45
+ isConversationProcessing: () => false,
44
46
  addMessage: () => ({ id: "mock-msg-id" }),
45
47
  getMessageById: () => null,
46
48
  updateMessageContent: () => {},
@@ -168,6 +168,25 @@ mock.module("../notifications/emit-signal.js", () => ({
168
168
  }),
169
169
  }));
170
170
 
171
+ // Guardian principalId is resolved from the gateway binding reader. Mirror the
172
+ // vellum binding seeded by resetTables so guardian dispatch can resolve it.
173
+ mock.module("../contacts/guardian-delivery-reader.js", () => ({
174
+ getGuardianDelivery: async () => [
175
+ {
176
+ channelType: "vellum",
177
+ contactId: "guardian-vellum",
178
+ principalId: "test-principal-id",
179
+ address: "local",
180
+ status: "active",
181
+ },
182
+ ],
183
+ guardianForChannel: (
184
+ list: Array<{ channelType: string; status: string }>,
185
+ channelType: string,
186
+ ) => list.find((g) => g.channelType === channelType && g.status === "active"),
187
+ anyGuardian: (list: unknown[]) => list[0],
188
+ }));
189
+
171
190
  mock.module("../calls/voice-session-bridge.js", () => {
172
191
  mockStartVoiceTurn = mock(createMockVoiceTurn(["Hello", " there"]));
173
192
  return {
@@ -69,7 +69,6 @@ mock.module("../daemon/approval-generators.js", () => ({
69
69
  createApprovalConversationGenerator: () => _testApprovalConversationGenerator,
70
70
  }));
71
71
 
72
- import { upsertContact } from "../contacts/contact-store.js";
73
72
  import type { Conversation } from "../daemon/conversation.js";
74
73
  import {
75
74
  createCanonicalGuardianDelivery,
@@ -86,7 +85,10 @@ import * as gatewayClient from "../runtime/gateway-client.js";
86
85
  import * as pendingInteractions from "../runtime/pending-interactions.js";
87
86
  import { _setTestPollMaxWait } from "../runtime/routes/channel-route-shared.js";
88
87
  import { resetDbForTesting } from "./db-test-helpers.js";
89
- import { handleChannelInbound } from "./helpers/channel-test-adapter.js";
88
+ import {
89
+ handleChannelInbound,
90
+ seedContactChannel,
91
+ } from "./helpers/channel-test-adapter.js";
90
92
  import { createGuardianBinding } from "./helpers/create-guardian-binding.js";
91
93
 
92
94
  await initializeDb();
@@ -212,22 +214,19 @@ function makeInboundRequest(overrides: Record<string, unknown> = {}): Request {
212
214
  const noopProcessMessage = mock(async () => ({ messageId: "msg-1" }));
213
215
 
214
216
  function ensureTestContact(): void {
215
- upsertContact({
217
+ seedContactChannel({
218
+ sourceChannel: "telegram",
219
+ externalUserId: "telegram-user-default",
216
220
  displayName: "Test User",
217
- channels: [
218
- {
219
- type: "telegram",
220
- address: "telegram-user-default",
221
- status: "active",
222
- policy: "allow",
223
- },
224
- {
225
- type: "slack",
226
- address: "slack-user-default",
227
- status: "active",
228
- policy: "allow",
229
- },
230
- ],
221
+ status: "active",
222
+ policy: "allow",
223
+ });
224
+ seedContactChannel({
225
+ sourceChannel: "slack",
226
+ externalUserId: "slack-user-default",
227
+ displayName: "Test User",
228
+ status: "active",
229
+ policy: "allow",
231
230
  });
232
231
  }
233
232
 
@@ -1926,16 +1925,12 @@ describe("trusted-contact self-approval blocked before guardian approval row exi
1926
1925
  guardianDeliveryChatId: "guardian-tc-selfapproval-chat",
1927
1926
  guardianPrincipalId: "guardian-tc-selfapproval",
1928
1927
  });
1929
- upsertContact({
1928
+ seedContactChannel({
1929
+ sourceChannel: "telegram",
1930
+ externalUserId: "tc-selfapproval-user",
1930
1931
  displayName: "TC Self-Approval User",
1931
- channels: [
1932
- {
1933
- type: "telegram",
1934
- address: "tc-selfapproval-user",
1935
- status: "active",
1936
- policy: "allow",
1937
- },
1938
- ],
1932
+ status: "active",
1933
+ policy: "allow",
1939
1934
  });
1940
1935
  });
1941
1936
 
@@ -493,6 +493,34 @@ describe("channel-delivery-store", () => {
493
493
  });
494
494
  });
495
495
 
496
+ test("conversation detail omits Slack metadata for non-Slack channels", () => {
497
+ const result = recordInbound("telegram", "tg-chat-1", "msg-1", {
498
+ sourceThreadId: "9001",
499
+ });
500
+
501
+ upsertBinding({
502
+ conversationId: result.conversationId,
503
+ sourceChannel: "telegram",
504
+ externalChatId: "tg-chat-1",
505
+ externalChatName: "Family",
506
+ externalThreadId: "9001",
507
+ });
508
+
509
+ const detail = buildConversationDetailResponse(result.conversationId);
510
+ const binding = detail?.conversation.channelBinding;
511
+
512
+ // The channel-neutral fields pass through for any source channel...
513
+ expect(binding).toMatchObject({
514
+ sourceChannel: "telegram",
515
+ externalChatId: "tg-chat-1",
516
+ externalChatName: "Family",
517
+ externalThreadId: "9001",
518
+ });
519
+ // ...but Slack-only deep-link metadata is not synthesized.
520
+ expect(binding).not.toHaveProperty("slackThread");
521
+ expect(binding).not.toHaveProperty("slackChannel");
522
+ });
523
+
496
524
  test("binding upsert preserves existing chat name when incoming name is missing", () => {
497
525
  const result = recordInbound("slack", "C0123ABCDEF", "msg-1", {
498
526
  sourceThreadId: "1710000000.000100",