@vellumai/assistant 0.4.33 → 0.4.35

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 (149) hide show
  1. package/package.json +1 -1
  2. package/src/__tests__/access-request-decision.test.ts +2 -3
  3. package/src/__tests__/actor-token-service.test.ts +4 -11
  4. package/src/__tests__/approval-primitive.test.ts +0 -45
  5. package/src/__tests__/assistant-id-boundary-guard.test.ts +169 -0
  6. package/src/__tests__/callback-handoff-copy.test.ts +0 -1
  7. package/src/__tests__/channel-approval-routes.test.ts +5 -45
  8. package/src/__tests__/channel-guardian.test.ts +122 -345
  9. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +4 -3
  10. package/src/__tests__/contacts-tools.test.ts +4 -5
  11. package/src/__tests__/conversation-attention-store.test.ts +2 -65
  12. package/src/__tests__/conversation-attention-telegram.test.ts +0 -2
  13. package/src/__tests__/conversation-pairing.test.ts +0 -1
  14. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -2
  15. package/src/__tests__/guardian-action-conversation-turn.test.ts +1 -7
  16. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -74
  17. package/src/__tests__/guardian-action-late-reply.test.ts +1 -8
  18. package/src/__tests__/guardian-grant-minting.test.ts +0 -1
  19. package/src/__tests__/guardian-routing-state.test.ts +0 -3
  20. package/src/__tests__/inbound-invite-redemption.test.ts +0 -3
  21. package/src/__tests__/non-member-access-request.test.ts +0 -7
  22. package/src/__tests__/notification-broadcaster.test.ts +1 -2
  23. package/src/__tests__/notification-decision-fallback.test.ts +0 -2
  24. package/src/__tests__/notification-decision-strategy.test.ts +0 -1
  25. package/src/__tests__/relay-server.test.ts +11 -83
  26. package/src/__tests__/scoped-approval-grants.test.ts +9 -40
  27. package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -36
  28. package/src/__tests__/send-endpoint-busy.test.ts +0 -1
  29. package/src/__tests__/send-notification-tool.test.ts +0 -1
  30. package/src/__tests__/slack-inbound-verification.test.ts +2 -4
  31. package/src/__tests__/thread-seed-composer.test.ts +0 -1
  32. package/src/__tests__/tool-approval-handler.test.ts +0 -1
  33. package/src/__tests__/tool-grant-request-escalation.test.ts +0 -4
  34. package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -5
  35. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -17
  36. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -13
  37. package/src/__tests__/trusted-contact-verification.test.ts +3 -15
  38. package/src/__tests__/twilio-routes.test.ts +2 -2
  39. package/src/__tests__/voice-invite-redemption.test.ts +0 -1
  40. package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -37
  41. package/src/approvals/approval-primitive.ts +0 -15
  42. package/src/approvals/guardian-decision-primitive.ts +0 -3
  43. package/src/approvals/guardian-request-resolvers.ts +0 -5
  44. package/src/calls/call-domain.ts +0 -3
  45. package/src/calls/call-store.ts +0 -3
  46. package/src/calls/guardian-action-sweep.ts +2 -1
  47. package/src/calls/guardian-dispatch.ts +1 -2
  48. package/src/calls/relay-access-wait.ts +0 -4
  49. package/src/calls/relay-server.ts +3 -11
  50. package/src/calls/relay-setup-router.ts +1 -2
  51. package/src/calls/relay-verification.ts +0 -1
  52. package/src/calls/twilio-routes.ts +0 -3
  53. package/src/calls/types.ts +0 -1
  54. package/src/calls/voice-session-bridge.ts +0 -1
  55. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +100 -171
  56. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -1
  57. package/src/contacts/contact-store.ts +13 -88
  58. package/src/contacts/contacts-write.ts +3 -11
  59. package/src/contacts/types.ts +0 -1
  60. package/src/daemon/handlers/config-channels.ts +16 -42
  61. package/src/daemon/handlers/config-inbox.ts +6 -6
  62. package/src/daemon/handlers/contacts.ts +3 -11
  63. package/src/daemon/handlers/index.ts +0 -2
  64. package/src/daemon/session-process.ts +0 -4
  65. package/src/memory/conversation-attention-store.ts +4 -19
  66. package/src/memory/conversation-crud.ts +0 -2
  67. package/src/memory/db-init.ts +4 -0
  68. package/src/memory/guardian-action-store.ts +0 -12
  69. package/src/memory/guardian-approvals.ts +35 -80
  70. package/src/memory/guardian-rate-limits.ts +1 -14
  71. package/src/memory/guardian-verification.ts +6 -34
  72. package/src/memory/invite-store.ts +5 -14
  73. package/src/memory/migrations/026-guardian-verification-sessions.ts +28 -9
  74. package/src/memory/migrations/027a-guardian-bootstrap-token.ts +16 -3
  75. package/src/memory/migrations/038-actor-token-records.ts +8 -1
  76. package/src/memory/migrations/039-actor-refresh-token-records.ts +11 -2
  77. package/src/memory/migrations/110-channel-guardian.ts +27 -6
  78. package/src/memory/migrations/112-assistant-inbox.ts +39 -15
  79. package/src/memory/migrations/114-notifications.ts +37 -15
  80. package/src/memory/migrations/117-conversation-attention.ts +33 -9
  81. package/src/memory/migrations/134-contacts-notes-column.ts +64 -45
  82. package/src/memory/migrations/136-drop-assistant-id-columns.ts +263 -0
  83. package/src/memory/migrations/index.ts +1 -0
  84. package/src/memory/migrations/registry.ts +14 -1
  85. package/src/memory/migrations/schema-introspection.ts +18 -0
  86. package/src/memory/schema/calls.ts +0 -7
  87. package/src/memory/schema/contacts.ts +0 -8
  88. package/src/memory/schema/guardian.ts +0 -5
  89. package/src/memory/schema/infrastructure.ts +0 -2
  90. package/src/memory/schema/notifications.ts +3 -17
  91. package/src/memory/scoped-approval-grants.ts +2 -24
  92. package/src/notifications/adapters/sms.ts +2 -1
  93. package/src/notifications/broadcaster.ts +1 -6
  94. package/src/notifications/decision-engine.ts +3 -4
  95. package/src/notifications/deliveries-store.ts +0 -4
  96. package/src/notifications/destination-resolver.ts +4 -6
  97. package/src/notifications/deterministic-checks.ts +1 -6
  98. package/src/notifications/emit-signal.ts +4 -11
  99. package/src/notifications/events-store.ts +7 -17
  100. package/src/notifications/preference-summary.ts +2 -2
  101. package/src/notifications/preferences-store.ts +2 -9
  102. package/src/notifications/signal.ts +0 -1
  103. package/src/notifications/thread-candidates.ts +1 -11
  104. package/src/notifications/types.ts +0 -3
  105. package/src/runtime/access-request-helper.ts +3 -10
  106. package/src/runtime/actor-refresh-token-store.ts +0 -6
  107. package/src/runtime/actor-token-store.ts +3 -16
  108. package/src/runtime/actor-trust-resolver.ts +1 -4
  109. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -9
  110. package/src/runtime/auth/credential-service.ts +1 -15
  111. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  112. package/src/runtime/channel-guardian-service.ts +15 -46
  113. package/src/runtime/channel-invite-transport.ts +8 -0
  114. package/src/runtime/channel-invite-transports/email.ts +4 -0
  115. package/src/runtime/channel-invite-transports/slack.ts +6 -0
  116. package/src/runtime/channel-invite-transports/sms.ts +4 -0
  117. package/src/runtime/channel-invite-transports/telegram.ts +6 -0
  118. package/src/runtime/confirmation-request-guardian-bridge.ts +0 -1
  119. package/src/runtime/guardian-action-followup-executor.ts +3 -2
  120. package/src/runtime/guardian-action-grant-minter.ts +0 -1
  121. package/src/runtime/guardian-outbound-actions.ts +2 -12
  122. package/src/runtime/guardian-vellum-migration.ts +2 -3
  123. package/src/runtime/http-server.ts +3 -10
  124. package/src/runtime/http-types.ts +13 -1
  125. package/src/runtime/invite-redemption-service.ts +1 -14
  126. package/src/runtime/local-actor-identity.ts +2 -5
  127. package/src/runtime/routes/access-request-decision.ts +0 -1
  128. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +0 -9
  129. package/src/runtime/routes/channel-readiness-routes.ts +29 -18
  130. package/src/runtime/routes/contact-routes.ts +15 -40
  131. package/src/runtime/routes/conversation-attention-routes.ts +0 -2
  132. package/src/runtime/routes/global-search-routes.ts +0 -2
  133. package/src/runtime/routes/guardian-bootstrap-routes.ts +6 -7
  134. package/src/runtime/routes/guardian-expiry-sweep.ts +3 -2
  135. package/src/runtime/routes/inbound-message-handler.ts +0 -3
  136. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -43
  137. package/src/runtime/routes/inbound-stages/background-dispatch.ts +1 -4
  138. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -6
  139. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +0 -1
  140. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +0 -1
  141. package/src/runtime/routes/inbound-stages/verification-intercept.ts +3 -7
  142. package/src/runtime/routes/pairing-routes.ts +4 -4
  143. package/src/runtime/routes/surface-content-routes.ts +104 -0
  144. package/src/runtime/tool-grant-request-helper.ts +0 -1
  145. package/src/tools/browser/browser-manager.ts +22 -21
  146. package/src/tools/browser/runtime-check.ts +111 -6
  147. package/src/tools/calls/call-start.ts +1 -3
  148. package/src/tools/followups/followup_create.ts +1 -2
  149. package/src/tools/tool-approval-handler.ts +0 -2
