@vellumai/assistant 0.10.3-dev.202606260318.2a238d5 → 0.10.3-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 (231) hide show
  1. package/openapi.yaml +55 -72
  2. package/package.json +1 -1
  3. package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +31 -83
  4. package/src/__tests__/assistant-stream-state.test.ts +76 -3
  5. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -4
  6. package/src/__tests__/channel-approval-routes.test.ts +26 -21
  7. package/src/__tests__/channel-delivery-store.test.ts +0 -28
  8. package/src/__tests__/channel-guardian.test.ts +32 -82
  9. package/src/__tests__/channel-inbound-disk-pressure.test.ts +19 -11
  10. package/src/__tests__/channel-reply-delivery.test.ts +2 -6
  11. package/src/__tests__/config-loader-backfill.test.ts +0 -148
  12. package/src/__tests__/contact-store-user-file.test.ts +10 -7
  13. package/src/__tests__/contacts-relay-reads.test.ts +9 -6
  14. package/src/__tests__/contacts-write.test.ts +2 -0
  15. package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -4
  16. package/src/__tests__/conversation-agent-loop.test.ts +7 -98
  17. package/src/__tests__/conversation-attention-telegram.test.ts +11 -9
  18. package/src/__tests__/conversation-error.test.ts +0 -18
  19. package/src/__tests__/conversation-fork-crud.test.ts +24 -354
  20. package/src/__tests__/delete-propagation.test.ts +3 -5
  21. package/src/__tests__/dm-backfill.test.ts +4 -6
  22. package/src/__tests__/emit-signal-routing-intent.test.ts +6 -2
  23. package/src/__tests__/guardian-binding-drift-heal.test.ts +23 -43
  24. package/src/__tests__/guardian-dispatch.test.ts +5 -50
  25. package/src/__tests__/guardian-routing-state.test.ts +10 -6
  26. package/src/__tests__/helpers/channel-test-adapter.ts +12 -45
  27. package/src/__tests__/helpers/create-guardian-binding.ts +23 -15
  28. package/src/__tests__/helpers/mock-logger.ts +0 -1
  29. package/src/__tests__/inbound-invite-redemption.test.ts +10 -87
  30. package/src/__tests__/invite-redemption-service.test.ts +53 -273
  31. package/src/__tests__/invite-routes-http.test.ts +0 -34
  32. package/src/__tests__/invite-service-ipc.test.ts +2 -65
  33. package/src/__tests__/list-messages-page-latest.test.ts +4 -173
  34. package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -2
  35. package/src/__tests__/non-member-access-request.test.ts +13 -15
  36. package/src/__tests__/onboarding-persona-write.test.ts +22 -52
  37. package/src/__tests__/persist-onboarding-artifacts.test.ts +0 -1
  38. package/src/__tests__/persona-resolver.test.ts +45 -75
  39. package/src/__tests__/plugin-bootstrap.test.ts +5 -13
  40. package/src/__tests__/provider-usage-tracking.test.ts +1 -1
  41. package/src/__tests__/reaction-persistence.test.ts +4 -51
  42. package/src/__tests__/relay-server.test.ts +31 -88
  43. package/src/__tests__/runtime-attachment-metadata.test.ts +11 -9
  44. package/src/__tests__/settings-routes.test.ts +0 -32
  45. package/src/__tests__/slack-block-formatting.test.ts +38 -1
  46. package/src/__tests__/sse-actor-principal-guardian-source.test.ts +36 -13
  47. package/src/__tests__/stt-hints.test.ts +3 -6
  48. package/src/__tests__/subagent-fork-spawn.test.ts +7 -6
  49. package/src/__tests__/subagent-role-registry.test.ts +4 -17
  50. package/src/__tests__/subagent-tools.test.ts +3 -398
  51. package/src/__tests__/thread-backfill.test.ts +3 -3
  52. package/src/__tests__/tool-preview-lifecycle.test.ts +10 -26
  53. package/src/__tests__/tool-start-timestamp.test.ts +3 -4
  54. package/src/__tests__/trusted-contact-approval-notifier.test.ts +51 -37
  55. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -2
  56. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +7 -9
  57. package/src/__tests__/trusted-contact-multichannel.test.ts +7 -16
  58. package/src/__tests__/trusted-contact-verification.test.ts +54 -79
  59. package/src/__tests__/voice-invite-redemption.test.ts +20 -183
  60. package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
  61. package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +2 -2
  62. package/src/__tests__/workspace-migration-drop-user-md.test.ts +238 -196
  63. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +47 -35
  64. package/src/agent/loop-exclusive-tool.test.ts +15 -19
  65. package/src/agent/loop.ts +1 -108
  66. package/src/api/responses/conversation-message.ts +0 -9
  67. package/src/approvals/guardian-request-resolvers.ts +4 -16
  68. package/src/calls/__tests__/relay-setup-router.test.ts +18 -10
  69. package/src/calls/guardian-dispatch.ts +11 -14
  70. package/src/calls/inbound-trust-reader.ts +1 -7
  71. package/src/calls/relay-access-wait.ts +6 -6
  72. package/src/calls/relay-server.ts +2 -22
  73. package/src/calls/relay-setup-router.ts +10 -10
  74. package/src/cli/commands/__tests__/conversations-slack.test.ts +0 -1
  75. package/src/cli/commands/contacts.ts +7 -10
  76. package/src/cli/commands/memory/__tests__/worker.test.ts +17 -147
  77. package/src/cli/commands/memory/worker.ts +30 -97
  78. package/src/cli/commands/plugins.ts +146 -3
  79. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +17 -17
  80. package/src/cli/lib/__tests__/publish-plugin.test.ts +0 -98
  81. package/src/cli/lib/publish-plugin.ts +1 -231
  82. package/src/config/__tests__/sync-gated-profiles.test.ts +7 -5
  83. package/src/config/bundled-skills/subagent/SKILL.md +1 -16
  84. package/src/config/bundled-skills/subagent/TOOLS.json +4 -5
  85. package/src/config/call-site-defaults.ts +6 -0
  86. package/src/config/llm-resolver.ts +3 -0
  87. package/src/config/schemas/call-site-catalog.ts +7 -0
  88. package/src/config/schemas/heartbeat.ts +5 -2
  89. package/src/config/schemas/llm.ts +12 -3
  90. package/src/config/schemas/memory-lifecycle.ts +1 -1
  91. package/src/config/seed-inference-profiles.ts +36 -77
  92. package/src/config/sync-gated-profiles.ts +3 -0
  93. package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +8 -7
  94. package/src/contacts/__tests__/member-write-relay.test.ts +11 -35
  95. package/src/contacts/contact-store.ts +237 -27
  96. package/src/contacts/contacts-write.ts +58 -18
  97. package/src/contacts/member-write-relay.ts +31 -25
  98. package/src/contacts/types.ts +15 -3
  99. package/src/daemon/__tests__/conversation-tool-setup.test.ts +44 -0
  100. package/src/daemon/conversation-agent-loop-handlers.ts +10 -29
  101. package/src/daemon/conversation-agent-loop.ts +61 -68
  102. package/src/daemon/conversation-error.ts +10 -7
  103. package/src/daemon/conversation-tool-setup.ts +10 -0
  104. package/src/daemon/conversation.ts +0 -10
  105. package/src/daemon/external-plugins-bootstrap.ts +2 -8
  106. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +1 -0
  107. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +2 -0
  108. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +2 -0
  109. package/src/daemon/handlers/__tests__/config-channels.test.ts +14 -9
  110. package/src/daemon/handlers/config-channels.ts +29 -14
  111. package/src/daemon/lifecycle.ts +4 -16
  112. package/src/daemon/message-types/surfaces.ts +0 -2
  113. package/src/heartbeat/heartbeat-service.ts +0 -5
  114. package/src/home/relationship-state-writer.ts +0 -5
  115. package/src/memory/conversation-crud.ts +61 -136
  116. package/src/memory/jobs-worker.ts +29 -75
  117. package/src/memory/memory-retrospective-job.ts +0 -5
  118. package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +5 -27
  119. package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +6 -79
  120. package/src/memory/schema/contacts.ts +2 -6
  121. package/src/memory/schema/conversations.ts +0 -39
  122. package/src/memory/steps.ts +367 -1090
  123. package/src/memory/worker-control.ts +18 -104
  124. package/src/memory/worker-process.ts +0 -17
  125. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +0 -11
  126. package/src/messaging/providers/a2a/deliver.ts +1 -5
  127. package/src/messaging/providers/index.ts +241 -65
  128. package/src/notifications/__tests__/broadcaster.test.ts +8 -0
  129. package/src/notifications/__tests__/connected-channels.test.ts +36 -8
  130. package/src/notifications/__tests__/destination-resolver.test.ts +117 -12
  131. package/src/notifications/destination-resolver.ts +23 -7
  132. package/src/notifications/emit-signal.ts +11 -5
  133. package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
  134. package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
  135. package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
  136. package/src/plugins/defaults/advisor/__tests__/consult.test.ts +314 -0
  137. package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +106 -0
  138. package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +60 -0
  139. package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
  140. package/src/{subagent/__tests__/consult-transcript.test.ts → plugins/defaults/advisor/__tests__/transcript.test.ts} +10 -47
  141. package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
  142. package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
  143. package/src/plugins/defaults/advisor/config.ts +21 -0
  144. package/src/plugins/defaults/advisor/consult.ts +197 -0
  145. package/src/plugins/defaults/advisor/context-pack.ts +288 -0
  146. package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
  147. package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
  148. package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
  149. package/src/plugins/defaults/advisor/package.json +14 -0
  150. package/src/{subagent/consult-prompt.ts → plugins/defaults/advisor/steering.ts} +39 -17
  151. package/src/plugins/defaults/advisor/tools/advisor.ts +92 -0
  152. package/src/{subagent/consult-transcript.ts → plugins/defaults/advisor/transcript.ts} +8 -18
  153. package/src/plugins/defaults/index.ts +35 -0
  154. package/src/plugins/registry.ts +12 -55
  155. package/src/prompts/persona-resolver.ts +11 -43
  156. package/src/providers/call-site-routing.ts +0 -41
  157. package/src/providers/provider-send-message.ts +0 -6
  158. package/src/providers/ratelimit.ts +0 -6
  159. package/src/providers/registry.ts +1 -1
  160. package/src/providers/retry.ts +0 -6
  161. package/src/providers/types.ts +0 -13
  162. package/src/providers/usage-tracking.ts +0 -6
  163. package/src/runtime/__tests__/guardian-vellum-migration.test.ts +27 -30
  164. package/src/runtime/__tests__/local-principal-trust.test.ts +18 -16
  165. package/src/runtime/__tests__/trust-verdict-consumer.test.ts +168 -115
  166. package/src/runtime/access-request-helper.ts +2 -1
  167. package/src/runtime/actor-trust-resolver.ts +17 -44
  168. package/src/runtime/anchored-guardian.test.ts +54 -7
  169. package/src/runtime/anchored-guardian.ts +53 -4
  170. package/src/runtime/assistant-stream-state.ts +74 -12
  171. package/src/runtime/channel-reply-delivery.ts +8 -3
  172. package/src/runtime/guardian-vellum-migration.ts +16 -18
  173. package/src/runtime/invite-redemption-service.ts +10 -25
  174. package/src/runtime/local-actor-identity.ts +20 -27
  175. package/src/runtime/routes/__tests__/contact-routes.test.ts +7 -100
  176. package/src/runtime/routes/__tests__/global-search-routes.test.ts +2 -1
  177. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +1 -2
  178. package/src/runtime/routes/contact-routes.ts +25 -40
  179. package/src/runtime/routes/conversation-list-routes.ts +29 -1
  180. package/src/runtime/routes/conversation-routes.ts +7 -27
  181. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +10 -0
  182. package/src/runtime/routes/inbound-stages/background-dispatch.ts +8 -4
  183. package/src/runtime/routes/inbound-stages/reaction-intercept.ts +0 -19
  184. package/src/runtime/routes/settings-routes.ts +3 -8
  185. package/src/runtime/services/conversation-serializer.ts +49 -6
  186. package/src/runtime/slack-block-formatting.ts +15 -0
  187. package/src/runtime/trust-verdict-consumer.ts +41 -36
  188. package/src/subagent/index.ts +1 -1
  189. package/src/subagent/manager.ts +33 -245
  190. package/src/subagent/types.ts +1 -8
  191. package/src/tools/registry.ts +3 -10
  192. package/src/tools/subagent/spawn.ts +5 -234
  193. package/src/util/logger.ts +0 -9
  194. package/src/util/platform.ts +0 -14
  195. package/src/workspace/migrations/031-drop-user-md.ts +148 -232
  196. package/src/workspace/migrations/registry.ts +0 -2
  197. package/src/__tests__/compaction-ledger-store.test.ts +0 -128
  198. package/src/__tests__/consult-deadline.test.ts +0 -60
  199. package/src/__tests__/contact-store-interaction-info.test.ts +0 -156
  200. package/src/__tests__/db-compaction-events-migration.test.ts +0 -129
  201. package/src/__tests__/helpers/seed-contact-channel.ts +0 -96
  202. package/src/__tests__/plugin-disabled-state.test.ts +0 -190
  203. package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +0 -135
  204. package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +0 -158
  205. package/src/__tests__/subagent-fork-prompt-role.test.ts +0 -195
  206. package/src/__tests__/subagent-spawn-and-await.test.ts +0 -546
  207. package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +0 -137
  208. package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +0 -170
  209. package/src/agent/loop-native-web-search.test.ts +0 -200
  210. package/src/contacts/gateway-channel-read.ts +0 -51
  211. package/src/memory/compaction-ledger-store.ts +0 -107
  212. package/src/memory/migrations/302-create-compaction-events.ts +0 -107
  213. package/src/memory/migrations/303-add-conversation-creation-seq.ts +0 -33
  214. package/src/messaging/channel-binding-metadata.ts +0 -31
  215. package/src/messaging/channel-binding-schema.ts +0 -51
  216. package/src/messaging/providers/__tests__/callback-routing.test.ts +0 -45
  217. package/src/messaging/providers/__tests__/transport-dispatch.test.ts +0 -195
  218. package/src/messaging/providers/a2a/transport.ts +0 -10
  219. package/src/messaging/providers/callback-routing.ts +0 -48
  220. package/src/messaging/providers/channel-transport.ts +0 -55
  221. package/src/messaging/providers/slack/binding-metadata.ts +0 -62
  222. package/src/messaging/providers/slack/transport.ts +0 -92
  223. package/src/messaging/providers/telegram-bot/transport.ts +0 -51
  224. package/src/messaging/providers/whatsapp/transport.ts +0 -38
  225. package/src/plugins/disabled-state.ts +0 -31
  226. package/src/runtime/__tests__/member-verdict-cache.test.ts +0 -119
  227. package/src/runtime/local-actor-identity.test.ts +0 -108
  228. package/src/runtime/member-verdict-cache.ts +0 -0
  229. package/src/subagent/__tests__/consult-prompt.test.ts +0 -35
  230. package/src/tools/subagent/consult-deadline.ts +0 -49
  231. package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +0 -64
