@vellumai/assistant 0.4.33 → 0.4.34

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 (137) 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 +150 -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/notifications/tools/send-notification.ts +0 -1
  56. package/src/contacts/contact-store.ts +13 -88
  57. package/src/contacts/contacts-write.ts +3 -11
  58. package/src/contacts/types.ts +0 -1
  59. package/src/daemon/handlers/config-channels.ts +16 -42
  60. package/src/daemon/handlers/config-inbox.ts +6 -6
  61. package/src/daemon/handlers/contacts.ts +3 -11
  62. package/src/daemon/handlers/index.ts +0 -2
  63. package/src/daemon/session-process.ts +0 -4
  64. package/src/memory/conversation-attention-store.ts +4 -19
  65. package/src/memory/conversation-crud.ts +0 -2
  66. package/src/memory/db-init.ts +4 -0
  67. package/src/memory/guardian-action-store.ts +0 -12
  68. package/src/memory/guardian-approvals.ts +35 -80
  69. package/src/memory/guardian-rate-limits.ts +1 -14
  70. package/src/memory/guardian-verification.ts +6 -34
  71. package/src/memory/invite-store.ts +5 -14
  72. package/src/memory/migrations/134-contacts-notes-column.ts +64 -45
  73. package/src/memory/migrations/136-drop-assistant-id-columns.ts +263 -0
  74. package/src/memory/migrations/index.ts +1 -0
  75. package/src/memory/migrations/registry.ts +14 -1
  76. package/src/memory/schema/calls.ts +0 -7
  77. package/src/memory/schema/contacts.ts +0 -8
  78. package/src/memory/schema/guardian.ts +0 -5
  79. package/src/memory/schema/infrastructure.ts +0 -2
  80. package/src/memory/schema/notifications.ts +3 -17
  81. package/src/memory/scoped-approval-grants.ts +2 -24
  82. package/src/notifications/adapters/sms.ts +2 -1
  83. package/src/notifications/broadcaster.ts +1 -6
  84. package/src/notifications/decision-engine.ts +3 -4
  85. package/src/notifications/deliveries-store.ts +0 -4
  86. package/src/notifications/destination-resolver.ts +4 -6
  87. package/src/notifications/deterministic-checks.ts +1 -6
  88. package/src/notifications/emit-signal.ts +4 -11
  89. package/src/notifications/events-store.ts +7 -17
  90. package/src/notifications/preference-summary.ts +2 -2
  91. package/src/notifications/preferences-store.ts +2 -9
  92. package/src/notifications/signal.ts +0 -1
  93. package/src/notifications/thread-candidates.ts +1 -11
  94. package/src/notifications/types.ts +0 -3
  95. package/src/runtime/access-request-helper.ts +3 -10
  96. package/src/runtime/actor-refresh-token-store.ts +0 -6
  97. package/src/runtime/actor-token-store.ts +3 -16
  98. package/src/runtime/actor-trust-resolver.ts +1 -4
  99. package/src/runtime/auth/__tests__/credential-service.test.ts +0 -9
  100. package/src/runtime/auth/credential-service.ts +1 -15
  101. package/src/runtime/auth/require-bound-guardian.ts +1 -4
  102. package/src/runtime/channel-guardian-service.ts +15 -46
  103. package/src/runtime/channel-invite-transport.ts +8 -0
  104. package/src/runtime/channel-invite-transports/email.ts +4 -0
  105. package/src/runtime/channel-invite-transports/slack.ts +6 -0
  106. package/src/runtime/channel-invite-transports/sms.ts +4 -0
  107. package/src/runtime/channel-invite-transports/telegram.ts +6 -0
  108. package/src/runtime/confirmation-request-guardian-bridge.ts +0 -1
  109. package/src/runtime/guardian-action-followup-executor.ts +3 -2
  110. package/src/runtime/guardian-action-grant-minter.ts +0 -1
  111. package/src/runtime/guardian-outbound-actions.ts +2 -12
  112. package/src/runtime/guardian-vellum-migration.ts +2 -3
  113. package/src/runtime/http-server.ts +0 -1
  114. package/src/runtime/invite-redemption-service.ts +1 -14
  115. package/src/runtime/local-actor-identity.ts +2 -5
  116. package/src/runtime/routes/access-request-decision.ts +0 -1
  117. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +0 -9
  118. package/src/runtime/routes/channel-readiness-routes.ts +29 -18
  119. package/src/runtime/routes/contact-routes.ts +15 -40
  120. package/src/runtime/routes/conversation-attention-routes.ts +0 -2
  121. package/src/runtime/routes/global-search-routes.ts +0 -2
  122. package/src/runtime/routes/guardian-bootstrap-routes.ts +5 -6
  123. package/src/runtime/routes/guardian-expiry-sweep.ts +3 -2
  124. package/src/runtime/routes/inbound-message-handler.ts +0 -3
  125. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -43
  126. package/src/runtime/routes/inbound-stages/background-dispatch.ts +1 -4
  127. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -6
  128. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +0 -1
  129. package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +0 -1
  130. package/src/runtime/routes/inbound-stages/verification-intercept.ts +3 -7
  131. package/src/runtime/routes/pairing-routes.ts +4 -4
  132. package/src/runtime/tool-grant-request-helper.ts +0 -1
  133. package/src/tools/browser/browser-manager.ts +22 -21
  134. package/src/tools/browser/runtime-check.ts +111 -6
  135. package/src/tools/calls/call-start.ts +1 -3
  136. package/src/tools/followups/followup_create.ts +1 -2
  137. package/src/tools/tool-approval-handler.ts +0 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vellumai/assistant",
