@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
@@ -1,11 +1,9 @@
1
1
  /**
2
2
  * Tests for getConnectedChannels connectivity resolution.
3
3
  *
4
- * Connectivity must mirror destination-resolver's `resolveGuardian`:
5
- * gateway-first, with a LOCAL contacts fallback on ANY per-channel no-match
6
- * (gateway list null OR no active gateway entry for the channel). This keeps a
7
- * channel from being marked connected when it can't be delivered (and
8
- * vice-versa).
4
+ * Connectivity mirrors destination-resolver's `resolveGuardian`: guardian
5
+ * delivery is sourced solely from the gateway. A channel with no active gateway
6
+ * binding is not connected, keeping connectivity aligned with deliverability.
9
7
  */
10
8
 
11
9
  import { beforeEach, describe, expect, mock, test } from "bun:test";
@@ -20,7 +18,6 @@ mock.module("../../util/logger.js", () => ({
20
18
 
21
19
  let deliverableChannels: string[] = [];
22
20
  let gatewayGuardians: GuardianDelivery[] | null = null;
23
- let localChatId: string | null = null;
24
21
 
25
22
  const realConfig = await import("../../channels/config.js");
26
23
 
@@ -36,16 +33,6 @@ mock.module("../../contacts/guardian-delivery-reader.js", () => ({
36
33
  getGuardianDelivery: async () => gatewayGuardians,
37
34
  }));
38
35
 
39
- const realContactStore = await import("../../contacts/contact-store.js");
40
-
41
- mock.module("../../contacts/contact-store.js", () => ({
42
- ...realContactStore,
43
- findGuardianForChannel: (_channelType: string) =>
44
- localChatId === null
45
- ? null
46
- : { contact: { principalId: "p1" }, channel: { externalChatId: localChatId } },
47
- }));
48
-
49
36
  const { getConnectedChannels } = await import("../emit-signal.js");
50
37
 
51
38
  function gatewayBinding(channelType: string, externalChatId: string): GuardianDelivery {
@@ -55,43 +42,28 @@ function gatewayBinding(channelType: string, externalChatId: string): GuardianDe
55
42
  beforeEach(() => {
56
43
  deliverableChannels = [];
57
44
  gatewayGuardians = null;
58
- localChatId = null;
59
45
  });
60
46
 
61
- describe("getConnectedChannels gateway-first-then-local connectivity", () => {
62
- test("marks telegram connected from a gateway-only binding", async () => {
47
+ describe("getConnectedChannels gateway connectivity", () => {
48
+ test("marks telegram connected from a gateway binding", async () => {
63
49
  deliverableChannels = ["telegram"];
64
50
  gatewayGuardians = [gatewayBinding("telegram", "123")];
65
- localChatId = null;
66
51
 
67
52
  expect(await getConnectedChannels()).toContain("telegram");
68
53
  });
69
54
 
70
- test("falls back to a local binding when the gateway is unreachable (null)", async () => {
55
+ test("marks telegram disconnected when the gateway is unreachable (null)", async () => {
71
56
  deliverableChannels = ["telegram"];
72
57
  gatewayGuardians = null;
73
- localChatId = "456";
74
-
75
- expect(await getConnectedChannels()).toContain("telegram");
76
- });
77
-
78
- test("marks telegram disconnected when neither source has a binding", async () => {
79
- deliverableChannels = ["telegram"];
80
- gatewayGuardians = null;
81
- localChatId = null;
82
58
 
83
59
  expect(await getConnectedChannels()).not.toContain("telegram");
84
60
  });
85
61
 
86
- test("falls back to local when the gateway responds without that channel", async () => {
87
- // Gateway present but with no active telegram entry ⇒ per-channel no-match,
88
- // so connectivity falls back to the local mirror (mirrors
89
- // destination-resolver's per-channel fallback).
62
+ test("marks telegram disconnected when the gateway has no binding", async () => {
90
63
  deliverableChannels = ["telegram"];
91
64
  gatewayGuardians = [];
92
- localChatId = "789";
93
65
 
94
- expect(await getConnectedChannels()).toContain("telegram");
66
+ expect(await getConnectedChannels()).not.toContain("telegram");
95
67
  });
96
68
 
97
69
  test("only marks slack connected for D-prefixed (DM) chat IDs", async () => {
@@ -1,10 +1,10 @@
1
1
  /**
2
2
  * Verifies resolveDestinations resolves guardian delivery endpoints from the
3
- * gateway-provided guardian list, with shapes identical to the local read, and
4
- * falls back to the local contacts read when the list is null.
3
+ * gateway-provided guardian list, and omits a channel when the list is
4
+ * null/empty or carries no entry for it.
5
5
  */
6
6
 
7
- import { beforeEach, describe, expect, mock, test } from "bun:test";
7
+ import { describe, expect, mock, test } from "bun:test";
8
8
 
9
9
  import type { GuardianDelivery } from "@vellumai/gateway-client";
10
10
 
@@ -15,15 +15,6 @@ mock.module("../../util/logger.js", () => ({
15
15
  }),
16
16
  }));
17
17
 
18
- // Local fallback read; mocked so the null-list path is deterministic.
19
- let localGuardian:
20
- | { contact: { principalId?: string }; channel: { address: string; externalChatId?: string } }
21
- | null = null;
22
-
23
- mock.module("../../contacts/contact-store.js", () => ({
24
- findGuardianForChannel: (_channelType: string) => localGuardian,
25
- }));
26
-
27
18
  const { resolveDestinations } = await import("../destination-resolver.js");
28
19
 
29
20
  function guardian(
@@ -37,10 +28,6 @@ function guardian(
37
28
  }
38
29
 
39
30
  describe("resolveDestinations — gateway guardian list", () => {
40
- beforeEach(() => {
41
- localGuardian = null;
42
- });
43
-
44
31
  test("vellum carries guardianPrincipalId from the gateway list", () => {
45
32
  const list = [
46
33
  guardian({ channelType: "vellum", address: "user@example.com", principalId: "prin-1" }),
@@ -137,120 +124,28 @@ describe("resolveDestinations — gateway guardian list", () => {
137
124
  });
138
125
  });
139
126
 
140
- describe("resolveDestinations — null list falls back to local read", () => {
141
- beforeEach(() => {
142
- localGuardian = null;
143
- });
144
-
145
- test("telegram resolves from the local contacts read", () => {
146
- localGuardian = {
147
- contact: {},
148
- channel: { address: "tg-user", externalChatId: "12345" },
149
- };
127
+ describe("resolveDestinations — gateway empty or missing channel", () => {
128
+ test("null gateway list omits telegram", () => {
150
129
  const result = resolveDestinations(["telegram"], null);
151
- expect(result.get("telegram")).toEqual({
152
- channel: "telegram",
153
- endpoint: "12345",
154
- metadata: { externalUserId: "tg-user" },
155
- bindingContext: {
156
- sourceChannel: "telegram",
157
- externalChatId: "12345",
158
- externalUserId: "tg-user",
159
- },
160
- });
161
- });
162
-
163
- test("slack DM resolves from the local contacts read", () => {
164
- localGuardian = {
165
- contact: {},
166
- channel: { address: "slack-user", externalChatId: "D123" },
167
- };
168
- const result = resolveDestinations(["slack"], null);
169
- expect(result.get("slack")).toEqual({
170
- channel: "slack",
171
- endpoint: "D123",
172
- metadata: { externalUserId: "slack-user" },
173
- bindingContext: {
174
- sourceChannel: "slack",
175
- externalChatId: "D123",
176
- externalUserId: "slack-user",
177
- },
178
- });
130
+ expect(result.has("telegram")).toBe(false);
179
131
  });
180
132
 
181
- test("vellum carries principalId from the local contacts read", () => {
182
- localGuardian = {
183
- contact: { principalId: "prin-1" },
184
- channel: { address: "user@example.com" },
185
- };
133
+ test("null gateway list omits vellum principalId metadata", () => {
186
134
  const result = resolveDestinations(["vellum"], null);
187
- expect(result.get("vellum")).toEqual({
188
- channel: "vellum",
189
- metadata: { guardianPrincipalId: "prin-1" },
190
- });
135
+ expect(result.get("vellum")).toEqual({ channel: "vellum" });
191
136
  });
192
- });
193
137
 
194
- describe("resolveDestinations gateway yields no channel match falls back to local", () => {
195
- beforeEach(() => {
196
- localGuardian = null;
197
- });
198
-
199
- test("empty gateway list falls back to local telegram binding", () => {
200
- localGuardian = {
201
- contact: {},
202
- channel: { address: "tg-user", externalChatId: "12345" },
203
- };
138
+ test("empty gateway list omits telegram", () => {
204
139
  const result = resolveDestinations(["telegram"], []);
205
- expect(result.get("telegram")).toEqual({
206
- channel: "telegram",
207
- endpoint: "12345",
208
- metadata: { externalUserId: "tg-user" },
209
- bindingContext: {
210
- sourceChannel: "telegram",
211
- externalChatId: "12345",
212
- externalUserId: "tg-user",
213
- },
214
- });
140
+ expect(result.has("telegram")).toBe(false);
215
141
  });
216
142
 
217
- test("gateway list missing the channel falls back to local slack DM", () => {
218
- localGuardian = {
219
- contact: {},
220
- channel: { address: "slack-user", externalChatId: "D123" },
221
- };
143
+ test("gateway list missing the channel omits slack", () => {
222
144
  // Gateway returns a telegram guardian but no slack entry.
223
145
  const list = [
224
146
  guardian({ channelType: "telegram", address: "tg", externalChatId: "999" }),
225
147
  ];
226
148
  const result = resolveDestinations(["slack"], list);
227
- expect(result.get("slack")).toEqual({
228
- channel: "slack",
229
- endpoint: "D123",
230
- metadata: { externalUserId: "slack-user" },
231
- bindingContext: {
232
- sourceChannel: "slack",
233
- externalChatId: "D123",
234
- externalUserId: "slack-user",
235
- },
236
- });
237
- });
238
-
239
- test("empty gateway list falls back to local vellum principalId", () => {
240
- localGuardian = {
241
- contact: { principalId: "prin-1" },
242
- channel: { address: "user@example.com" },
243
- };
244
- const result = resolveDestinations(["vellum"], []);
245
- expect(result.get("vellum")).toEqual({
246
- channel: "vellum",
247
- metadata: { guardianPrincipalId: "prin-1" },
248
- });
249
- });
250
-
251
- test("empty gateway list with no local binding omits telegram", () => {
252
- localGuardian = null;
253
- const result = resolveDestinations(["telegram"], []);
254
- expect(result.has("telegram")).toBe(false);
149
+ expect(result.has("slack")).toBe(false);
255
150
  });
256
151
  });
@@ -15,7 +15,6 @@ import type { GuardianDelivery } from "@vellumai/gateway-client";
15
15
 
16
16
  import { isNotificationDeliverable } from "../channels/config.js";
17
17
  import type { ChannelId } from "../channels/types.js";
18
- import { findGuardianForChannel } from "../contacts/contact-store.js";
19
18
  import { guardianForChannel } from "../contacts/guardian-delivery-reader.js";
20
19
  import { getLogger } from "../util/logger.js";
21
20
  import type { ChannelDestination, NotificationChannel } from "./types.js";
@@ -29,13 +28,7 @@ interface ResolvedGuardian {
29
28
  externalChatId?: string;
30
29
  }
31
30
 
32
- /**
33
- * Resolve the guardian delivery endpoint for a channel: gateway list first,
34
- * else the local contacts read. The local read is the transitional
35
- * dual-written mirror and covers a transient gateway failure (null list) or a
36
- * gateway list missing this channel, so a soft-failed gateway read does not
37
- * drop a binding the local store still holds. Removed in Combo 11.
38
- */
31
+ /** Resolve the guardian delivery endpoint for a channel from the gateway list. */
39
32
  function resolveGuardian(
40
33
  guardians: GuardianDelivery[] | null,
41
34
  channelType: string,
@@ -43,19 +36,11 @@ function resolveGuardian(
43
36
  const g = guardians
44
37
  ? guardianForChannel(guardians, channelType)
45
38
  : undefined;
46
- if (g) {
47
- return {
48
- principalId: g.principalId ?? undefined,
49
- address: g.address,
50
- externalChatId: g.externalChatId ?? undefined,
51
- };
52
- }
53
- const local = findGuardianForChannel(channelType);
54
- if (!local) return undefined;
39
+ if (!g) return undefined;
55
40
  return {
56
- principalId: local.contact.principalId ?? undefined,
57
- address: local.channel.address,
58
- externalChatId: local.channel.externalChatId ?? undefined,
41
+ principalId: g.principalId ?? undefined,
42
+ address: g.address,
43
+ externalChatId: g.externalChatId ?? undefined,
59
44
  };
60
45
  }
61
46
 
@@ -67,9 +52,8 @@ function resolveGuardian(
67
52
  * Returns a map keyed by `NotificationChannel`. Channels that cannot be
68
53
  * resolved (e.g. no Telegram binding configured) are omitted from the result.
69
54
  *
70
- * `guardians` is the gateway-resolved guardian list; per channel, a missing
71
- * match (null list or no entry for the channel) falls back to the local
72
- * contacts read for this release.
55
+ * `guardians` is the gateway-resolved guardian list; a channel with no entry
56
+ * in the list is omitted from the result.
73
57
  */
74
58
  export function resolveDestinations(
75
59
  channels: readonly (ChannelId | NotificationChannel)[],
@@ -13,7 +13,6 @@ import type { GuardianDelivery } from "@vellumai/gateway-client";
13
13
  import { v4 as uuid } from "uuid";
14
14
 
15
15
  import { getDeliverableChannels } from "../channels/config.js";
16
- import { findGuardianForChannel } from "../contacts/contact-store.js";
17
16
  import {
18
17
  getGuardianDelivery,
19
18
  guardianForChannel,
@@ -96,22 +95,17 @@ export function getBroadcaster(): NotificationBroadcaster {
96
95
 
97
96
  /**
98
97
  * Resolve a binding-based channel's delivery endpoint (externalChatId) the
99
- * SAME way destination-resolver's `resolveGuardian` does: gateway match first,
100
- * falling back to the LOCAL contacts read on ANY per-channel no-match — gateway
101
- * list null (unreachable) OR no active gateway entry for this channel. The
102
- * local read is the transitional dual-written mirror, removed in Combo 11.
103
- * Keeping connectivity aligned with delivery prevents a channel being marked
104
- * connected but then skipped with no destination (or vice-versa).
98
+ * SAME way destination-resolver's `resolveGuardian` does: from the gateway
99
+ * guardian delivery for this channel. Keeping connectivity aligned with
100
+ * delivery prevents a channel being marked connected but then skipped with no
101
+ * destination (or vice-versa).
105
102
  */
106
103
  function resolveChannelChatId(
107
104
  guardians: GuardianDelivery[] | null,
108
105
  channelType: string,
109
106
  ): string | undefined {
110
107
  const g = guardians ? guardianForChannel(guardians, channelType) : undefined;
111
- if (g) {
112
- return g.externalChatId ?? undefined;
113
- }
114
- return findGuardianForChannel(channelType)?.channel.externalChatId ?? undefined;
108
+ return g?.externalChatId ?? undefined;
115
109
  }
116
110
 
117
111
  export async function getConnectedChannels(): Promise<NotificationChannel[]> {
@@ -24,15 +24,8 @@
24
24
  * {@link registerDefaultPlugins} at call time.
25
25
  */
26
26
 
27
- import { finalizeTool } from "../../tools/tool-defaults.js";
28
27
  import { registerPlugin, resetPluginRegistryForTests } from "../registry.js";
29
28
  import { type Plugin, PluginExecutionError } from "../types.js";
30
- import { resetAdvisorStateForTests } from "./advisor/advisor-state-store.js";
31
- import advisorPostModelCall from "./advisor/hooks/post-model-call.js";
32
- import advisorPreModelCall from "./advisor/hooks/pre-model-call.js";
33
- import advisorUserPromptSubmit from "./advisor/hooks/user-prompt-submit.js";
34
- import advisorPkg from "./advisor/package.json" with { type: "json" };
35
- import advisorTool from "./advisor/tools/advisor.js";
36
29
  import compactionPkg from "./compaction/package.json" with { type: "json" };
37
30
  import emptyResponsePostModelCall from "./empty-response/hooks/post-model-call.js";
38
31
  import emptyResponseStop from "./empty-response/hooks/stop.js";
@@ -329,30 +322,6 @@ export const defaultToolResultTruncatePlugin: Plugin = {
329
322
  },
330
323
  };
331
324
 
332
- /**
333
- * `advisor` — adds the model-visible `advisor` tool: a no-argument tool the
334
- * model calls to consult a stronger inference profile (the `inference` call
335
- * site with a `quality-optimized` override) on the full transcript, routed
336
- * through the assistant's own inference. Three hooks feed it: `user-prompt-submit`
337
- * seeds the capture, `pre-model-call` records the executor's system prompt and
338
- * injects the steering that nudges the model to consult, and `post-model-call`
339
- * snapshots the transcript the tool reads. `finalizeTool` fills the tool's
340
- * defaults so it satisfies `Tool`, and `bootstrapPlugins` registers it into the
341
- * catalog.
342
- */
343
- export const defaultAdvisorPlugin: Plugin = {
344
- manifest: {
345
- name: advisorPkg.name,
346
- version: advisorPkg.version,
347
- },
348
- hooks: {
349
- "user-prompt-submit": advisorUserPromptSubmit,
350
- "pre-model-call": advisorPreModelCall,
351
- "post-model-call": advisorPostModelCall,
352
- },
353
- tools: [finalizeTool(advisorTool, "advisor")],
354
- };
355
-
356
325
  /**
357
326
  * Full set of first-party default plugins. Used by
358
327
  * {@link registerDefaultPlugins} to drive the registration loop; the array
@@ -375,9 +344,6 @@ function getAllDefaultPlugins(): readonly Plugin[] {
375
344
  defaultCompactionPlugin,
376
345
  defaultTitleGeneratePlugin,
377
346
  memoryV3ShadowPlugin,
378
- // Registered last so its capture hooks observe the fully-processed turn
379
- // (memory injections, history repair) that the executor actually sees.
380
- defaultAdvisorPlugin,
381
347
  ];
382
348
  }
383
349
 
@@ -424,7 +390,6 @@ export function resetPluginRegistryAndRegisterDefaults(): void {
424
390
  resetExplorationDriftStateForTests();
425
391
  resetTaskProgressNudgeStateForTests();
426
392
  resetSurfaceCompletionNudgeStoreForTests();
427
- resetAdvisorStateForTests();
428
393
  resetCaptionCacheForTests();
429
394
  registerDefaultPlugins();
430
395
  }
@@ -7,17 +7,28 @@ mock.module("../../../../util/logger.js", () => ({
7
7
  getLogger: () => makeMockLogger(),
8
8
  }));
9
9
 
10
+ // Keep the real exports (e.g. getQdrantClient) so this partial mock is harmless
11
+ // when section-dense-store's transitive imports pull them in; only
12
+ // resolveQdrantUrl is pinned to a fixed URL.
13
+ const realQdrantClient = await import("../../../../memory/qdrant-client.js");
10
14
  mock.module("../../../../memory/qdrant-client.js", () => ({
15
+ ...realQdrantClient,
11
16
  resolveQdrantUrl: () => "http://127.0.0.1:6333",
12
17
  }));
13
18
 
14
19
  // Stub the shared embedding backend. Records the queries it was asked to embed
15
20
  // and returns one deterministic vector so `denseLane` can issue the search.
21
+ // Keep the real exports (e.g. generateSparseEmbedding) so this partial mock is
22
+ // harmless when section-dense-store's transitive imports pull them in; only
23
+ // embedWithBackend is replaced.
24
+ const realEmbeddingBackend =
25
+ await import("../../../../memory/embedding-backend.js");
16
26
  const embedState = {
17
27
  calls: [] as string[][],
18
28
  throws: null as Error | null,
19
29
  };
20
30
  mock.module("../../../../memory/embedding-backend.js", () => ({
31
+ ...realEmbeddingBackend,
21
32
  embedWithBackend: async (_config: unknown, inputs: string[]) => {
22
33
  embedState.calls.push(inputs);
23
34
  if (embedState.throws) throw embedState.throws;