package/openapi.yaml CHANGED
@@ -4841,18 +4841,14 @@ paths:
4841
4841
  type: string
4842
4842
  role:
4843
4843
  type: string
4844
- enum:
4845
- - guardian
4846
- - contact
4847
4844
  notes:
4848
4845
  anyOf:
4849
4846
  - type: string
4850
4847
  - type: "null"
4851
4848
  contactType:
4852
- type: string
4853
- enum:
4854
- - human
4855
- - assistant
4849
+ anyOf:
4850
+ - type: string
4851
+ - type: "null"
4856
4852
  lastInteraction:
4857
4853
  anyOf:
4858
4854
  - type: number
@@ -4919,15 +4915,20 @@ paths:
4919
4915
  - address
4920
4916
  - isPrimary
4921
4917
  - externalUserId
4918
+ - status
4919
+ - policy
4920
+ - verifiedAt
4921
+ - verifiedVia
4922
4922
  - lastSeenAt
4923
4923
  - interactionCount
4924
4924
  - lastInteraction
4925
+ - revokedReason
4926
+ - blockedReason
4925
4927
  additionalProperties: false
4926
4928
  required:
4927
4929
  - id
4928
4930
  - displayName
4929
4931
  - role
4930
- - contactType
4931
4932
  - interactionCount
