@vellumai/assistant 0.10.3 → 0.10.4-staging.1

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 (239) hide show
  1. package/openapi.yaml +73 -56
  2. package/package.json +1 -1
  3. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
  4. package/src/__tests__/assistant-stream-state.test.ts +3 -76
  5. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -2
  6. package/src/__tests__/channel-approval-routes.test.ts +21 -26
  7. package/src/__tests__/channel-delivery-store.test.ts +28 -0
  8. package/src/__tests__/channel-guardian.test.ts +82 -32
  9. package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
  10. package/src/__tests__/channel-reply-delivery.test.ts +6 -2
  11. package/src/__tests__/compaction-ledger-store.test.ts +128 -0
  12. package/src/__tests__/config-loader-backfill.test.ts +148 -0
  13. package/src/__tests__/consult-deadline.test.ts +60 -0
  14. package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
  15. package/src/__tests__/contact-store-user-file.test.ts +7 -10
  16. package/src/__tests__/contacts-relay-reads.test.ts +6 -9
  17. package/src/__tests__/contacts-write.test.ts +0 -2
  18. package/src/__tests__/conversation-agent-loop-overflow.test.ts +4 -2
  19. package/src/__tests__/conversation-agent-loop.test.ts +98 -7
  20. package/src/__tests__/conversation-attention-telegram.test.ts +9 -11
  21. package/src/__tests__/conversation-error.test.ts +18 -0
  22. package/src/__tests__/conversation-fork-crud.test.ts +354 -24
  23. package/src/__tests__/conversation-title-service.test.ts +222 -201
  24. package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
  25. package/src/__tests__/delete-propagation.test.ts +5 -3
  26. package/src/__tests__/dm-backfill.test.ts +6 -4
  27. package/src/__tests__/emit-signal-routing-intent.test.ts +2 -6
  28. package/src/__tests__/guardian-binding-drift-heal.test.ts +43 -23
  29. package/src/__tests__/guardian-dispatch.test.ts +50 -5
  30. package/src/__tests__/guardian-routing-state.test.ts +6 -10
  31. package/src/__tests__/helpers/channel-test-adapter.ts +45 -12
  32. package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
  33. package/src/__tests__/helpers/mock-logger.ts +1 -0
  34. package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
  35. package/src/__tests__/inbound-invite-redemption.test.ts +87 -10
  36. package/src/__tests__/invite-redemption-service.test.ts +273 -53
  37. package/src/__tests__/invite-routes-http.test.ts +34 -0
  38. package/src/__tests__/invite-service-ipc.test.ts +65 -2
  39. package/src/__tests__/list-messages-page-latest.test.ts +173 -4
  40. package/src/__tests__/mcp-config-secret-boundary.test.ts +3 -0
  41. package/src/__tests__/non-member-access-request.test.ts +15 -13
  42. package/src/__tests__/onboarding-persona-write.test.ts +52 -22
  43. package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
  44. package/src/__tests__/persona-resolver.test.ts +75 -45
  45. package/src/__tests__/plugin-bootstrap.test.ts +13 -5
  46. package/src/__tests__/plugin-disabled-state.test.ts +190 -0
  47. package/src/__tests__/provider-usage-tracking.test.ts +1 -1
  48. package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
  49. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
  50. package/src/__tests__/reaction-persistence.test.ts +51 -4
  51. package/src/__tests__/relay-server.test.ts +88 -31
  52. package/src/__tests__/runtime-attachment-metadata.test.ts +9 -11
  53. package/src/__tests__/settings-routes.test.ts +32 -0
  54. package/src/__tests__/slack-block-formatting.test.ts +1 -38
  55. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +13 -36
  56. package/src/__tests__/stt-hints.test.ts +6 -3
  57. package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
  58. package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
  59. package/src/__tests__/subagent-role-registry.test.ts +17 -4
  60. package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
  61. package/src/__tests__/subagent-tools.test.ts +398 -3
  62. package/src/__tests__/thread-backfill.test.ts +3 -3
  63. package/src/__tests__/tool-preview-lifecycle.test.ts +26 -10
  64. package/src/__tests__/tool-start-timestamp.test.ts +4 -3
  65. package/src/__tests__/trusted-contact-approval-notifier.test.ts +37 -51
  66. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -2
  67. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
  68. package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
  69. package/src/__tests__/trusted-contact-verification.test.ts +79 -54
  70. package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
  71. package/src/__tests__/voice-invite-redemption.test.ts +183 -20
  72. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
  73. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +2 -2
  74. package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
  75. package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
  76. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
  77. package/src/agent/loop-exclusive-tool.test.ts +19 -15
  78. package/src/agent/loop-native-web-search.test.ts +200 -0
  79. package/src/agent/loop.ts +108 -1
  80. package/src/api/responses/conversation-message.ts +9 -0
  81. package/src/approvals/guardian-request-resolvers.ts +16 -4
  82. package/src/calls/__tests__/relay-setup-router.test.ts +10 -18
  83. package/src/calls/guardian-dispatch.ts +14 -11
  84. package/src/calls/inbound-trust-reader.ts +7 -1
  85. package/src/calls/relay-access-wait.ts +6 -6
  86. package/src/calls/relay-server.ts +22 -2
  87. package/src/calls/relay-setup-router.ts +10 -10
  88. package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
  89. package/src/cli/commands/contacts.ts +10 -7
  90. package/src/cli/commands/memory/__tests__/worker.test.ts +147 -17
  91. package/src/cli/commands/memory/worker.ts +97 -30
  92. package/src/cli/commands/plugins.ts +3 -146
  93. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +17 -17
  94. package/src/cli/lib/__tests__/publish-plugin.test.ts +98 -0
  95. package/src/cli/lib/publish-plugin.ts +231 -1
  96. package/src/config/__tests__/sync-gated-profiles.test.ts +5 -7
  97. package/src/config/bundled-skills/subagent/SKILL.md +16 -1
  98. package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
  99. package/src/config/call-site-defaults.ts +0 -6
  100. package/src/config/llm-resolver.ts +0 -3
  101. package/src/config/schemas/call-site-catalog.ts +0 -7
  102. package/src/config/schemas/heartbeat.ts +2 -5
  103. package/src/config/schemas/llm.ts +3 -12
  104. package/src/config/schemas/memory-lifecycle.ts +1 -1
  105. package/src/config/seed-inference-profiles.ts +76 -35
  106. package/src/config/sync-gated-profiles.ts +0 -3
  107. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +7 -8
  108. package/src/contacts/__tests__/member-write-relay.test.ts +35 -11
  109. package/src/contacts/contact-store.ts +27 -237
  110. package/src/contacts/contacts-write.ts +18 -58
  111. package/src/contacts/gateway-channel-read.ts +51 -0
  112. package/src/contacts/member-write-relay.ts +25 -31
  113. package/src/contacts/types.ts +3 -15
  114. package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
  115. package/src/daemon/conversation-agent-loop-handlers.ts +29 -10
  116. package/src/daemon/conversation-agent-loop.ts +68 -61
  117. package/src/daemon/conversation-error.ts +7 -10
  118. package/src/daemon/conversation-tool-setup.ts +0 -10
  119. package/src/daemon/conversation.ts +10 -0
  120. package/src/daemon/external-plugins-bootstrap.ts +8 -2
  121. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
  122. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
  123. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -2
  124. package/src/daemon/handlers/__tests__/config-channels.test.ts +9 -14
  125. package/src/daemon/handlers/config-channels.ts +14 -29
  126. package/src/daemon/lifecycle.ts +16 -4
  127. package/src/daemon/message-types/surfaces.ts +2 -0
  128. package/src/heartbeat/heartbeat-service.ts +5 -0
  129. package/src/home/relationship-state-writer.ts +5 -0
  130. package/src/memory/__tests__/embedding-cache.test.ts +136 -0
  131. package/src/memory/compaction-ledger-store.ts +107 -0
  132. package/src/memory/conversation-crud.ts +136 -61
  133. package/src/memory/conversation-title-service.ts +173 -24
  134. package/src/memory/embedding-backend.ts +8 -1
  135. package/src/memory/embedding-cache.ts +139 -0
  136. package/src/memory/jobs-worker.ts +75 -29
  137. package/src/memory/memory-retrospective-job.ts +5 -0
  138. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +27 -5
  139. package/src/memory/migrations/302-create-compaction-events.ts +107 -0
  140. package/src/memory/migrations/303-add-conversation-creation-seq.ts +33 -0
  141. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +79 -6
  142. package/src/memory/schema/contacts.ts +6 -2
  143. package/src/memory/schema/conversations.ts +39 -0
  144. package/src/memory/steps.ts +1090 -367
  145. package/src/memory/worker-control.ts +104 -18
  146. package/src/memory/worker-process.ts +17 -0
  147. package/src/messaging/channel-binding-metadata.ts +31 -0
  148. package/src/messaging/channel-binding-schema.ts +51 -0
  149. package/src/messaging/providers/__tests__/callback-routing.test.ts +45 -0
  150. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +195 -0
  151. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +11 -0
  152. package/src/messaging/providers/a2a/deliver.ts +5 -1
  153. package/src/messaging/providers/a2a/transport.ts +10 -0
  154. package/src/messaging/providers/callback-routing.ts +48 -0
  155. package/src/messaging/providers/channel-transport.ts +55 -0
  156. package/src/messaging/providers/index.ts +65 -241
  157. package/src/messaging/providers/slack/binding-metadata.ts +62 -0
  158. package/src/messaging/providers/slack/transport.ts +92 -0
  159. package/src/messaging/providers/telegram-bot/transport.ts +51 -0
  160. package/src/messaging/providers/whatsapp/transport.ts +38 -0
  161. package/src/notifications/__tests__/broadcaster.test.ts +0 -8
  162. package/src/notifications/__tests__/connected-channels.test.ts +8 -36
  163. package/src/notifications/__tests__/destination-resolver.test.ts +12 -117
  164. package/src/notifications/destination-resolver.ts +7 -23
  165. package/src/notifications/emit-signal.ts +5 -11
  166. package/src/plugins/defaults/index.ts +0 -35
  167. package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +11 -0
  168. package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +243 -2
  169. package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +167 -14
  170. package/src/plugins/disabled-state.ts +31 -0
  171. package/src/plugins/registry.ts +55 -12
  172. package/src/prompts/persona-resolver.ts +43 -11
  173. package/src/providers/call-site-routing.ts +41 -0
  174. package/src/providers/provider-send-message.ts +6 -0
  175. package/src/providers/ratelimit.ts +6 -0
  176. package/src/providers/registry.ts +1 -1
  177. package/src/providers/retry.ts +6 -0
  178. package/src/providers/types.ts +13 -0
  179. package/src/providers/usage-tracking.ts +6 -0
  180. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +30 -27
  181. package/src/runtime/__tests__/local-principal-trust.test.ts +16 -18
  182. package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
  183. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +115 -168
  184. package/src/runtime/access-request-helper.ts +1 -2
  185. package/src/runtime/actor-trust-resolver.ts +44 -17
  186. package/src/runtime/anchored-guardian.test.ts +7 -54
  187. package/src/runtime/anchored-guardian.ts +4 -53
  188. package/src/runtime/assistant-stream-state.ts +12 -74
  189. package/src/runtime/channel-reply-delivery.ts +3 -8
  190. package/src/runtime/guardian-vellum-migration.ts +18 -16
  191. package/src/runtime/invite-redemption-service.ts +25 -10
  192. package/src/runtime/local-actor-identity.test.ts +108 -0
  193. package/src/runtime/local-actor-identity.ts +27 -20
  194. package/src/runtime/member-verdict-cache.ts +0 -0
  195. package/src/runtime/routes/__tests__/contact-routes.test.ts +100 -7
  196. package/src/runtime/routes/__tests__/global-search-routes.test.ts +1 -2
  197. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +2 -1
  198. package/src/runtime/routes/contact-routes.ts +40 -25
  199. package/src/runtime/routes/conversation-list-routes.ts +1 -29
  200. package/src/runtime/routes/conversation-routes.ts +27 -7
  201. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -10
  202. package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -8
  203. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +19 -0
  204. package/src/runtime/routes/settings-routes.ts +8 -3
  205. package/src/runtime/services/conversation-serializer.ts +6 -49
  206. package/src/runtime/slack-block-formatting.ts +0 -15
  207. package/src/runtime/trust-verdict-consumer.ts +36 -41
  208. package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
  209. package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
  210. package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +17 -39
  211. package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
  212. package/src/subagent/index.ts +1 -1
  213. package/src/subagent/manager.ts +245 -33
  214. package/src/subagent/types.ts +8 -1
  215. package/src/tools/registry.ts +10 -3
  216. package/src/tools/subagent/consult-deadline.ts +49 -0
  217. package/src/tools/subagent/spawn.ts +234 -5
  218. package/src/util/logger.ts +9 -0
  219. package/src/util/platform.ts +14 -0
  220. package/src/workspace/migrations/031-drop-user-md.ts +232 -148
  221. package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
  222. package/src/workspace/migrations/registry.ts +2 -0
  223. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
  224. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
  225. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
  226. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -314
  227. package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
  228. package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
  229. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
  230. package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
  231. package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
  232. package/src/plugins/defaults/advisor/config.ts +0 -21
  233. package/src/plugins/defaults/advisor/consult.ts +0 -197
  234. package/src/plugins/defaults/advisor/context-pack.ts +0 -288
  235. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
  236. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
  237. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
  238. package/src/plugins/defaults/advisor/package.json +0 -14
  239. package/src/plugins/defaults/advisor/tools/advisor.ts +0 -92