@@ -41,7 +41,7 @@ export function resolveLocalIpcTrustContext(
41
41
  const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
42
42
 
43
43
  // Try contacts-first for the vellum guardian channel
44
- const guardianResult = findGuardianForChannel("vellum", assistantId);
44
+ const guardianResult = findGuardianForChannel("vellum");
45
45
  if (guardianResult && guardianResult.contact.principalId) {
46
46
  const guardianPrincipalId = guardianResult.contact.principalId;
47
47
  const trustCtx = resolveTrustContext({
@@ -93,10 +93,7 @@ export function resolveLocalIpcAuthContext(sessionId: string): AuthContext {
93
93
  const authContext = buildIpcAuthContext(sessionId);
94
94
 
95
95
  // Enrich with the guardian principal ID from contacts-first path
96
- const guardianResult = findGuardianForChannel(
97
- "vellum",
98
- authContext.assistantId,
99
- );
96
+ const guardianResult = findGuardianForChannel("vellum");
100
97
  if (guardianResult && guardianResult.contact.principalId) {
101
98
  return {
102
99
  ...authContext,
@@ -86,7 +86,6 @@ export function handleAccessRequestDecision(
86
86
  // so only the original requester can consume the code. Mark as
87
87
  // trusted_contact so the consume path skips guardian binding creation.
88
88
  const session = createOutboundSession({
89
- assistantId: approval.assistantId,
90
89
  channel: approval.channel,
91
90
  expectedExternalUserId: approval.requesterExternalUserId,
92
91
  expectedChatId: approval.requesterChatId,
@@ -99,7 +99,6 @@ export async function handleGuardianCallbackDecision(
99
99
  callbackDecision.requestId,
100
100
  sourceChannel,
101
101
  conversationExternalId,
102
- assistantId,
103
102
  )
104
103
  : null;
105
104
 
@@ -110,7 +109,6 @@ export async function handleGuardianCallbackDecision(
110
109
  const allPending = getAllPendingApprovalsByGuardianChat(
111
110
  sourceChannel,
112
111
  conversationExternalId,
113
- assistantId,
114
112
  );
115
113
  if (allPending.length === 1) {
116
114
  guardianApproval = allPending[0];
@@ -141,7 +139,6 @@ export async function handleGuardianCallbackDecision(
141
139
  const allPending = getAllPendingApprovalsByGuardianChat(
142
140
  sourceChannel,
143
141
  conversationExternalId,
144
- assistantId,
145
142
  );
146
143
  if (allPending.length === 1) {
147
144
  guardianApproval = allPending[0];
@@ -204,7 +201,6 @@ export async function handleGuardianCallbackDecision(
204
201
  const allGuardianPending = getAllPendingApprovalsByGuardianChat(
205
202
  sourceChannel,
206
203
  conversationExternalId,
207
- assistantId,
208
204
  );
209
205
  // Only present approvals that belong to this sender so the engine
210
206
  // does not offer disambiguation for requests assigned to a rotated
@@ -609,7 +605,6 @@ async function handleLegacyDecision(params: {
609
605
  legacyGuardianDecision.requestId,
610
606
  sourceChannel,
611
607
  conversationExternalId,
612
- assistantId,
613
608
  );
614
609
  if (!resolvedByRequest) {
615
610
  // The referenced request doesn't match any pending guardian
@@ -807,7 +802,6 @@ async function handleAccessRequestApproval(
807
802
  sourceEventName: "ingress.trusted_contact.guardian_decision",
808
803
  sourceChannel: approval.channel,
809
804
  sourceSessionId: approval.conversationId,
810
- assistantId,
811
805
  attentionHints: {
812
806
  requiresAction: false,
813
807
  urgency: "medium",
@@ -822,7 +816,6 @@ async function handleAccessRequestApproval(
822
816
  sourceEventName: "ingress.trusted_contact.denied",
823
817
  sourceChannel: approval.channel,
824
818
  sourceSessionId: approval.conversationId,
825
- assistantId,
826
819
  attentionHints: {
827
820
  requiresAction: false,
828
821
  urgency: "low",
@@ -891,7 +884,6 @@ async function handleAccessRequestApproval(
891
884
  sourceEventName: "ingress.trusted_contact.guardian_decision",
892
885
  sourceChannel: approval.channel,
893
886
  sourceSessionId: approval.conversationId,
894
- assistantId,
895
887
  attentionHints: {
896
888
  requiresAction: false,
897
889
  urgency: "medium",
@@ -918,7 +910,6 @@ async function handleAccessRequestApproval(
918
910
  sourceEventName: "ingress.trusted_contact.verification_sent",
919
911
  sourceChannel: approval.channel,
920
912
  sourceSessionId: approval.conversationId,
921
- assistantId,
922
913
  attentionHints: {
923
914
  requiresAction: false,
924
915
  urgency: "low",
@@ -7,6 +7,7 @@
7
7
 
8
8
  import type { ChannelId } from "../../channels/types.js";
9
9
  import { getReadinessService } from "../../daemon/handlers/config-channels.js";
10
+ import { getInviteAdapterRegistry } from "../channel-invite-transport.js";
10
11
  import type { RouteDefinition } from "../http-router.js";
11
12
 
12
13
  /**
@@ -21,18 +22,23 @@ export async function handleGetChannelReadiness(url: URL): Promise<Response> {
21
22
 
22
23
  const service = getReadinessService();
23
24
  const snapshots = await service.getReadiness(channel, includeRemote);
25
+ const adapterRegistry = getInviteAdapterRegistry();
24
26
 
25
27
  return Response.json({
26
28
  success: true,
27
- snapshots: snapshots.map((s) => ({
28
- channel: s.channel,
29
- ready: s.ready,
30
- checkedAt: s.checkedAt,
31
- stale: s.stale,
32
- reasons: s.reasons,
33
- localChecks: s.localChecks,
34
- remoteChecks: s.remoteChecks,
35
- })),
29
+ snapshots: snapshots.map((s) => {
30
+ const adapter = adapterRegistry.get(s.channel);
31
+ return {
32
+ channel: s.channel,
33
+ ready: s.ready,
34
+ checkedAt: s.checkedAt,
35
+ stale: s.stale,
36
+ reasons: s.reasons,
37
+ localChecks: s.localChecks,
38
+ remoteChecks: s.remoteChecks,
39
+ channelHandle: adapter?.resolveChannelHandle?.() ?? undefined,
40
+ };
41
+ }),
36
42
  });
37
43
  }
38
44
 
@@ -62,18 +68,23 @@ export async function handleRefreshChannelReadiness(
62
68
  body.channel,
63
69
  body.includeRemote,
64
70
  );
71
+ const adapterRegistry = getInviteAdapterRegistry();
65
72
 
66
73
  return Response.json({
67
74
  success: true,
68
- snapshots: snapshots.map((s) => ({
69
- channel: s.channel,
70
- ready: s.ready,
71
- checkedAt: s.checkedAt,
72
- stale: s.stale,
73
- reasons: s.reasons,
74
- localChecks: s.localChecks,
75
- remoteChecks: s.remoteChecks,
76
- })),
75
+ snapshots: snapshots.map((s) => {
76
+ const adapter = adapterRegistry.get(s.channel);
77
+ return {
78
+ channel: s.channel,
79
+ ready: s.ready,
80
+ checkedAt: s.checkedAt,
81
+ stale: s.stale,
82
+ reasons: s.reasons,
83
+ localChecks: s.localChecks,
84
+ remoteChecks: s.remoteChecks,
85
+ channelHandle: adapter?.resolveChannelHandle?.() ?? undefined,
86
+ };
87
+ }),
77
88
  });
78
89
  }
79
90
 
@@ -81,7 +81,7 @@ const VALID_ASSISTANT_SPECIES: readonly AssistantSpecies[] = [
81
81
  * Also supports search query params: query, channelAddress, channelType.
82
82
  * When any search param is provided, delegates to searchContacts() instead of listContacts().
83
83
  */
84
- export function handleListContacts(url: URL, assistantId: string): Response {
84
+ export function handleListContacts(url: URL): Response {
85
85
  const limit = Number(url.searchParams.get("limit") ?? 50);
86
86
  const role = url.searchParams.get("role") as ContactRole | null;
87
87
  const contactTypeParam = url.searchParams.get("contactType");
@@ -104,7 +104,6 @@ export function handleListContacts(url: URL, assistantId: string): Response {
104
104
 
105
105
  if (hasSearchParams) {
106
106
  const contacts = searchContacts({
107
- assistantId,
108
107
  query: query ?? undefined,
109
108
  channelAddress: channelAddress ?? undefined,
110
109
  channelType: channelType ?? undefined,
@@ -118,12 +117,7 @@ export function handleListContacts(url: URL, assistantId: string): Response {
118
117
  });
119
118
  }
120
119
 
121
- const contacts = listContacts(
122
- assistantId,
123
- limit,
124
- role ?? undefined,
125
- contactType,
126
- );
120
+ const contacts = listContacts(limit, role ?? undefined, contactType);
127
121
  return Response.json({
128
122
  ok: true,
129
123
  contacts: contacts.map(withGuardianNameOverride),
@@ -133,11 +127,8 @@ export function handleListContacts(url: URL, assistantId: string): Response {
133
127
  /**
134
128
  * GET /v1/contacts/:id
135
129
  */
136
- export function handleGetContact(
137
- contactId: string,
138
- assistantId: string,
139
- ): Response {
140
- const contact = getContact(contactId, assistantId);
130
+ export function handleGetContact(contactId: string): Response {
131
+ const contact = getContact(contactId);
141
132
  if (!contact) {
142
133
  return httpError("NOT_FOUND", `Contact "${contactId}" not found`, 404);
143
134
  }
@@ -155,10 +146,7 @@ export function handleGetContact(
155
146
  /**
156
147
  * POST /v1/contacts/merge { keepId, mergeId }
157
148
  */
158
- export async function handleMergeContacts(
159
- req: Request,
160
- assistantId: string,
161
- ): Promise<Response> {
149
+ export async function handleMergeContacts(req: Request): Promise<Response> {
162
150
  const body = (await req.json()) as { keepId?: string; mergeId?: string };
163
151
 
164
152
  if (!body.keepId || !body.mergeId) {
@@ -166,7 +154,7 @@ export async function handleMergeContacts(
166
154
  }
167
155
 
168
156
  try {
169
- const contact = mergeContacts(body.keepId, body.mergeId, assistantId);
157
+ const contact = mergeContacts(body.keepId, body.mergeId);
170
158
  return Response.json({
171
159
  ok: true,
172
160
  contact: withGuardianNameOverride(contact),
@@ -209,10 +197,7 @@ function isChannelPolicy(value: string): value is ChannelPolicy {
209
197
  /**
210
198
  * POST /v1/contacts { displayName, id?, notes?, contactType?, assistantMetadata?, ... }
211
199
  */
212
- export async function handleUpsertContact(
213
- req: Request,
214
- assistantId: string,
215
- ): Promise<Response> {
200
+ export async function handleUpsertContact(req: Request): Promise<Response> {
216
201
  const body = (await req.json()) as {
217
202
  id?: string;
218
203
  displayName?: string;
@@ -328,7 +313,6 @@ export async function handleUpsertContact(
328
313
  notes: body.notes,
329
314
  role: body.role as ContactRole | undefined,
330
315
  contactType: body.contactType as ContactType | undefined,
331
- assistantId,
332
316
  channels: body.channels?.map((ch) => ({
333
317
  ...ch,
334
318
  status: ch.status as ChannelStatus | undefined,
@@ -360,7 +344,6 @@ export async function handleUpsertContact(
360
344
  export async function handleUpdateContactChannel(
361
345
  req: Request,
362
346
  channelId: string,
363
- assistantId: string,
364
347
  ): Promise<Response> {
365
348
  const body = (await req.json()) as {
366
349
  status?: string;
@@ -426,7 +409,7 @@ export async function handleUpdateContactChannel(
426
409
  return httpError("NOT_FOUND", `Channel "${channelId}" not found`, 404);
427
410
  }
428
411
 
429
- const parentContact = getContact(updated.contactId, assistantId);
412
+ const parentContact = getContact(updated.contactId);
430
413
  return Response.json({
431
414
  ok: true,
432
415
  contact: parentContact
@@ -487,7 +470,7 @@ export async function handleVerifyContactChannel(
487
470
  channelId: string,
488
471
  assistantId: string,
489
472
  ): Promise<Response> {
490
- const contact = getContact(contactId, assistantId);
473
+ const contact = getContact(contactId);
491
474
  if (!contact) {
492
475
  return httpError("NOT_FOUND", `Contact "${contactId}" not found`, 404);
493
476
  }
@@ -555,7 +538,6 @@ export async function handleVerifyContactChannel(
555
538
  }
556
539
 
557
540
  const sessionResult = createOutboundSession({
558
- assistantId,
559
541
  channel: verificationChannel,
560
542
  expectedPhoneE164: phoneE164,
561
543
  expectedExternalUserId: channel.externalUserId ?? undefined,
@@ -589,7 +571,6 @@ export async function handleVerifyContactChannel(
589
571
  // Telegram with known chat ID: identity is already bound
590
572
  if (channel.externalChatId) {
591
573
  const sessionResult = createOutboundSession({
592
- assistantId,
593
574
  channel: verificationChannel,
594
575
  expectedChatId: channel.externalChatId,
595
576
  expectedExternalUserId: channel.externalUserId ?? undefined,
@@ -639,7 +620,6 @@ export async function handleVerifyContactChannel(
639
620
  .digest("hex");
640
621
 
641
622
  const sessionResult = createOutboundSession({
642
- assistantId,
643
623
  channel: verificationChannel,
644
624
  identityBindingStatus: "pending_bootstrap",
645
625
  destinationAddress: effectiveDestination,
@@ -675,7 +655,6 @@ export async function handleVerifyContactChannel(
675
655
  }
676
656
 
677
657
  const sessionResult = createOutboundSession({
678
- assistantId,
679
658
  channel: verificationChannel,
680
659
  expectedExternalUserId: channel.externalUserId ?? undefined,
681
660
  expectedChatId: channel.externalChatId ?? undefined,
@@ -721,27 +700,24 @@ export function contactRouteDefinitions(): RouteDefinition[] {
721
700
  {
722
701
  endpoint: "contacts",
723
702
  method: "GET",
724
- handler: ({ url, authContext }) =>
725
- handleListContacts(url, authContext.assistantId),
703
+ handler: ({ url }) => handleListContacts(url),
726
704
  },
727
705
  {
728
706
  endpoint: "contacts",
729
707
  method: "POST",
730
- handler: async ({ req, authContext }) =>
731
- handleUpsertContact(req, authContext.assistantId),
708
+ handler: async ({ req }) => handleUpsertContact(req),
732
709
  },
733
710
  {
734
711
  endpoint: "contacts/merge",
735
712
  method: "POST",
736
- handler: async ({ req, authContext }) =>
737
- handleMergeContacts(req, authContext.assistantId),
713
+ handler: async ({ req }) => handleMergeContacts(req),
738
714
  },
739
715
  {
740
716
  endpoint: "contacts/channels/:id",
741
717
  method: "PATCH",
742
718
  policyKey: "contacts/channels",
743
- handler: async ({ req, params, authContext }) =>
744
- handleUpdateContactChannel(req, params.id, authContext.assistantId),
719
+ handler: async ({ req, params }) =>
720
+ handleUpdateContactChannel(req, params.id),
745
721
  },
746
722
  {
747
723
  endpoint: "contacts/:contactId/channels/:channelId/verify",
@@ -768,8 +744,7 @@ export function contactCatchAllRouteDefinitions(): RouteDefinition[] {
768
744
  endpoint: "contacts/:id",
769
745
  method: "GET",
770
746
  policyKey: "contacts",
771
- handler: ({ params, authContext }) =>
772
- handleGetContact(params.id, authContext.assistantId),
747
+ handler: ({ params }) => handleGetContact(params.id),
773
748
  },
774
749
  ];
775
750
  }
@@ -10,7 +10,6 @@ import {
10
10
  } from "../../memory/conversation-attention-store.js";
11
11
  import * as conversationStore from "../../memory/conversation-store.js";
12
12
  import { truncate } from "../../util/truncate.js";
13
- import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
14
13
  import { httpError } from "../http-errors.js";
15
14
  import type { RouteDefinition } from "../http-router.js";
16
15
 
@@ -38,7 +37,6 @@ export function handleListConversationAttention(url: URL): Response {
38
37
  }
39
38
 
40
39
  const attentionStates = listConversationAttention({
41
- assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
42
40
  state: stateParam as AttentionFilterState,
43
41
  sourceChannel: channel,
44
42
  source: sourceParam !== "all" ? sourceParam : undefined,
@@ -19,7 +19,6 @@ import { rawAll } from "../../memory/raw-query.js";
19
19
  import { semanticSearch } from "../../memory/search/semantic.js";
20
20
  import { listSchedules } from "../../schedule/schedule-store.js";
21
21
  import { getLogger } from "../../util/logger.js";
22
- import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
23
22
  import { httpError } from "../http-errors.js";
24
23
  import type { RouteDefinition } from "../http-router.js";
25
24
 
@@ -250,7 +249,6 @@ export async function handleGlobalSearch(url: URL): Promise<Response> {
250
249
 
251
250
  if (categories.has("contacts")) {
252
251
  const contactResults = searchContacts({
253
- assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
254
252
  query,
255
253
  limit,
256
254
  });
@@ -45,7 +45,7 @@ function ensureGuardianPrincipal(assistantId: string): {
45
45
  guardianPrincipalId: string;
46
46
  isNew: boolean;
47
47
  } {
48
- const guardianResult = findGuardianForChannel("vellum", assistantId);
48
+ const guardianResult = findGuardianForChannel("vellum");
49
49
  if (guardianResult && guardianResult.contact.principalId) {
50
50
  return {
51
51
  guardianPrincipalId: guardianResult.contact.principalId,
@@ -57,7 +57,6 @@ function ensureGuardianPrincipal(assistantId: string): {
57
57
  const guardianPrincipalId = `vellum-principal-${uuid()}`;
58
58
 
59
59
  createGuardianBinding({
60
- assistantId,
61
60
  channel: "vellum",
62
61
  guardianExternalUserId: guardianPrincipalId,
63
62
  guardianDeliveryChatId: "local",
@@ -115,7 +114,7 @@ export async function handleGuardianBootstrap(
115
114
  );
116
115
  }
117
116
 
118
- if (platform !== "macos" && platform !== "cli") {
117
+ if (platform !== "macos" && platform !== "cli" && platform !== "web") {
119
118
  return httpError(
120
119
  "BAD_REQUEST",
121
120
  "Invalid platform. Bootstrap is macOS/CLI-only; iOS uses QR pairing.",
@@ -123,13 +122,13 @@ export async function handleGuardianBootstrap(
123
122
  );
124
123
  }
125
124
 
126
- const assistantId = DAEMON_INTERNAL_ASSISTANT_ID;
127
- const { guardianPrincipalId, isNew } = ensureGuardianPrincipal(assistantId);
125
+ const { guardianPrincipalId, isNew } = ensureGuardianPrincipal(
126
+ DAEMON_INTERNAL_ASSISTANT_ID,
127
+ );
128
128
  const hashedDeviceId = hashDeviceId(deviceId);
129
129
 
130
130
  // Mint credential pair (access token + refresh token)
131
131
  const credentials = mintCredentialPair({
132
- assistantId,
133
132
  platform,
134
133
  deviceId,
135
134
  guardianPrincipalId,
@@ -137,7 +136,7 @@ export async function handleGuardianBootstrap(
137
136
  });
138
137
 
139
138
  log.info(
140
- { assistantId, platform, guardianPrincipalId, isNew },
139
+ { platform, guardianPrincipalId, isNew },
141
140
  "Guardian bootstrap completed",
142
141
  );
143
142
 
@@ -9,6 +9,7 @@ import {
9
9
  } from "../../memory/channel-guardian-store.js";
10
10
  import { getLogger } from "../../util/logger.js";
11
11
  import { composeApprovalMessageGenerative } from "../approval-message-composer.js";
12
+ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
12
13
  import type { ApprovalDecisionResult } from "../channel-approval-types.js";
13
14
  import { handleChannelDecision } from "../channel-approvals.js";
14
15
  import { deliverChannelReply } from "../gateway-client.js";
@@ -68,7 +69,7 @@ export function sweepExpiredGuardianApprovals(
68
69
  {
69
70
  chatId: approval.requesterChatId,
70
71
  text: requesterText,
71
- assistantId: approval.assistantId,
72
+ assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
72
73
  },
73
74
  mintBearerToken?.(),
74
75
  );
@@ -96,7 +97,7 @@ export function sweepExpiredGuardianApprovals(
96
97
  {
97
98
  chatId: approval.guardianChatId,
98
99
  text: guardianText,
99
- assistantId: approval.assistantId,
100
+ assistantId: DAEMON_INTERNAL_ASSISTANT_ID,
100
101
  },
101
102
  mintBearerToken?.(),
102
103
  );
@@ -501,7 +501,6 @@ export async function handleChannelInbound(
501
501
  : body.callbackData!;
502
502
  recordConversationSeenSignal({
503
503
  conversationId: result.conversationId,
504
- assistantId: canonicalAssistantId,
505
504
  signalType: `${sourceChannel}_callback` as SignalType,
506
505
  confidence: "inferred",
507
506
  sourceChannel,
@@ -515,7 +514,6 @@ export async function handleChannelInbound(
515
514
  : trimmedContent;
516
515
  recordConversationSeenSignal({
517
516
  conversationId: result.conversationId,
518
- assistantId: canonicalAssistantId,
519
517
  signalType: `${sourceChannel}_inbound_message` as SignalType,
520
518
  confidence: "inferred",
521
519
  sourceChannel,
@@ -554,7 +552,6 @@ export async function handleChannelInbound(
554
552
  : body.callbackData!;
555
553
  recordConversationSeenSignal({
556
554
  conversationId: result.conversationId,
557
- assistantId: canonicalAssistantId,
558
555
  signalType: `${sourceChannel}_callback` as SignalType,
559
556
  confidence: "inferred",
560
557
  sourceChannel,
@@ -191,14 +191,8 @@ export async function enforceIngressAcl(
191
191
  // omitted: rebind sessions create a consumable challenge while a
192
192
  // binding already exists, and the identity check inside
193
193
  // validateAndConsumeChallenge prevents unauthorized takeovers.
194
- const hasPendingChallenge = !!getPendingChallenge(
195
- canonicalAssistantId,
196
- sourceChannel,
197
- );
198
- const hasActiveOutboundSession = !!findActiveSession(
199
- canonicalAssistantId,
200
- sourceChannel,
201
- );
194
+ const hasPendingChallenge = !!getPendingChallenge(sourceChannel);
195
+ const hasActiveOutboundSession = !!findActiveSession(sourceChannel);
202
196
  if (hasPendingChallenge || hasActiveOutboundSession) {
203
197
  denyNonMember = false;
204
198
  } else {
@@ -219,7 +213,6 @@ export async function enforceIngressAcl(
219
213
  ).payload as string;
220
214
  const bootstrapTokenForAcl = bootstrapPayload.slice(3); // strip 'gv_' prefix
221
215
  const bootstrapSessionForAcl = resolveBootstrapToken(
222
- canonicalAssistantId,
223
216
  sourceChannel,
224
217
  bootstrapTokenForAcl,
225
218
  );
@@ -299,13 +292,8 @@ export async function enforceIngressAcl(
299
292
  // user can reply with the code in the DM to self-verify.
300
293
  if (sourceChannel === "slack" && (canonicalSenderId ?? rawSenderId)) {
301
294
  const slackVerifyResult = initiateSlackVerificationChallenge({
302
- canonicalAssistantId,
303
295
  sourceChannel,
304
- conversationExternalId,
305
296
  senderUserId: (canonicalSenderId ?? rawSenderId)!,
306
- replyCallbackUrl,
307
- mintBearerToken,
308
- assistantId,
309
297
  });
310
298
 
311
299
  if (slackVerifyResult.initiated) {
@@ -420,14 +408,8 @@ export async function enforceIngressAcl(
420
408
  // revoked/blocked — otherwise the user can never re-verify.
421
409
  let denyInactiveMember = true;
422
410
  if (isGuardianVerifyCode) {
423
- const hasPendingChallenge = !!getPendingChallenge(
424
- canonicalAssistantId,
425
- sourceChannel,
426
- );
427
- const hasActiveOutboundSession = !!findActiveSession(
428
- canonicalAssistantId,
429
- sourceChannel,
430
- );
411
+ const hasPendingChallenge = !!getPendingChallenge(sourceChannel);
412
+ const hasActiveOutboundSession = !!findActiveSession(sourceChannel);
431
413
  if (hasPendingChallenge || hasActiveOutboundSession) {
432
414
  denyInactiveMember = false;
433
415
  } else {
@@ -448,7 +430,6 @@ export async function enforceIngressAcl(
448
430
  ).payload as string;
449
431
  const bootstrapTokenForAcl = bootstrapPayload.slice(3);
450
432
  const bootstrapSessionForAcl = resolveBootstrapToken(
451
- canonicalAssistantId,
452
433
  sourceChannel,
453
434
  bootstrapTokenForAcl,
454
435
  );
@@ -538,13 +519,8 @@ export async function enforceIngressAcl(
538
519
  (canonicalSenderId ?? rawSenderId)
539
520
  ) {
540
521
  const slackVerifyResult = initiateSlackVerificationChallenge({
541
- canonicalAssistantId,
542
522
  sourceChannel,
543
- conversationExternalId,
544
523
  senderUserId: (canonicalSenderId ?? rawSenderId)!,
545
- replyCallbackUrl,
546
- mintBearerToken,
547
- assistantId,
548
524
  });
549
525
 
550
526
  if (slackVerifyResult.initiated) {
@@ -1098,28 +1074,17 @@ interface SlackVerificationResult {
1098
1074
  * creates a trusted contact record (not a guardian binding).
1099
1075
  */
1100
1076
  function initiateSlackVerificationChallenge(params: {
1101
- canonicalAssistantId: string;
1102
1077
  sourceChannel: ChannelId;
1103
- conversationExternalId: string;
1104
1078
  senderUserId: string;
1105
- replyCallbackUrl: string | undefined;
1106
- mintBearerToken: () => string;
1107
- assistantId: string;
1108
1079
  }): SlackVerificationResult {
1109
- const { canonicalAssistantId, sourceChannel, senderUserId } = params;
1080
+ const { sourceChannel, senderUserId } = params;
1110
1081
 
1111
1082
  // Skip if there is already a pending challenge or active session for
1112
1083
  // this sender to avoid flooding them with duplicate codes. We scope by
1113
1084
  // sender identity (expectedExternalUserId) so that a pending session for
1114
1085
  // user A does not suppress challenges for user B.
1115
- const existingChallenge = getPendingChallenge(
1116
- canonicalAssistantId,
1117
- sourceChannel,
1118
- );
1119
- const existingSession = findActiveSession(
1120
- canonicalAssistantId,
1121
- sourceChannel,
1122
- );
1086
+ const existingChallenge = getPendingChallenge(sourceChannel);
1087
+ const existingSession = findActiveSession(sourceChannel);
1123
1088
  const senderHasPending =
1124
1089
  (existingChallenge &&
1125
1090
  existingChallenge.expectedExternalUserId === senderUserId) ||
@@ -1140,7 +1105,6 @@ function initiateSlackVerificationChallenge(params: {
1140
1105
 
1141
1106
  try {
1142
1107
  const session = createOutboundSession({
1143
- assistantId: canonicalAssistantId,
1144
1108
  channel: sourceChannel,
1145
1109
  expectedExternalUserId: senderUserId,
1146
1110
  expectedChatId: senderUserId,
@@ -507,10 +507,7 @@ export function startTrustedContactApprovalNotifier(params: {
507
507
 
508
508
  if (info && !globalNotifiedApprovalRequestIds.has(info.requestId)) {
509
509
  globalNotifiedApprovalRequestIds.set(info.requestId, conversationId);
510
- const guardian = findGuardianForChannel(
511
- sourceChannel,
512
- assistantId ?? DAEMON_INTERNAL_ASSISTANT_ID,
513
- );
510
+ const guardian = findGuardianForChannel(sourceChannel);
514
511
  const guardianName = resolveGuardianName(
515
512
  guardian?.contact.displayName,
516
513
  );
@@ -74,11 +74,7 @@ export async function handleBootstrapIntercept(
74
74
  }
75
75
 
76
76
  const bootstrapToken = (commandIntent.payload as string).slice(3);
77
- const bootstrapSession = resolveBootstrapToken(
78
- canonicalAssistantId,
79
- sourceChannel,
80
- bootstrapToken,
81
- );
77
+ const bootstrapSession = resolveBootstrapToken(sourceChannel, bootstrapToken);
82
78
 
83
79
  if (!bootstrapSession || bootstrapSession.status !== "pending_bootstrap") {
84
80
  // Not found or expired — fall through to normal /start handling
@@ -94,7 +90,6 @@ export async function handleBootstrapIntercept(
94
90
  // Create a new identity-bound outbound session with a fresh secret.
95
91
  // The old bootstrap session is auto-revoked by createOutboundSession.
96
92
  const newSession = createOutboundSession({
97
- assistantId: canonicalAssistantId,
98
93
  channel: sourceChannel,
99
94
  expectedExternalUserId: rawSenderId,
100
95
  expectedChatId: conversationExternalId,
@@ -134,7 +134,6 @@ export function handleEscalationIntercept(
134
134
  sourceEventName: "ingress.escalation",
135
135
  sourceChannel,
136
136
  sourceSessionId: conversationId,
137
- assistantId: canonicalAssistantId,
138
137
  attentionHints: {
139
138
  requiresAction: true,
140
139
  urgency: "high",
@@ -115,7 +115,6 @@ export function runSecretIngressCheck(params: SecretIngressCheckParams): void {
115
115
  : "User sent media attachment";
116
116
  recordConversationSeenSignal({
117
117
  conversationId,
118
- assistantId: canonicalAssistantId,
119
118
  signalType: "telegram_inbound_message",
120
119
  confidence: "inferred",
121
120
  sourceChannel: "telegram",