3
- "version": "0.4.33",
3
+ "version": "0.4.34",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "vellum": "./src/index.ts"
@@ -99,7 +99,6 @@ function createTestApproval(overrides: Record<string, unknown> = {}) {
99
99
  return createApprovalRequest({
100
100
  runId: `ingress-access-request-${Date.now()}`,
101
101
  conversationId: `access-req-telegram-user-unknown-456`,
102
- assistantId: "self",
103
102
  channel: "telegram",
104
103
  requesterExternalUserId: "user-unknown-456",
105
104
  requesterChatId: "chat-123",
@@ -157,7 +156,7 @@ describe("access request decision handler", () => {
157
156
  expect(result.type).toBe("approved");
158
157
 
159
158
  // There should be an active session for this channel
160
- const session = findActiveSession("self", "telegram");
159
+ const session = findActiveSession("telegram");
161
160
  expect(session).not.toBeNull();
162
161
  expect(session!.expectedExternalUserId).toBe("user-unknown-456");
163
162
  expect(session!.expectedChatId).toBe("chat-123");
@@ -186,7 +185,7 @@ describe("access request decision handler", () => {
186
185
  expect(updated!.decidedByExternalUserId).toBe("guardian-user-789");
187
186
 
188
187
  // No verification session should be created
189
- const session = findActiveSession("self", "telegram");
188
+ const session = findActiveSession("telegram");
190
189
  expect(session).toBeNull();
191
190
  });
192
191
 
@@ -129,7 +129,6 @@ describe("actor-token store (hash-only)", () => {
129
129
 
130
130
  const record = createActorTokenRecord({
131
131
  tokenHash,
132
- assistantId: "self",
133
132
  guardianPrincipalId: "principal-store",
134
133
  hashedDeviceId: "hashed-dev-store",
135
134
  platform: "macos",
@@ -148,7 +147,6 @@ describe("actor-token store (hash-only)", () => {
148
147
 
149
148
  createActorTokenRecord({
150
149
  tokenHash,
151
- assistantId: "self",
152
150
  guardianPrincipalId: "principal-bind",
153
151
  hashedDeviceId: "hashed-dev-bind",
154
152
  platform: "ios",
@@ -156,7 +154,6 @@ describe("actor-token store (hash-only)", () => {
156
154
  });
157
155
 
158
156
  const found = findActiveByDeviceBinding(
159
- "self",
160
157
  "principal-bind",
161
158
  "hashed-dev-bind",
162
159
  );
@@ -169,7 +166,6 @@ describe("actor-token store (hash-only)", () => {
169
166
 
170
167
  createActorTokenRecord({
171
168
  tokenHash,
172
- assistantId: "self",
173
169
  guardianPrincipalId: "principal-revoke",
174
170
  hashedDeviceId: "hashed-dev-revoke",
175
171
  platform: "macos",
@@ -177,7 +173,6 @@ describe("actor-token store (hash-only)", () => {
177
173
  });
178
174
 
179
175
  const count = revokeByDeviceBinding(
180
- "self",
181
176
  "principal-revoke",
182
177
  "hashed-dev-revoke",
183
178
  );
@@ -192,7 +187,6 @@ describe("actor-token store (hash-only)", () => {
192
187
 
193
188
  createActorTokenRecord({
194
189
  tokenHash,
195
- assistantId: "self",
196
190
  guardianPrincipalId: "principal-single",
197
191
  hashedDeviceId: "hashed-dev-single",
198
192
  platform: "macos",
@@ -213,7 +207,7 @@ describe("guardian vellum migration", () => {
213
207
  const principalId = ensureVellumGuardianBinding("self");
214
208
  expect(principalId).toMatch(/^vellum-principal-/);
215
209
 
216
- const guardianResult = findGuardianForChannel("vellum", "self");
210
+ const guardianResult = findGuardianForChannel("vellum");
217
211
  expect(guardianResult).not.toBeNull();
218
212
  expect(guardianResult!.contact.principalId).toBe(principalId);
219
213
  expect(guardianResult!.channel.verifiedVia).toBe("startup-migration");
@@ -227,7 +221,6 @@ describe("guardian vellum migration", () => {
227
221
 
228
222
  test("ensureVellumGuardianBinding preserves existing bindings for other channels", () => {
229
223
  createGuardianBinding({
230
- assistantId: "self",
231
224
  channel: "telegram",
232
225
  guardianExternalUserId: "tg-user-123",
233
226
  guardianDeliveryChatId: "tg-chat-456",
@@ -237,11 +230,11 @@ describe("guardian vellum migration", () => {
237
230
 
238
231
  ensureVellumGuardianBinding("self");
239
232
 
240
- const tgGuardian = findGuardianForChannel("telegram", "self");
233
+ const tgGuardian = findGuardianForChannel("telegram");
241
234
  expect(tgGuardian).not.toBeNull();
242
235
  expect(tgGuardian!.channel.externalUserId).toBe("tg-user-123");
243
236
 
244
- const vGuardian = findGuardianForChannel("vellum", "self");
237
+ const vGuardian = findGuardianForChannel("vellum");
245
238
  expect(vGuardian).not.toBeNull();
246
239
  });
247
240
  });
@@ -439,7 +432,7 @@ describe("resolveLocalIpcAuthContext", () => {
439
432
 
440
433
  test("enriches actorPrincipalId from vellum guardian binding when present", () => {
441
434
  ensureVellumGuardianBinding("self");
442
- const guardianResult = findGuardianForChannel("vellum", "self");
435
+ const guardianResult = findGuardianForChannel("vellum");
443
436
  expect(guardianResult).toBeTruthy();
444
437
 
445
438
  const ctx = resolveLocalIpcAuthContext("session-123");
@@ -60,7 +60,6 @@ afterAll(() => {
60
60
  function mintParams(overrides: Partial<MintGrantParams> = {}): MintGrantParams {
61
61
  const futureExpiry = new Date(Date.now() + 60_000).toISOString();
62
62
  return {
63
- assistantId: "self",
64
63
  scopeMode: "request_id",
65
64
  requestChannel: "telegram",
66
65
  decisionChannel: "telegram",
@@ -177,7 +176,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
177
176
  toolName: "shell",
178
177
  inputDigest: computeToolApprovalDigest("shell", { command: "ls" }),
179
178
  consumingRequestId: "consumer-1",
180
- assistantId: "self",
181
179
  });
182
180
 
183
181
  expect(result.ok).toBe(true);
@@ -200,7 +198,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
200
198
  toolName: "shell",
201
199
  inputDigest: digest,
202
200
  consumingRequestId: "consumer-2",
203
- assistantId: "self",
204
201
  });
205
202
 
206
203
  expect(result.ok).toBe(true);
@@ -224,7 +221,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
224
221
  toolName: "shell",
225
222
  inputDigest: digest,
226
223
  consumingRequestId: "consumer-3",
227
- assistantId: "self",
228
224
  });
229
225
 
230
226
  expect(result.ok).toBe(true);
@@ -242,7 +238,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
242
238
  toolName: "shell",
243
239
  inputDigest: computeToolApprovalDigest("shell", { command: "ls" }),
244
240
  consumingRequestId: "consumer-miss",
245
- assistantId: "self",
246
241
  },
247
242
  { maxWaitMs: 0 },
248
243
  );
@@ -267,7 +262,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
267
262
  toolName: "file_write",
268
263
  inputDigest: digest,
269
264
  consumingRequestId: "consumer-mismatch-tool",
270
- assistantId: "self",
271
265
  },
272
266
  { maxWaitMs: 0 },
273
267
  );
@@ -293,32 +287,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
293
287
  command: "rm -rf /",
294
288
  }),
295
289
  consumingRequestId: "consumer-mismatch-input",
296
- assistantId: "self",
297
- },
298
- { maxWaitMs: 0 },
299
- );
300
-
301
- expect(result.ok).toBe(false);
302
- if (result.ok) return;
303
- expect(result.reason).toBe("no_match");
304
- });
305
-
306
- test("miss: assistant ID mismatch", async () => {
307
- mintGrantFromDecision(
308
- mintParams({
309
- scopeMode: "request_id",
310
- requestId: "req-assist",
311
- assistantId: "assistant-A",
312
- }),
313
- );
314
-
315
- const result = await consumeGrantForInvocation(
316
- {
317
- requestId: "req-assist",
318
- toolName: "shell",
319
- inputDigest: computeToolApprovalDigest("shell", {}),
320
- consumingRequestId: "consumer-assist-mismatch",
321
- assistantId: "assistant-B",
322
290
  },
323
291
  { maxWaitMs: 0 },
324
292
  );
@@ -344,7 +312,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
344
312
  toolName: "shell",
345
313
  inputDigest: computeToolApprovalDigest("shell", {}),
346
314
  consumingRequestId: "consumer-expired",
347
- assistantId: "self",
348
315
  },
349
316
  { maxWaitMs: 0 },
350
317
  );
@@ -368,7 +335,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
368
335
  toolName: "shell",
369
336
  inputDigest: computeToolApprovalDigest("shell", {}),
370
337
  consumingRequestId: "consumer-first",
371
- assistantId: "self",
372
338
  });
373
339
  expect(first.ok).toBe(true);
374
340
 
@@ -378,7 +344,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
378
344
  toolName: "shell",
379
345
  inputDigest: computeToolApprovalDigest("shell", {}),
380
346
  consumingRequestId: "consumer-second",
381
- assistantId: "self",
382
347
  },
383
348
  { maxWaitMs: 0 },
384
349
  );
@@ -401,7 +366,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
401
366
  toolName: "shell",
402
367
  inputDigest: digest,
403
368
  consumingRequestId: "consumer-sig-first",
404
- assistantId: "self",
405
369
  });
406
370
  expect(first.ok).toBe(true);
407
371
 
@@ -410,7 +374,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
410
374
  toolName: "shell",
411
375
  inputDigest: digest,
412
376
  consumingRequestId: "consumer-sig-second",
413
- assistantId: "self",
414
377
  },
415
378
  { maxWaitMs: 0 },
416
379
  );
@@ -439,7 +402,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
439
402
  toolName: "shell",
440
403
  inputDigest: digest,
441
404
  consumingRequestId: "consumer-ctx",
442
- assistantId: "self",
443
405
  conversationId: "conv-ctx",
444
406
  callSessionId: "call-ctx",
445
407
  });
@@ -463,7 +425,6 @@ describe("approval-primitive / consumeGrantForInvocation", () => {
463
425
  toolName: "shell",
464
426
  inputDigest: digest,
465
427
  consumingRequestId: "consumer-ctx-mismatch",
466
- assistantId: "self",
467
428
  conversationId: "conv-B",
468
429
  },
469
430
  { maxWaitMs: 0 },
@@ -497,7 +458,6 @@ describe("approval-primitive / consumeGrantForInvocation retry", () => {
497
458
  toolName: "shell",
498
459
  inputDigest: digest,
499
460
  consumingRequestId: "consumer-async-immediate",
500
- assistantId: "self",
501
461
  });
502
462
  const elapsed = Date.now() - start;
503
463
 
@@ -528,7 +488,6 @@ describe("approval-primitive / consumeGrantForInvocation retry", () => {
528
488
  toolName: "shell",
529
489
  inputDigest: digest,
530
490
  consumingRequestId: "consumer-async-delayed",
531
- assistantId: "self",
532
491
  },
533
492
  { maxWaitMs: 5_000, intervalMs: 100 },
534
493
  );
@@ -553,7 +512,6 @@ describe("approval-primitive / consumeGrantForInvocation retry", () => {
553
512
  toolName: "shell",
554
513
  inputDigest: digest,
555
514
  consumingRequestId: "consumer-async-timeout",
556
- assistantId: "self",
557
515
  },
558
516
  { maxWaitMs: 500, intervalMs: 100 },
559
517
  );
@@ -580,7 +538,6 @@ describe("approval-primitive / consumeGrantForInvocation retry", () => {
580
538
  toolName: "shell",
581
539
  inputDigest: digest,
582
540
  consumingRequestId: "consumer-aborted",
583
- assistantId: "self",
584
541
  },
585
542
  { maxWaitMs: 2_000, intervalMs: 50, signal: controller.signal },
586
543
  );
@@ -606,7 +563,6 @@ describe("approval-primitive / consumeGrantForInvocation retry", () => {
606
563
  toolName: "shell",
607
564
  inputDigest: digest,
608
565
  consumingRequestId: "consumer-pre-aborted",
609
- assistantId: "self",
610
566
  },
611
567
  { maxWaitMs: 2_000, intervalMs: 50, signal: controller.signal },
612
568
  );
@@ -628,7 +584,6 @@ describe("approval-primitive / consumeGrantForInvocation retry", () => {
628
584
  toolName: "shell",
629
585
  inputDigest: digest,
630
586
  consumingRequestId: "consumer-no-retry",
631
- assistantId: "self",
632
587
  },
633
588
  { maxWaitMs: 0 },
634
589
  );
@@ -18,6 +18,8 @@ import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
18
18
  * - No assistant-scoped route handlers in the daemon HTTP server
19
19
  * - No hardcoded `'self'` string for assistant scoping (use the constant)
20
20
  * - The constant itself equals `'self'`
21
+ * - No `assistantId` columns in daemon SQLite schema definitions
22
+ * - No `assistantId` parameter in daemon store function signatures
21
23
  */
22
24
 
23
25
  // ---------------------------------------------------------------------------
@@ -466,4 +468,152 @@ describe("assistant ID boundary", () => {
466
468
  ).not.toContain("assistantId");
467
469
  }
468
470
  });
471
+
472
+ // -------------------------------------------------------------------------
473
+ // Rule (f): No assistantId columns in daemon SQLite schema definitions
474
+ //
475
+ // The daemon is assistant-agnostic — it uses DAEMON_INTERNAL_ASSISTANT_ID
476
+ // implicitly. Schema files must not define assistantId columns, which would
477
+ // re-introduce assistant-scoped storage in the daemon layer.
478
+ // -------------------------------------------------------------------------
479
+
480
+ test("no assistantId columns in daemon SQLite schema definitions", () => {
481
+ const repoRoot = getRepoRoot();
482
+
483
+ // Scan all Drizzle schema files for assistantId column definitions.
484
+ // The Drizzle ORM pattern is `assistantId: text(` for defining a text
485
+ // column named assistantId.
486
+ const schemaGlobs = [
487
+ "assistant/src/memory/schema/*.ts",
488
+ "assistant/src/memory/schema/**/*.ts",
489
+ ];
490
+
491
+ let grepOutput = "";
492
+ try {
493
+ grepOutput = execFileSync(
494
+ "git",
495
+ ["grep", "-nE", "assistantId\\s*:\\s*text\\(", "--", ...schemaGlobs],
496
+ { encoding: "utf-8", cwd: repoRoot },
497
+ ).trim();
498
+ } catch (err) {
499
+ // Exit code 1 means no matches — happy path
500
+ if ((err as { status?: number }).status === 1) {
501
+ return;
502
+ }
503
+ throw err;
504
+ }
505
+
506
+ const lines = grepOutput.split("\n").filter((l) => l.length > 0);
507
+ const violations = lines.filter((line) => {
508
+ // Allow comments
509
+ const parts = line.split(":");
510
+ const content = parts.slice(2).join(":").trim();
511
+ if (
512
+ content.startsWith("//") ||
513
+ content.startsWith("*") ||
514
+ content.startsWith("/*")
515
+ ) {
516
+ return false;
517
+ }
518
+ return true;
519
+ });
520
+
521
+ if (violations.length > 0) {
522
+ const message = [
523
+ "Found `assistantId` column definitions in daemon SQLite schema files.",
524
+ "`assistantId` columns are not allowed in daemon schema — the daemon uses",
525
+ "`DAEMON_INTERNAL_ASSISTANT_ID` implicitly and is assistant-agnostic.",
526
+ "",
527
+ "Violations:",
528
+ ...violations.map((v) => ` - ${v}`),
529
+ ].join("\n");
530
+
531
+ expect(violations, message).toEqual([]);
532
+ }
533
+ });
534
+
535
+ // -------------------------------------------------------------------------
536
+ // Rule (g): No assistantId parameter in daemon store function signatures
537
+ //
538
+ // Store functions in the daemon layer must not accept assistantId as a
539
+ // parameter. The daemon is assistant-agnostic — all assistant scoping
540
+ // uses DAEMON_INTERNAL_ASSISTANT_ID internally.
541
+ // -------------------------------------------------------------------------
542
+
543
+ test("no assistantId parameter in daemon store function signatures", () => {
544
+ const repoRoot = getRepoRoot();
545
+
546
+ // Scan store files for exported function signatures that include
547
+ // assistantId as a parameter. This covers memory stores, contact stores,
548
+ // notification stores, credential/token stores, and call stores.
549
+ const storeGlobs = [
550
+ "assistant/src/memory/*.ts",
551
+ "assistant/src/contacts/*.ts",
552
+ "assistant/src/notifications/*.ts",
553
+ "assistant/src/runtime/auth/credential-service.ts",
554
+ "assistant/src/runtime/actor-token-store.ts",
555
+ "assistant/src/runtime/actor-refresh-token-store.ts",
556
+ "assistant/src/calls/call-store.ts",
557
+ ];
558
+
559
+ // Match exported function declarations/expressions with assistantId in
560
+ // their parameter lists. Patterns:
561
+ // export function foo(assistantId
562
+ // export function foo(bar, assistantId
563
+ // export async function foo(assistantId
564
+ // export const foo = (assistantId
565
+ // export const foo = async (assistantId
566
+ // We use a broad pattern that catches assistantId appearing after an
567
+ // opening paren in an export context.
568
+ const pattern =
569
+ "export\\s+(async\\s+)?function\\s+\\w+\\s*\\([^)]*assistantId|export\\s+const\\s+\\w+\\s*=\\s*(async\\s+)?\\([^)]*assistantId";
570
+
571
+ let grepOutput = "";
572
+ try {
573
+ grepOutput = execFileSync(
574
+ "git",
575
+ ["grep", "-nE", pattern, "--", ...storeGlobs],
576
+ { encoding: "utf-8", cwd: repoRoot },
577
+ ).trim();
578
+ } catch (err) {
579
+ // Exit code 1 means no matches — happy path
580
+ if ((err as { status?: number }).status === 1) {
581
+ return;
582
+ }
583
+ throw err;
584
+ }
585
+
586
+ const allLines = grepOutput.split("\n").filter((l) => l.length > 0);
587
+ const violations = allLines.filter((line) => {
588
+ const filePath = line.split(":")[0];
589
+ if (isTestFile(filePath)) return false;
590
+ if (isMigrationFile(filePath)) return false;
591
+
592
+ // Allow comments
593
+ const parts = line.split(":");
594
+ const content = parts.slice(2).join(":").trim();
595
+ if (
596
+ content.startsWith("//") ||
597
+ content.startsWith("*") ||
598
+ content.startsWith("/*")
599
+ ) {
600
+ return false;
601
+ }
602
+
603
+ return true;
604
+ });
605
+
606
+ if (violations.length > 0) {
607
+ const message = [
608
+ "Found daemon store functions with `assistantId` in their parameter signatures.",
609
+ "Store functions must not accept `assistantId` — the daemon is assistant-agnostic",
610
+ "and uses `DAEMON_INTERNAL_ASSISTANT_ID` implicitly.",
611
+ "",
612
+ "Violations:",
613
+ ...violations.map((v) => ` - ${v}`),
614
+ ].join("\n");
615
+
616
+ expect(violations, message).toEqual([]);
617
+ }
618
+ });
469
619
  });
@@ -15,7 +15,6 @@ function buildSignal(
15
15
  ): NotificationSignal {
16
16
  return {
17
17
  signalId: "test-signal-1",
18
- assistantId: "self",
19
18
  createdAt: Date.now(),
20
19
  sourceChannel: "voice",
21
20
  sourceSessionId: "test-session-1",