4932
4933
  - createdAt
4933
4934
  - updatedAt
@@ -5001,18 +5002,14 @@ paths:
5001
5002
  type: string
5002
5003
  role:
5003
5004
  type: string
5004
- enum:
5005
- - guardian
5006
- - contact
5007
5005
  notes:
5008
5006
  anyOf:
5009
5007
  - type: string
5010
5008
  - type: "null"
5011
5009
  contactType:
5012
- type: string
5013
- enum:
5014
- - human
5015
- - assistant
5010
+ anyOf:
5011
+ - type: string
5012
+ - type: "null"
5016
5013
  lastInteraction:
5017
5014
  anyOf:
5018
5015
  - type: number
@@ -5079,15 +5076,20 @@ paths:
5079
5076
  - address
5080
5077
  - isPrimary
5081
5078
  - externalUserId
5079
+ - status
5080
+ - policy
5081
+ - verifiedAt
5082
+ - verifiedVia
5082
5083
  - lastSeenAt
5083
5084
  - interactionCount
5084
5085
  - lastInteraction
5086
+ - revokedReason
5087
+ - blockedReason
5085
5088
  additionalProperties: false
5086
5089
  required:
5087
5090
  - id
5088
5091
  - displayName
5089
5092
  - role
5090
- - contactType
5091
5093
  - interactionCount