package/openapi.yaml CHANGED
@@ -3,7 +3,7 @@
3
3
  openapi: 3.1.0
4
4
  info:
5
5
  title: Vellum Assistant API
6
- version: 0.10.3
6
+ version: 0.10.4
7
7
  description: Auto-generated OpenAPI specification for the Vellum Assistant runtime HTTP server.
8
8
  servers:
9
9
  - url: http://127.0.0.1:7821
@@ -4841,14 +4841,18 @@ paths:
4841
4841
  type: string
4842
4842
  role:
4843
4843
  type: string
4844
+ enum:
4845
+ - guardian
4846
+ - contact
4844
4847
  notes:
4845
4848
  anyOf:
4846
4849
  - type: string
4847
4850
  - type: "null"
4848
4851
  contactType:
4849
- anyOf:
4850
- - type: string
4851
- - type: "null"
4852
+ type: string
4853
+ enum:
4854
+ - human
4855
+ - assistant
4852
4856
  lastInteraction:
4853
4857
  anyOf:
4854
4858
  - type: number
@@ -4915,20 +4919,15 @@ paths:
4915
4919
  - address
4916
4920
  - isPrimary
4917
4921
  - externalUserId
4918
- - status
4919
- - policy
4920
- - verifiedAt
4921
- - verifiedVia
4922
4922
  - lastSeenAt