5092
5094
  - createdAt
5093
5095
  - updatedAt
@@ -5130,18 +5132,14 @@ paths:
5130
5132
  type: string
5131
5133
  role:
5132
5134
  type: string
5133
- enum:
5134
- - guardian
5135
- - contact
5136
5135
  notes:
5137
5136
  anyOf:
5138
5137
  - type: string
5139
5138
  - type: "null"
5140
5139
  contactType:
5141
- type: string
5142
- enum:
5143
- - human
5144
- - assistant
5140
+ anyOf:
5141
+ - type: string
5142
+ - type: "null"
5145
5143
  lastInteraction:
5146
5144
  anyOf:
5147
5145
  - type: number
@@ -5208,15 +5206,20 @@ paths:
5208
5206
  - address
5209
5207
  - isPrimary
5210
5208
  - externalUserId
5209
+ - status
5210
+ - policy
5211
+ - verifiedAt
5212
+ - verifiedVia
5211
5213
  - lastSeenAt
5212
5214
  - interactionCount
5213
5215
  - lastInteraction
5216
+ - revokedReason
5217
+ - blockedReason
5214
5218
  additionalProperties: false
5215
5219
  required:
5216
5220
  - id
5217
5221
  - displayName
5218
5222
  - role
5219
- - contactType
5220
5223
  - interactionCount