4923
4923
  - interactionCount
4924
4924
  - lastInteraction
4925
- - revokedReason
4926
- - blockedReason
4927
4925
  additionalProperties: false
4928
4926
  required:
4929
4927
  - id
4930
4928
  - displayName
4931
4929
  - role
4930
+ - contactType
4932
4931
  - interactionCount
4933
4932
  - createdAt
4934
4933
  - updatedAt
@@ -5002,14 +5001,18 @@ paths:
5002
5001
  type: string
5003
5002
  role:
5004
5003
  type: string
5004
+ enum:
5005
+ - guardian
5006
+ - contact
5005
5007
  notes:
5006
5008
  anyOf:
5007
5009
  - type: string
5008
5010
  - type: "null"
5009
5011
  contactType:
5010
- anyOf:
5011
- - type: string
5012
- - type: "null"
5012
+ type: string
5013
+ enum:
5014
+ - human
5015
+ - assistant
5013
5016
  lastInteraction:
5014
5017
  anyOf:
5015
5018
  - type: number
@@ -5076,20 +5079,15 @@ paths:
5076
5079
  - address
5077
5080
  - isPrimary
5078
5081
  - externalUserId
5079
- - status
5080
- - policy
5081
- - verifiedAt
5082
- - verifiedVia
5083
5082
  - lastSeenAt
5084
5083
  - interactionCount
5085
5084
  - lastInteraction
5086
- - revokedReason
5087
- - blockedReason
5088
5085
  additionalProperties: false
5089
5086
  required:
5090
5087
  - id
5091
5088
  - displayName
5092
5089
  - role
5090
+ - contactType
5093
5091
  - interactionCount
5094
5092
  - createdAt
5095
5093
  - updatedAt
@@ -5132,14 +5130,18 @@ paths:
5132
5130
  type: string
5133
5131
  role:
5134
5132
  type: string
5133
+ enum:
5134
+ - guardian
5135
+ - contact
5135
5136
  notes:
5136
5137
  anyOf:
5137
5138
  - type: string
5138
5139
  - type: "null"
5139
5140
  contactType:
5140
- anyOf:
5141
- - type: string
5142
- - type: "null"
5141
+ type: string
5142
+ enum:
5143
+ - human
5144
+ - assistant
5143
5145
  lastInteraction:
5144
5146
  anyOf:
5145
5147
  - type: number
@@ -5206,20 +5208,15 @@ paths:
5206
5208
  - address
5207
5209
  - isPrimary
5208
5210
  - externalUserId
5209
- - status
5210
- - policy
5211
- - verifiedAt
5212
- - verifiedVia
5213
5211
  - lastSeenAt