5221
5224
  - createdAt
5222
5225
  - updatedAt
@@ -5504,18 +5507,14 @@ paths:
5504
5507
  type: string
5505
5508
  role:
5506
5509
  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
- type: string
5516
- enum:
5517
- - human
5518
- - assistant
5515
+ anyOf:
5516
+ - type: string
5517
+ - type: "null"
5519
5518
  lastInteraction:
5520
5519
  anyOf:
5521
5520
  - type: number
@@ -5582,15 +5581,20 @@ paths:
5582
5581
  - address
5583
5582
  - isPrimary
5584
5583
  - externalUserId
5584
+ - status
5585
+ - policy
5586
+ - verifiedAt
5587
+ - verifiedVia
5585
5588
  - lastSeenAt
5586
5589
  - interactionCount
5587
5590
  - lastInteraction
5591
+ - revokedReason
5592
+ - blockedReason
5588
5593
  additionalProperties: false
5589
5594
  required:
5590
5595
  - id
5591
5596
  - displayName
5592
5597
  - role
5593
- - contactType
5594
5598
  - interactionCount
5595
5599
  - createdAt
5596
5600
  - updatedAt
@@ -5698,18 +5702,14 @@ paths:
5698
5702
  type: string
5699
5703
  role:
5700
5704
  type: string
5701
- enum:
5702
- - guardian
5703
- - contact
5704
5705
  notes:
5705
5706
  anyOf:
5706
5707
  - type: string