5214
5212
  - interactionCount
5215
5213
  - lastInteraction
5216
- - revokedReason
5217
- - blockedReason
5218
5214
  additionalProperties: false
5219
5215
  required:
5220
5216
  - id
5221
5217
  - displayName
5222
5218
  - role
5219
+ - contactType
5223
5220
  - interactionCount
5224
5221
  - createdAt
5225
5222
  - updatedAt
@@ -5507,14 +5504,18 @@ paths:
5507
5504
  type: string
5508
5505
  role:
5509
5506
  type: string
5507
+ enum:
5508
+ - guardian
5509
+ - contact
5510
5510
  notes:
5511
5511
  anyOf:
5512
5512
  - type: string
5513
5513
  - type: "null"
5514
5514
  contactType:
5515
- anyOf:
5516
- - type: string
5517
- - type: "null"
5515
+ type: string
5516
+ enum:
5517
+ - human
5518
+ - assistant
5518
5519
  lastInteraction:
5519
5520
  anyOf:
5520
5521
  - type: number
@@ -5581,20 +5582,15 @@ paths:
5581
5582
  - address
5582
5583
  - isPrimary
5583
5584
  - externalUserId
5584
- - status
5585
- - policy
5586
- - verifiedAt
5587
- - verifiedVia
5588
5585
  - lastSeenAt
5589
5586
  - interactionCount
5590
5587
  - lastInteraction
5591
- - revokedReason
5592
- - blockedReason
5593
5588
  additionalProperties: false
5594
5589
  required:
5595
5590
  - id
5596
5591
  - displayName
5597
5592
  - role
5593
+ - contactType
5598
5594
  - interactionCount
5599
5595
  - createdAt
5600
5596
  - updatedAt
@@ -5702,14 +5698,18 @@ paths:
5702
5698
  type: string
5703
5699
  role:
5704
5700
  type: string
5701
+ enum:
5702
+ - guardian
5703
+ - contact
5705
5704
  notes:
5706
5705
  anyOf:
5707
5706
  - type: string
5708
5707
  - type: "null"
5709
5708
  contactType:
5710
- anyOf:
5711
- - type: string
5712
- - type: "null"
5709
+ type: string
5710
+ enum:
5711
+ - human
5712
+ - assistant
5713
5713
  lastInteraction:
5714
5714
  anyOf:
5715
5715
  - type: number
@@ -5776,20 +5776,15 @@ paths:
5776
5776
  - address