5707
5708
  - type: "null"
5708
5709
  contactType:
5709
- type: string
5710
- enum:
5711
- - human
5712
- - assistant
5710
+ anyOf:
5711
+ - type: string
5712
+ - type: "null"
5713
5713
  lastInteraction:
5714
5714
  anyOf:
5715
5715
  - type: number
@@ -5776,15 +5776,20 @@ paths:
5776
5776
  - address
5777
5777
  - isPrimary
5778
5778
  - externalUserId
5779
+ - status
5780
+ - policy
5781
+ - verifiedAt
5782
+ - verifiedVia
5779
5783
  - lastSeenAt
5780
5784
  - interactionCount
5781
5785
  - lastInteraction
5786
+ - revokedReason
5787
+ - blockedReason
5782
5788
  additionalProperties: false
5783
5789
  required:
5784
5790
  - id
5785
5791
  - displayName
5786
5792
  - role
5787
- - contactType
5788
5793
  - interactionCount
5789
5794
  - createdAt
5790
5795
  - updatedAt
@@ -17426,30 +17431,6 @@ paths:
17426
17431
  webUrl:
17427
17432
  type: string
17428
17433
  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
17453
17434
  required:
17454
17435
  - channelId
17455
17436
  - channelTs
@@ -17486,14 +17467,6 @@ paths:
17486
17467
  anyOf:
17487
17468
  - type: number
17488
17469
  - 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
17497
17470
  required:
17498
17471
  - messages
17499
17472
  additionalProperties: false
@@ -29715,6 +29688,12 @@ components:
29715
29688
  - $ref: "#/components/schemas/ProfileStatus"
29716
29689
  - type: "null"
29717
29690
  - type: "null"
29691
+ advisorEnabled:
29692
+ anyOf:
29693
+ - anyOf:
29694
+ - type: boolean
29695
+ - type: "null"
29696
+ - type: "null"
29718
29697
  mix:
29719
29698
  anyOf:
29720
29699
  - minItems: 2
@@ -30442,6 +30421,10 @@ components:
30442
30421
  anyOf:
30443
30422
  - $ref: "#/components/schemas/ProfileStatus"
30444
30423
  - type: "null"
30424
+ advisorEnabled:
30425
+ anyOf:
30426
+ - type: boolean
30427
+ - type: "null"
30445
30428
  mix:
30446
30429
  minItems: 2
30447
30430
  type: array
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.10.3-dev.202606260318.2a238d5",
3
+ "version": "0.10.3-staging.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "exports": {
@@ -7,18 +7,12 @@
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 ACL status so relay-setup-router
10
+ * `memberRecord` with the right channel/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
-
22
16
  // ── Logger mock (suppress output) ───────────────────────────────────────────