5777
5777
  - isPrimary
5778
5778
  - externalUserId
5779
- - status
5780
- - policy
5781
- - verifiedAt
5782
- - verifiedVia
5783
5779
  - lastSeenAt
5784
5780
  - interactionCount
5785
5781
  - lastInteraction
5786
- - revokedReason
5787
- - blockedReason
5788
5782
  additionalProperties: false
5789
5783
  required:
5790
5784
  - id
5791
5785
  - displayName
5792
5786
  - role
5787
+ - contactType
5793
5788
  - interactionCount
5794
5789
  - createdAt
5795
5790
  - updatedAt
@@ -17431,6 +17426,30 @@ paths:
17431
17426
  webUrl:
17432
17427
  type: string
17433
17428
  additionalProperties: false
17429
+ eventKind:
17430
+ type: string
17431
+ enum:
17432
+ - message
17433
+ - reaction
17434
+ reaction:
17435
+ type: object
17436
+ properties:
17437
+ emoji:
17438
+ type: string
17439
+ op:
17440
+ type: string
17441
+ enum:
17442
+ - added
17443
+ - removed
17444
+ actorDisplayName:
17445
+ type: string
17446
+ targetChannelTs:
17447
+ type: string
17448
+ required:
17449
+ - emoji
17450
+ - op
17451
+ - targetChannelTs
17452
+ additionalProperties: false
17434
17453
  required:
17435
17454
  - channelId
17436
17455
  - channelTs
@@ -17467,6 +17486,14 @@ paths:
17467
17486
  anyOf:
17468
17487
  - type: number
17469
17488
  - type: "null"
17489
+ processing:
17490
+ description:
17491
+ "Whether the agent is currently mid-turn for this conversation, sourced authoritatively from the persisted
17492
+ `processing_started_at` column. `true` means a turn is in flight; `false` means the conversation
17493
+ is idle. Clients use this to recover from a dropped SSE stream: if a turn appears to be running
17494
+ locally but the server reports `processing: false`, the turn has ended (or died) and the UI should
17495
+ stop waiting rather than spin indefinitely. Absent on older daemons that predate this field."
17496
+ type: boolean
17470
17497
  required:
17471
17498
  - messages
17472
17499
  additionalProperties: false
@@ -29688,12 +29715,6 @@ components:
29688
29715
  - $ref: "#/components/schemas/ProfileStatus"
29689
29716
  - type: "null"
29690
29717
  - type: "null"
29691
- advisorEnabled:
29692
- anyOf:
29693
- - anyOf:
29694
- - type: boolean
29695
- - type: "null"
29696
- - type: "null"
29697
29718
  mix:
29698
29719
  anyOf:
29699
29720
  - minItems: 2
@@ -30421,10 +30442,6 @@ components:
30421
30442
  anyOf:
30422
30443
  - $ref: "#/components/schemas/ProfileStatus"
30423
30444
  - type: "null"
30424
- advisorEnabled:
30425
- anyOf:
30426
- - type: boolean
30427
- - type: "null"
30428
30445
  mix:
30429
30446
  minItems: 2
30430
30447
  type: array
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.10.3",
3
+ "version": "0.10.4-staging.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -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
  });
@@ -12,9 +12,7 @@ import {
12
12
  _resetStreamStateForTesting,
13
13
  _simulateRestartForTesting,
14
14
  getCurrentSeq,
15
- getPersistedSeq,
16
15
  getReplayWindow,
17
- recordPersistedSeq,
18
16
  stampAndBuffer,
19
17
  } from "../runtime/assistant-stream-state.js";
20
18
 
@@ -571,80 +569,9 @@ describe("assistant-stream-state", () => {
571
569
  });
572
570
  });
573
571
 
574
- describe("persisted seq", () => {
575
- test("getPersistedSeq is null for an unknown conversation", () => {
576
- expect(getPersistedSeq("conv_unknown")).toBeNull();
577
- });
578
-
579
- test("records and retrieves a per-conversation value", () => {
580
- recordPersistedSeq("conv_a", 7);
581
- expect(getPersistedSeq("conv_a")).toBe(7);
582
- expect(getPersistedSeq("conv_b")).toBeNull();
583
- });
584
-
585
- test("tracks conversations independently", () => {
586
- recordPersistedSeq("conv_a", 3);
587
- recordPersistedSeq("conv_b", 9);
588
- expect(getPersistedSeq("conv_a")).toBe(3);
589
- expect(getPersistedSeq("conv_b")).toBe(9);
590
- });
591
-
592
- test("advances monotonically and never regresses", () => {
593
- recordPersistedSeq("conv_a", 5);
594
- recordPersistedSeq("conv_a", 12);
595
- expect(getPersistedSeq("conv_a")).toBe(12);
596
-
597
- // A lower seq (e.g. an out-of-order async commit) is clamped.
598
- recordPersistedSeq("conv_a", 8);
599
- expect(getPersistedSeq("conv_a")).toBe(12);
600
- });
601
-
602
- test("ignores non-positive and non-finite seq values", () => {
603
- recordPersistedSeq("conv_a", 0);
604
- recordPersistedSeq("conv_a", -3);
605
- recordPersistedSeq("conv_a", Number.NaN);
606
- recordPersistedSeq("conv_a", Number.POSITIVE_INFINITY);
607
- expect(getPersistedSeq("conv_a")).toBeNull();
608
- });
609
-
610
- test("is cleared by reset", () => {
611
- recordPersistedSeq("conv_a", 4);
612
- _resetStreamStateForTesting();
613
- expect(getPersistedSeq("conv_a")).toBeNull();
614
- });
615
-
616
- test("evicts the least-recently-recorded conversation past the cap", () => {
617
- // The map is LRU-bounded at 1024 conversations. Fill to the cap,
618
- // then one more insert evicts the oldest key.
619
- const CAP = 1024;
620
- for (let i = 0; i < CAP; i++) {
621
- recordPersistedSeq(`conv_${i}`, i + 1);
622
- }
623
- // All present at the cap.
624
- expect(getPersistedSeq("conv_0")).toBe(1);
625
- expect(getPersistedSeq(`conv_${CAP - 1}`)).toBe(CAP);
626
-
627
- // One more distinct conversation evicts the oldest (conv_0).
628
- recordPersistedSeq("conv_overflow", 9999);
629
- expect(getPersistedSeq("conv_0")).toBeNull();
630
- expect(getPersistedSeq("conv_1")).toBe(2);
631
- expect(getPersistedSeq("conv_overflow")).toBe(9999);
632
- });
633
-
634
- test("re-recording refreshes recency so a kept key is not evicted first", () => {
635
- const CAP = 1024;
636
- for (let i = 0; i < CAP; i++) {
637
- recordPersistedSeq(`conv_${i}`, i + 1);
638
- }
639
- // Touch the oldest key so it moves to the most-recent end.
640
- recordPersistedSeq("conv_0", 5000);
641
-
642
- // The next insert now evicts conv_1 (the new oldest), not conv_0.
643
- recordPersistedSeq("conv_overflow", 9999);
644
- expect(getPersistedSeq("conv_0")).toBe(5000);
645
- expect(getPersistedSeq("conv_1")).toBeNull();
646
- });
647
- });
572
+ // Per-conversation persisted seq now lives on the `conversations.seq`
573
+ // column (see conversation-crud `getConversationPersistedSeq` /
574
+ // `recordConversationPersistedSeq`); its tests live with that module.
648
575
 
649
576
  describe("seq persistence across restarts", () => {
650
577
  test("counter resumes above the persisted reservation after a restart", () => {
@@ -100,8 +100,10 @@ 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
+ setConversationProcessingStartedAt: () => {},
104
+ isConversationProcessing: () => false,
105
+ recordConversationPersistedSeq: () => {},
106
+ getConversationPersistedSeq: () => null,
105
107
  addMessage: mock(() => ({ id: "msg-1" })),
106
108
  archiveConversation: mock(() => true),
107
109
  batchSetDisplayOrders: mock(() => {}),