23
17
  mock.module("../util/logger.js", () => ({
24
18
  getLogger: () =>
@@ -29,45 +23,18 @@ mock.module("../util/logger.js", () => ({
29
23
  let _byAddress: ReturnType<
30
24
  (typeof import("../contacts/contact-store.js"))["findContactByAddress"]
31
25
  > = 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" };
26
+ let _guardian: ReturnType<
27
+ (typeof import("../contacts/contact-store.js"))["findGuardianForChannel"]
28
+ > = null;
36
29
 
37
30
  mock.module("../contacts/contact-store.js", () => ({
38
31
  findContactByAddress: (_type: string, _addr: string) => _byAddress,
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,
32
+ findGuardianForChannel: (_channel: string) => _guardian,
46
33
  }));
47
34
 
48
35
  // ── Real import after mocks ───────────────────────────────────────────────────
49
36
  import type { ContactWithChannels } from "../contacts/types.js";
50
37
  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
- }
71
38
 
72
39
  // ── Helpers ──────────────────────────────────────────────────────────────────
73
40
 
@@ -77,14 +44,13 @@ function makeContact(
77
44
  role: "guardian" | "contact" = "contact",
78
45
  status: "unverified" | "active" = "unverified",
79
46
  ): ContactWithChannels {
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 };
47
+ const channelId = "ch-test";
83
48
  return {
84
- id: CONTACT_ID,
49
+ id: "contact-test",
85
50
  displayName: "Patrick Test",
86
- notes: null,
87
51
  role,
52
+ principalId: null,
53
+ notes: null,
88
54
  lastInteraction: null,
89
55
  interactionCount: 0,
90
56
  contactType: "human" as const,
@@ -93,16 +59,22 @@ function makeContact(
93
59
  updatedAt: 0,
94
60
  channels: [
95
61
  {
96
- id: CHANNEL_ID,
97
- contactId: CONTACT_ID,
62
+ id: channelId,
63
+ contactId: "contact-test",
98
64
  type: "phone",
99
65
  address: PHONE,
100
66
  externalChatId: null,
101
67
  isPrimary: true,
102
- inviteId: null,
103
- lastSeenAt: null,
68
+ status,
69
+ policy: "allow",
70
+ verifiedAt: null,
71
+ verifiedVia: null,
72
+ revokedReason: null,
73
+ blockedReason: null,
104
74
  interactionCount: 0,
105
75
  lastInteraction: null,
76
+ lastSeenAt: null,
77
+ inviteId: null,
106
78
  createdAt: 0,
107
79
  updatedAt: 0,
108
80
  },
@@ -115,14 +87,12 @@ function makeContact(
115
87
  describe("resolveActorTrust — address fallback", () => {
116
88
  beforeEach(() => {
117
89
  _byAddress = null;
118
- _acl = { status: "unverified", policy: "allow", role: "contact" };
119
- __resetMemberVerdictCacheForTest();
90
+ _guardian = null;
120
91
  });
121
92
 
122
93
  test("finds unverified channel via address when externalUserId is null", () => {
123
94
  // Simulate a contact registered by name-capture: address set, externalUserId null.
124
95
  _byAddress = makeContact("contact", "unverified");
125
- seedAcl();
126
96
 
127
97
  const result = resolveActorTrust({
128
98
  assistantId: "asst-1",
@@ -133,7 +103,7 @@ describe("resolveActorTrust — address fallback", () => {
133
103
 
134
104
  expect(result.memberRecord).not.toBeNull();
135
105
  expect(result.memberRecord?.contact.displayName).toBe("Patrick Test");
136
- expect(result.memberRecord?.status).toBe("unverified");
106
+ expect(result.memberRecord?.channel.status).toBe("unverified");
137
107
  // trustClass is 'unverified_contact' for a member whose channel is
138
108
  // pending or unverified — known to the guardian but not yet verified.
139
109
  expect(result.trustClass).toBe("unverified_contact");
@@ -141,7 +111,6 @@ describe("resolveActorTrust — address fallback", () => {
141
111
 
142
112
  test("address lookup is the sole member resolution path", () => {
143
113
  _byAddress = makeContact("contact", "active");
144
- seedAcl();
145
114
 
146
115
  const result = resolveActorTrust({
147
116
  assistantId: "asst-1",
@@ -150,7 +119,7 @@ describe("resolveActorTrust — address fallback", () => {
150
119
  actorExternalId: PHONE,
151
120
  });
152
121
 
153
- expect(result.memberRecord?.status).toBe("active");
122
+ expect(result.memberRecord?.channel.status).toBe("active");
154
123
  expect(result.memberRecord?.channel.address).toBe(PHONE);
155
124
  });
156
125
 
@@ -168,28 +137,10 @@ describe("resolveActorTrust — address fallback", () => {
168
137
  expect(result.trustClass).toBe("unknown");
169
138
  });
170
139
 
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
-
188
140
  test("address-found active channel elevates trust to trusted_contact", () => {
189
141
  // An active channel found via address (e.g. after manual verify without externalUserId set)
190
142
  // should still yield trusted_contact trust class.
191
143
  _byAddress = makeContact("contact", "active");
192
- seedAcl();
193
144
 
194
145
  const result = resolveActorTrust({
195
146
  assistantId: "asst-1",
@@ -199,7 +150,7 @@ describe("resolveActorTrust — address fallback", () => {
199
150
  });
200
151
 
201
152
  expect(result.memberRecord).not.toBeNull();
202
- expect(result.memberRecord?.status).toBe("active");
153
+ expect(result.memberRecord?.channel.status).toBe("active");
203
154
  expect(result.trustClass).toBe("trusted_contact");
204
155
  });
205
156
 
@@ -207,10 +158,9 @@ describe("resolveActorTrust — address fallback", () => {
207
158
  // Mirrors the unverified branch but for `pending` status (e.g. a phone
208
159
  // contact registered by name-capture awaiting the DTMF challenge).
209
160
  const contact = makeContact("contact", "unverified");
210
- // Override ACL status to "pending" — makeContact only accepts unverified/active.
211
- _acl = { ..._acl, status: "pending" };
161
+ // Override status to "pending" — makeContact only accepts unverified/active
162
+ contact.channels[0]!.status = "pending";
212
163
  _byAddress = contact;
213
- seedAcl();
214
164
 
215
165
  const result = resolveActorTrust({
216
166
  assistantId: "asst-1",
@@ -219,17 +169,16 @@ describe("resolveActorTrust — address fallback", () => {
219
169
  actorExternalId: PHONE,
220
170
  });
221
171
 
222
- expect(result.memberRecord?.status).toBe("pending");
172
+ expect(result.memberRecord?.channel.status).toBe("pending");
223
173
  expect(result.trustClass).toBe("unverified_contact");
224
174
  });
225
175
 
226
176
  test("blocked-status member is classified as unknown (not unverified_contact)", () => {
227
177
  // Hard-deny statuses (blocked, revoked) stay `unknown` — admission-layer
228
- // re-checks channel status and emits the hard-deny reasons.
178
+ // re-checks channel.status and emits the hard-deny reasons.
229
179
  const contact = makeContact("contact", "unverified");
230
- _acl = { ..._acl, status: "blocked" };
180
+ contact.channels[0]!.status = "blocked";
231
181
  _byAddress = contact;
232
- seedAcl();
233
182
 
234
183
  const result = resolveActorTrust({
235
184
  assistantId: "asst-1",
@@ -238,15 +187,14 @@ describe("resolveActorTrust — address fallback", () => {
238
187
  actorExternalId: PHONE,
239
188
  });
240
189
 
241
- expect(result.memberRecord?.status).toBe("blocked");
190
+ expect(result.memberRecord?.channel.status).toBe("blocked");
242
191
  expect(result.trustClass).toBe("unknown");
243
192
  });
244
193
 
245
194
  test("revoked-status member is classified as unknown", () => {
246
195
  const contact = makeContact("contact", "unverified");
247
- _acl = { ..._acl, status: "revoked" };
196
+ contact.channels[0]!.status = "revoked";
248
197
  _byAddress = contact;
249
- seedAcl();
250
198
 
251
199
  const result = resolveActorTrust({
252
200
  assistantId: "asst-1",
@@ -255,7 +203,7 @@ describe("resolveActorTrust — address fallback", () => {
255
203
  actorExternalId: PHONE,
256
204
  });
257
205
 
258
- expect(result.memberRecord?.status).toBe("revoked");
206
+ expect(result.memberRecord?.channel.status).toBe("revoked");
259
207
  expect(result.trustClass).toBe("unknown");
260
208
  });
261
209
  });
@@ -12,7 +12,9 @@ import {
12
12
  _resetStreamStateForTesting,
13
13
  _simulateRestartForTesting,
14
14
  getCurrentSeq,
15
+ getPersistedSeq,
15
16
  getReplayWindow,
17
+ recordPersistedSeq,
16
18
  stampAndBuffer,
17
19
  } from "../runtime/assistant-stream-state.js";
18
20
 
@@ -569,9 +571,80 @@ describe("assistant-stream-state", () => {
569
571
  });
570
572
  });
571
573
 
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.
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
+ });
575
648
 
576
649
  describe("seq persistence across restarts", () => {
577
650
  test("counter resumes above the persisted reservation after a restart", () => {
@@ -100,10 +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,
105
- recordConversationPersistedSeq: () => {},
106
- getConversationPersistedSeq: () => null,
103
+ setConversationProcessingStartedAt: () => {},
104
+ isConversationProcessing: () => false,
107
105
  addMessage: mock(() => ({ id: "msg-1" })),
108
106
  archiveConversation: mock(() => true),
109
107
  batchSetDisplayOrders: mock(() => {}),