@vellumai/assistant 0.4.3 → 0.4.4

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 (183) hide show
  1. package/.env.example +3 -0
  2. package/ARCHITECTURE.md +40 -3
  3. package/README.md +43 -35
  4. package/package.json +1 -1
  5. package/scripts/ipc/generate-swift.ts +1 -0
  6. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +58 -120
  7. package/src/__tests__/actor-token-service.test.ts +1099 -0
  8. package/src/__tests__/agent-loop.test.ts +51 -0
  9. package/src/__tests__/approval-routes-http.test.ts +2 -0
  10. package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -5
  11. package/src/__tests__/assistant-id-boundary-guard.test.ts +125 -0
  12. package/src/__tests__/call-controller.test.ts +49 -0
  13. package/src/__tests__/call-pointer-message-composer.test.ts +171 -0
  14. package/src/__tests__/call-pointer-messages.test.ts +93 -3
  15. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +42 -0
  16. package/src/__tests__/callback-handoff-copy.test.ts +186 -0
  17. package/src/__tests__/channel-approval-routes.test.ts +133 -12
  18. package/src/__tests__/channel-guardian.test.ts +0 -87
  19. package/src/__tests__/channel-readiness-service.test.ts +10 -16
  20. package/src/__tests__/checker.test.ts +33 -12
  21. package/src/__tests__/config-schema.test.ts +4 -0
  22. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +410 -0
  23. package/src/__tests__/conversation-routes-guardian-reply.test.ts +256 -0
  24. package/src/__tests__/conversation-routes.test.ts +12 -3
  25. package/src/__tests__/credential-security-invariants.test.ts +1 -1
  26. package/src/__tests__/daemon-server-session-init.test.ts +4 -0
  27. package/src/__tests__/guardian-actions-endpoint.test.ts +19 -14
  28. package/src/__tests__/guardian-dispatch.test.ts +8 -0
  29. package/src/__tests__/guardian-outbound-http.test.ts +4 -4
  30. package/src/__tests__/guardian-question-mode.test.ts +200 -0
  31. package/src/__tests__/guardian-routing-invariants.test.ts +178 -0
  32. package/src/__tests__/guardian-routing-state.test.ts +525 -0
  33. package/src/__tests__/handle-user-message-secret-resume.test.ts +2 -0
  34. package/src/__tests__/handlers-telegram-config.test.ts +0 -83
  35. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +55 -0
  36. package/src/__tests__/headless-browser-navigate.test.ts +2 -0
  37. package/src/__tests__/ipc-snapshot.test.ts +18 -51
  38. package/src/__tests__/non-member-access-request.test.ts +131 -8
  39. package/src/__tests__/notification-decision-fallback.test.ts +129 -4
  40. package/src/__tests__/notification-decision-strategy.test.ts +62 -2
  41. package/src/__tests__/notification-guardian-path.test.ts +3 -0
  42. package/src/__tests__/recording-intent-handler.test.ts +1 -0
  43. package/src/__tests__/relay-server.test.ts +841 -39
  44. package/src/__tests__/send-endpoint-busy.test.ts +5 -0
  45. package/src/__tests__/session-agent-loop.test.ts +1 -0
  46. package/src/__tests__/session-confirmation-signals.test.ts +523 -0
  47. package/src/__tests__/session-init.benchmark.test.ts +0 -1
  48. package/src/__tests__/session-surfaces-task-progress.test.ts +1 -1
  49. package/src/__tests__/session-tool-setup-app-refresh.test.ts +81 -2
  50. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -1
  51. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -1
  52. package/src/__tests__/tool-executor.test.ts +21 -2
  53. package/src/__tests__/tool-grant-request-escalation.test.ts +333 -27
  54. package/src/__tests__/trusted-contact-approval-notifier.test.ts +678 -0
  55. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1064 -0
  56. package/src/__tests__/twilio-config.test.ts +2 -13
  57. package/src/agent/loop.ts +1 -1
  58. package/src/approvals/guardian-decision-primitive.ts +10 -2
  59. package/src/approvals/guardian-request-resolvers.ts +128 -9
  60. package/src/calls/call-constants.ts +21 -0
  61. package/src/calls/call-controller.ts +9 -2
  62. package/src/calls/call-domain.ts +28 -7
  63. package/src/calls/call-pointer-message-composer.ts +154 -0
  64. package/src/calls/call-pointer-messages.ts +106 -27
  65. package/src/calls/guardian-dispatch.ts +4 -2
  66. package/src/calls/relay-server.ts +424 -12
  67. package/src/calls/twilio-config.ts +4 -11
  68. package/src/calls/twilio-routes.ts +1 -1
  69. package/src/calls/types.ts +3 -1
  70. package/src/cli.ts +5 -4
  71. package/src/config/bundled-skills/agentmail/SKILL.md +4 -0
  72. package/src/config/bundled-skills/app-builder/SKILL.md +146 -10
  73. package/src/config/bundled-skills/app-builder/TOOLS.json +1 -1
  74. package/src/config/bundled-skills/email-setup/SKILL.md +1 -1
  75. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +105 -81
  76. package/src/config/bundled-skills/messaging/SKILL.md +61 -12
  77. package/src/config/bundled-skills/messaging/TOOLS.json +58 -0
  78. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +6 -1
  79. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +35 -0
  80. package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +52 -0
  81. package/src/config/bundled-skills/phone-calls/SKILL.md +30 -39
  82. package/src/config/bundled-skills/twitter/SKILL.md +3 -3
  83. package/src/config/bundled-skills/vercel-token-setup/SKILL.md +1 -0
  84. package/src/config/calls-schema.ts +24 -0
  85. package/src/config/env.ts +22 -0
  86. package/src/config/feature-flag-registry.json +8 -0
  87. package/src/config/schema.ts +2 -2
  88. package/src/config/skills.ts +11 -0
  89. package/src/config/system-prompt.ts +11 -1
  90. package/src/config/templates/SOUL.md +2 -0
  91. package/src/config/vellum-skills/sms-setup/SKILL.md +71 -82
  92. package/src/config/vellum-skills/trusted-contacts/SKILL.md +10 -9
  93. package/src/config/vellum-skills/twilio-setup/SKILL.md +88 -73
  94. package/src/daemon/call-pointer-generators.ts +59 -0
  95. package/src/daemon/computer-use-session.ts +2 -5
  96. package/src/daemon/handlers/apps.ts +76 -20
  97. package/src/daemon/handlers/config-channels.ts +5 -55
  98. package/src/daemon/handlers/config-inbox.ts +9 -3
  99. package/src/daemon/handlers/config-ingress.ts +28 -3
  100. package/src/daemon/handlers/config-telegram.ts +12 -0
  101. package/src/daemon/handlers/config.ts +2 -6
  102. package/src/daemon/handlers/pairing.ts +2 -0
  103. package/src/daemon/handlers/sessions.ts +48 -3
  104. package/src/daemon/handlers/shared.ts +17 -2
  105. package/src/daemon/ipc-contract/integrations.ts +1 -99
  106. package/src/daemon/ipc-contract/messages.ts +47 -1
  107. package/src/daemon/ipc-contract/notifications.ts +11 -0
  108. package/src/daemon/ipc-contract-inventory.json +2 -4
  109. package/src/daemon/lifecycle.ts +17 -0
  110. package/src/daemon/server.ts +14 -1
  111. package/src/daemon/session-agent-loop-handlers.ts +20 -0
  112. package/src/daemon/session-agent-loop.ts +22 -11
  113. package/src/daemon/session-lifecycle.ts +1 -1
  114. package/src/daemon/session-process.ts +11 -1
  115. package/src/daemon/session-runtime-assembly.ts +3 -0
  116. package/src/daemon/session-surfaces.ts +3 -2
  117. package/src/daemon/session.ts +88 -1
  118. package/src/daemon/tool-side-effects.ts +22 -0
  119. package/src/home-base/prebuilt/brain-graph.html +1483 -0
  120. package/src/home-base/prebuilt/index.html +40 -0
  121. package/src/inbound/platform-callback-registration.ts +157 -0
  122. package/src/memory/canonical-guardian-store.ts +1 -1
  123. package/src/memory/db-init.ts +4 -0
  124. package/src/memory/migrations/038-actor-token-records.ts +39 -0
  125. package/src/memory/migrations/index.ts +1 -0
  126. package/src/memory/schema.ts +16 -0
  127. package/src/messaging/provider-types.ts +24 -0
  128. package/src/messaging/provider.ts +7 -0
  129. package/src/messaging/providers/gmail/adapter.ts +127 -0
  130. package/src/messaging/providers/sms/adapter.ts +40 -37
  131. package/src/notifications/adapters/macos.ts +45 -2
  132. package/src/notifications/broadcaster.ts +16 -0
  133. package/src/notifications/copy-composer.ts +39 -1
  134. package/src/notifications/decision-engine.ts +22 -9
  135. package/src/notifications/destination-resolver.ts +16 -2
  136. package/src/notifications/emit-signal.ts +16 -8
  137. package/src/notifications/guardian-question-mode.ts +419 -0
  138. package/src/notifications/signal.ts +14 -3
  139. package/src/permissions/checker.ts +13 -1
  140. package/src/permissions/prompter.ts +14 -0
  141. package/src/providers/anthropic/client.ts +20 -0
  142. package/src/providers/provider-send-message.ts +15 -3
  143. package/src/runtime/access-request-helper.ts +71 -1
  144. package/src/runtime/actor-token-service.ts +234 -0
  145. package/src/runtime/actor-token-store.ts +236 -0
  146. package/src/runtime/channel-approvals.ts +5 -3
  147. package/src/runtime/channel-readiness-service.ts +23 -64
  148. package/src/runtime/channel-readiness-types.ts +3 -4
  149. package/src/runtime/channel-retry-sweep.ts +4 -1
  150. package/src/runtime/confirmation-request-guardian-bridge.ts +197 -0
  151. package/src/runtime/guardian-action-followup-executor.ts +1 -1
  152. package/src/runtime/guardian-context-resolver.ts +82 -0
  153. package/src/runtime/guardian-outbound-actions.ts +0 -3
  154. package/src/runtime/guardian-reply-router.ts +67 -30
  155. package/src/runtime/guardian-vellum-migration.ts +57 -0
  156. package/src/runtime/http-server.ts +65 -12
  157. package/src/runtime/http-types.ts +13 -0
  158. package/src/runtime/invite-redemption-service.ts +8 -0
  159. package/src/runtime/local-actor-identity.ts +76 -0
  160. package/src/runtime/middleware/actor-token.ts +271 -0
  161. package/src/runtime/routes/approval-routes.ts +82 -7
  162. package/src/runtime/routes/brain-graph-routes.ts +222 -0
  163. package/src/runtime/routes/channel-readiness-routes.ts +71 -0
  164. package/src/runtime/routes/conversation-routes.ts +140 -52
  165. package/src/runtime/routes/events-routes.ts +20 -5
  166. package/src/runtime/routes/guardian-action-routes.ts +45 -3
  167. package/src/runtime/routes/guardian-approval-interception.ts +29 -0
  168. package/src/runtime/routes/guardian-bootstrap-routes.ts +145 -0
  169. package/src/runtime/routes/inbound-message-handler.ts +143 -2
  170. package/src/runtime/routes/integration-routes.ts +7 -15
  171. package/src/runtime/routes/pairing-routes.ts +163 -0
  172. package/src/runtime/routes/twilio-routes.ts +934 -0
  173. package/src/runtime/tool-grant-request-helper.ts +3 -1
  174. package/src/security/oauth2.ts +27 -2
  175. package/src/security/token-manager.ts +46 -10
  176. package/src/tools/browser/browser-execution.ts +4 -3
  177. package/src/tools/browser/browser-handoff.ts +10 -18
  178. package/src/tools/browser/browser-manager.ts +80 -25
  179. package/src/tools/browser/browser-screencast.ts +35 -119
  180. package/src/tools/permission-checker.ts +15 -4
  181. package/src/tools/tool-approval-handler.ts +242 -18
  182. package/src/__tests__/handlers-twilio-config.test.ts +0 -1928
  183. package/src/daemon/handlers/config-twilio.ts +0 -1082
@@ -1338,7 +1338,6 @@ describe('IPC handler channel-aware guardian status', () => {
1338
1338
  type: 'guardian_verification',
1339
1339
  action: 'status',
1340
1340
  channel: 'telegram',
1341
- assistantId: 'self',
1342
1341
  };
1343
1342
 
1344
1343
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -1358,7 +1357,6 @@ describe('IPC handler channel-aware guardian status', () => {
1358
1357
  type: 'guardian_verification',
1359
1358
  action: 'status',
1360
1359
  channel: 'sms',
1361
- assistantId: 'self',
1362
1360
  };
1363
1361
 
1364
1362
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -1384,7 +1382,6 @@ describe('IPC handler channel-aware guardian status', () => {
1384
1382
  type: 'guardian_verification',
1385
1383
  action: 'status',
1386
1384
  channel: 'telegram',
1387
- assistantId: 'self',
1388
1385
  };
1389
1386
 
1390
1387
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -1413,7 +1410,6 @@ describe('IPC handler channel-aware guardian status', () => {
1413
1410
  type: 'guardian_verification',
1414
1411
  action: 'status',
1415
1412
  channel: 'telegram',
1416
- assistantId: 'self',
1417
1413
  };
1418
1414
 
1419
1415
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -1457,36 +1453,6 @@ describe('IPC handler channel-aware guardian status', () => {
1457
1453
  expect(resp!.channel).toBe('sms');
1458
1454
  });
1459
1455
 
1460
- test('status action with custom assistantId is ignored (daemon uses internal scope)', () => {
1461
- // Create binding under the internal scope constant — the handler always
1462
- // uses DAEMON_INTERNAL_ASSISTANT_ID regardless of what the caller passes.
1463
- createBinding({
1464
- assistantId: 'self',
1465
- channel: 'telegram',
1466
- guardianExternalUserId: 'user-77',
1467
- guardianDeliveryChatId: 'chat-77',
1468
- });
1469
-
1470
- const { ctx, lastResponse } = createMockCtx();
1471
- const msg: GuardianVerificationRequest = {
1472
- type: 'guardian_verification',
1473
- action: 'status',
1474
- channel: 'telegram',
1475
- assistantId: 'asst-custom', // ignored by handler
1476
- };
1477
-
1478
- handleGuardianVerification(msg, mockSocket, ctx);
1479
-
1480
- const resp = lastResponse();
1481
- expect(resp).not.toBeNull();
1482
- expect(resp!.success).toBe(true);
1483
- expect(resp!.bound).toBe(true);
1484
- expect(resp!.assistantId).toBe('self');
1485
- expect(resp!.channel).toBe('telegram');
1486
- expect(resp!.guardianExternalUserId).toBe('user-77');
1487
- expect(resp!.guardianDeliveryChatId).toBe('chat-77');
1488
- });
1489
-
1490
1456
  test('status action for unbound sms does not return guardianDeliveryChatId', () => {
1491
1457
  const { ctx, lastResponse } = createMockCtx();
1492
1458
  const msg: GuardianVerificationRequest = {
@@ -1512,7 +1478,6 @@ describe('IPC handler channel-aware guardian status', () => {
1512
1478
  type: 'guardian_verification',
1513
1479
  action: 'status',
1514
1480
  channel: 'voice',
1515
- assistantId: 'self',
1516
1481
  };
1517
1482
 
1518
1483
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -1529,7 +1494,6 @@ describe('IPC handler channel-aware guardian status', () => {
1529
1494
  type: 'guardian_verification',
1530
1495
  action: 'status',
1531
1496
  channel: 'voice',
1532
- assistantId: 'self',
1533
1497
  };
1534
1498
 
1535
1499
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -1944,7 +1908,6 @@ describe('IPC handler voice guardian verification', () => {
1944
1908
  type: 'guardian_verification',
1945
1909
  action: 'create_challenge',
1946
1910
  channel: 'voice',
1947
- assistantId: 'self',
1948
1911
  };
1949
1912
 
1950
1913
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -1965,7 +1928,6 @@ describe('IPC handler voice guardian verification', () => {
1965
1928
  type: 'guardian_verification',
1966
1929
  action: 'status',
1967
1930
  channel: 'voice',
1968
- assistantId: 'self',
1969
1931
  };
1970
1932
 
1971
1933
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -1991,7 +1953,6 @@ describe('IPC handler voice guardian verification', () => {
1991
1953
  type: 'guardian_verification',
1992
1954
  action: 'status',
1993
1955
  channel: 'voice',
1994
- assistantId: 'self',
1995
1956
  };
1996
1957
 
1997
1958
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -2018,7 +1979,6 @@ describe('IPC handler voice guardian verification', () => {
2018
1979
  type: 'guardian_verification',
2019
1980
  action: 'revoke',
2020
1981
  channel: 'voice',
2021
- assistantId: 'self',
2022
1982
  };
2023
1983
 
2024
1984
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -2051,7 +2011,6 @@ describe('IPC handler voice guardian verification', () => {
2051
2011
  type: 'guardian_verification',
2052
2012
  action: 'revoke',
2053
2013
  channel: 'voice',
2054
- assistantId: 'self',
2055
2014
  };
2056
2015
 
2057
2016
  handleGuardianVerification(msg, mockSocket, ctx);
@@ -2516,7 +2475,6 @@ describe('outbound SMS verification', () => {
2516
2475
  type: 'guardian_verification',
2517
2476
  action: 'start_outbound',
2518
2477
  channel: 'sms',
2519
- assistantId: 'self',
2520
2478
  destination: '+15551234567',
2521
2479
  };
2522
2480
 
@@ -2553,7 +2511,6 @@ describe('outbound SMS verification', () => {
2553
2511
  type: 'guardian_verification',
2554
2512
  action: 'start_outbound',
2555
2513
  channel: 'sms',
2556
- assistantId: 'self',
2557
2514
  destination: '+15559876543',
2558
2515
  rebind: false,
2559
2516
  };
@@ -2580,7 +2537,6 @@ describe('outbound SMS verification', () => {
2580
2537
  type: 'guardian_verification',
2581
2538
  action: 'start_outbound',
2582
2539
  channel: 'sms',
2583
- assistantId: 'self',
2584
2540
  destination: '+15559876543',
2585
2541
  rebind: true,
2586
2542
  };
@@ -2600,7 +2556,6 @@ describe('outbound SMS verification', () => {
2600
2556
  type: 'guardian_verification',
2601
2557
  action: 'start_outbound',
2602
2558
  channel: 'sms',
2603
- assistantId: 'self',
2604
2559
  destination: '+15551234567',
2605
2560
  }, mockSocket, startCtx);
2606
2561
 
@@ -2610,7 +2565,6 @@ describe('outbound SMS verification', () => {
2610
2565
  type: 'guardian_verification',
2611
2566
  action: 'resend_outbound',
2612
2567
  channel: 'sms',
2613
- assistantId: 'self',
2614
2568
  }, mockSocket, ctx);
2615
2569
 
2616
2570
  const resp = lastResponse();
@@ -2626,7 +2580,6 @@ describe('outbound SMS verification', () => {
2626
2580
  type: 'guardian_verification',
2627
2581
  action: 'start_outbound',
2628
2582
  channel: 'sms',
2629
- assistantId: 'self',
2630
2583
  destination: '+15551234567',
2631
2584
  }, mockSocket, startCtx);
2632
2585
 
@@ -2644,7 +2597,6 @@ describe('outbound SMS verification', () => {
2644
2597
  type: 'guardian_verification',
2645
2598
  action: 'resend_outbound',
2646
2599
  channel: 'sms',
2647
- assistantId: 'self',
2648
2600
  }, mockSocket, ctx);
2649
2601
 
2650
2602
  const resp = lastResponse();
@@ -2661,7 +2613,6 @@ describe('outbound SMS verification', () => {
2661
2613
  type: 'guardian_verification',
2662
2614
  action: 'start_outbound',
2663
2615
  channel: 'sms',
2664
- assistantId: 'self',
2665
2616
  destination: '+15551234567',
2666
2617
  }, mockSocket, startCtx);
2667
2618
 
@@ -2681,7 +2632,6 @@ describe('outbound SMS verification', () => {
2681
2632
  type: 'guardian_verification',
2682
2633
  action: 'resend_outbound',
2683
2634
  channel: 'sms',
2684
- assistantId: 'self',
2685
2635
  }, mockSocket, ctx);
2686
2636
 
2687
2637
  const resp = lastResponse();
@@ -2697,7 +2647,6 @@ describe('outbound SMS verification', () => {
2697
2647
  type: 'guardian_verification',
2698
2648
  action: 'start_outbound',
2699
2649
  channel: 'sms',
2700
- assistantId: 'self',
2701
2650
  destination: '+15551234567',
2702
2651
  }, mockSocket, startCtx);
2703
2652
 
@@ -2711,7 +2660,6 @@ describe('outbound SMS verification', () => {
2711
2660
  type: 'guardian_verification',
2712
2661
  action: 'cancel_outbound',
2713
2662
  channel: 'sms',
2714
- assistantId: 'self',
2715
2663
  }, mockSocket, ctx);
2716
2664
 
2717
2665
  const resp = lastResponse();
@@ -2803,7 +2751,6 @@ describe('outbound SMS verification', () => {
2803
2751
  type: 'guardian_verification',
2804
2752
  action: 'start_outbound',
2805
2753
  channel: 'slack',
2806
- assistantId: 'self',
2807
2754
  destination: '@some_user',
2808
2755
  }, mockSocket, ctx);
2809
2756
 
@@ -2819,7 +2766,6 @@ describe('outbound SMS verification', () => {
2819
2766
  type: 'guardian_verification',
2820
2767
  action: 'start_outbound',
2821
2768
  channel: 'sms',
2822
- assistantId: 'self',
2823
2769
  // no destination
2824
2770
  }, mockSocket, ctx);
2825
2771
 
@@ -2835,7 +2781,6 @@ describe('outbound SMS verification', () => {
2835
2781
  type: 'guardian_verification',
2836
2782
  action: 'start_outbound',
2837
2783
  channel: 'sms',
2838
- assistantId: 'self',
2839
2784
  destination: 'not-a-phone',
2840
2785
  }, mockSocket, ctx);
2841
2786
 
@@ -2851,7 +2796,6 @@ describe('outbound SMS verification', () => {
2851
2796
  type: 'guardian_verification',
2852
2797
  action: 'start_outbound',
2853
2798
  channel: 'sms',
2854
- assistantId: 'self',
2855
2799
  destination: '(555) 123-4567',
2856
2800
  }, mockSocket, ctx);
2857
2801
 
@@ -2892,7 +2836,6 @@ describe('outbound SMS verification', () => {
2892
2836
  type: 'guardian_verification',
2893
2837
  action: 'cancel_outbound',
2894
2838
  channel: 'sms',
2895
- assistantId: 'self',
2896
2839
  }, mockSocket, ctx);
2897
2840
 
2898
2841
  const resp = lastResponse();
@@ -2917,7 +2860,6 @@ describe('outbound Telegram verification', () => {
2917
2860
  type: 'guardian_verification',
2918
2861
  action: 'start_outbound',
2919
2862
  channel: 'telegram',
2920
- assistantId: 'self',
2921
2863
  destination: '@someuser',
2922
2864
  }, mockSocket, ctx);
2923
2865
 
@@ -2947,7 +2889,6 @@ describe('outbound Telegram verification', () => {
2947
2889
  type: 'guardian_verification',
2948
2890
  action: 'start_outbound',
2949
2891
  channel: 'telegram',
2950
- assistantId: 'self',
2951
2892
  destination: 'someuser',
2952
2893
  }, mockSocket, ctx);
2953
2894
 
@@ -2964,7 +2905,6 @@ describe('outbound Telegram verification', () => {
2964
2905
  type: 'guardian_verification',
2965
2906
  action: 'start_outbound',
2966
2907
  channel: 'telegram',
2967
- assistantId: 'self',
2968
2908
  destination: '123456789',
2969
2909
  }, mockSocket, ctx);
2970
2910
 
@@ -3002,7 +2942,6 @@ describe('outbound Telegram verification', () => {
3002
2942
  type: 'guardian_verification',
3003
2943
  action: 'start_outbound',
3004
2944
  channel: 'telegram',
3005
- assistantId: 'self',
3006
2945
  destination: '@someuser',
3007
2946
  }, mockSocket, ctx);
3008
2947
 
@@ -3025,7 +2964,6 @@ describe('outbound Telegram verification', () => {
3025
2964
  type: 'guardian_verification',
3026
2965
  action: 'start_outbound',
3027
2966
  channel: 'telegram',
3028
- assistantId: 'self',
3029
2967
  destination: '@newuser',
3030
2968
  rebind: false,
3031
2969
  }, mockSocket, ctx);
@@ -3196,7 +3134,6 @@ describe('outbound Telegram verification', () => {
3196
3134
  type: 'guardian_verification',
3197
3135
  action: 'start_outbound',
3198
3136
  channel: 'telegram',
3199
- assistantId: 'self',
3200
3137
  destination: '123456789',
3201
3138
  }, mockSocket, startCtx);
3202
3139
 
@@ -3210,7 +3147,6 @@ describe('outbound Telegram verification', () => {
3210
3147
  type: 'guardian_verification',
3211
3148
  action: 'resend_outbound',
3212
3149
  channel: 'telegram',
3213
- assistantId: 'self',
3214
3150
  }, mockSocket, ctx);
3215
3151
 
3216
3152
  const resp = lastResponse();
@@ -3231,7 +3167,6 @@ describe('outbound Telegram verification', () => {
3231
3167
  type: 'guardian_verification',
3232
3168
  action: 'start_outbound',
3233
3169
  channel: 'telegram',
3234
- assistantId: 'self',
3235
3170
  destination: '@someuser',
3236
3171
  }, mockSocket, startCtx);
3237
3172
 
@@ -3240,7 +3175,6 @@ describe('outbound Telegram verification', () => {
3240
3175
  type: 'guardian_verification',
3241
3176
  action: 'resend_outbound',
3242
3177
  channel: 'telegram',
3243
- assistantId: 'self',
3244
3178
  }, mockSocket, ctx);
3245
3179
 
3246
3180
  const resp = lastResponse();
@@ -3256,7 +3190,6 @@ describe('outbound Telegram verification', () => {
3256
3190
  type: 'guardian_verification',
3257
3191
  action: 'start_outbound',
3258
3192
  channel: 'telegram',
3259
- assistantId: 'self',
3260
3193
  destination: '123456789',
3261
3194
  }, mockSocket, startCtx);
3262
3195
 
@@ -3268,7 +3201,6 @@ describe('outbound Telegram verification', () => {
3268
3201
  type: 'guardian_verification',
3269
3202
  action: 'cancel_outbound',
3270
3203
  channel: 'telegram',
3271
- assistantId: 'self',
3272
3204
  }, mockSocket, ctx);
3273
3205
 
3274
3206
  const resp = lastResponse();
@@ -3314,7 +3246,6 @@ describe('outbound Telegram verification', () => {
3314
3246
  type: 'guardian_verification',
3315
3247
  action: 'start_outbound',
3316
3248
  channel: 'telegram',
3317
- assistantId: 'self',
3318
3249
  }, mockSocket, ctx);
3319
3250
 
3320
3251
  const resp = lastResponse();
@@ -3330,7 +3261,6 @@ describe('outbound Telegram verification', () => {
3330
3261
  type: 'guardian_verification',
3331
3262
  action: 'start_outbound',
3332
3263
  channel: 'telegram',
3333
- assistantId: 'self',
3334
3264
  destination: '123456789',
3335
3265
  }, mockSocket, startCtx);
3336
3266
 
@@ -3350,7 +3280,6 @@ describe('outbound Telegram verification', () => {
3350
3280
  type: 'guardian_verification',
3351
3281
  action: 'resend_outbound',
3352
3282
  channel: 'telegram',
3353
- assistantId: 'self',
3354
3283
  }, mockSocket, ctx);
3355
3284
 
3356
3285
  const resp = lastResponse();
@@ -3366,7 +3295,6 @@ describe('outbound Telegram verification', () => {
3366
3295
  type: 'guardian_verification',
3367
3296
  action: 'start_outbound',
3368
3297
  channel: 'telegram',
3369
- assistantId: 'self',
3370
3298
  destination: '123456789',
3371
3299
  }, mockSocket, startCtx);
3372
3300
 
@@ -3376,7 +3304,6 @@ describe('outbound Telegram verification', () => {
3376
3304
  type: 'guardian_verification',
3377
3305
  action: 'resend_outbound',
3378
3306
  channel: 'telegram',
3379
- assistantId: 'self',
3380
3307
  }, mockSocket, ctx);
3381
3308
 
3382
3309
  const resp = lastResponse();
@@ -3401,7 +3328,6 @@ describe('outbound voice verification', () => {
3401
3328
  type: 'guardian_verification',
3402
3329
  action: 'start_outbound',
3403
3330
  channel: 'voice',
3404
- assistantId: 'self',
3405
3331
  destination: '+15551234567',
3406
3332
  }, mockSocket, ctx);
3407
3333
 
@@ -3440,7 +3366,6 @@ describe('outbound voice verification', () => {
3440
3366
  type: 'guardian_verification',
3441
3367
  action: 'start_outbound',
3442
3368
  channel: 'voice',
3443
- assistantId: 'self',
3444
3369
  destination: 'not-a-phone',
3445
3370
  }, mockSocket, ctx);
3446
3371
 
@@ -3456,7 +3381,6 @@ describe('outbound voice verification', () => {
3456
3381
  type: 'guardian_verification',
3457
3382
  action: 'start_outbound',
3458
3383
  channel: 'voice',
3459
- assistantId: 'self',
3460
3384
  destination: '555-123-4567',
3461
3385
  }, mockSocket, ctx);
3462
3386
 
@@ -3496,7 +3420,6 @@ describe('outbound voice verification', () => {
3496
3420
  type: 'guardian_verification',
3497
3421
  action: 'start_outbound',
3498
3422
  channel: 'voice',
3499
- assistantId: 'self',
3500
3423
  destination: '+15559876543',
3501
3424
  rebind: false,
3502
3425
  }, mockSocket, ctx);
@@ -3514,7 +3437,6 @@ describe('outbound voice verification', () => {
3514
3437
  type: 'guardian_verification',
3515
3438
  action: 'start_outbound',
3516
3439
  channel: 'voice',
3517
- assistantId: 'self',
3518
3440
  destination: '+15551234567',
3519
3441
  }, mockSocket, startCtx);
3520
3442
 
@@ -3524,7 +3446,6 @@ describe('outbound voice verification', () => {
3524
3446
  type: 'guardian_verification',
3525
3447
  action: 'resend_outbound',
3526
3448
  channel: 'voice',
3527
- assistantId: 'self',
3528
3449
  }, mockSocket, ctx);
3529
3450
 
3530
3451
  const resp = lastResponse();
@@ -3540,7 +3461,6 @@ describe('outbound voice verification', () => {
3540
3461
  type: 'guardian_verification',
3541
3462
  action: 'start_outbound',
3542
3463
  channel: 'voice',
3543
- assistantId: 'self',
3544
3464
  destination: '+15551234567',
3545
3465
  }, mockSocket, startCtx);
3546
3466
 
@@ -3550,7 +3470,6 @@ describe('outbound voice verification', () => {
3550
3470
  type: 'guardian_verification',
3551
3471
  action: 'cancel_outbound',
3552
3472
  channel: 'voice',
3553
- assistantId: 'self',
3554
3473
  }, mockSocket, ctx);
3555
3474
 
3556
3475
  const resp = lastResponse();
@@ -3588,7 +3507,6 @@ describe('outbound voice verification', () => {
3588
3507
  type: 'guardian_verification',
3589
3508
  action: 'start_outbound',
3590
3509
  channel: 'voice',
3591
- assistantId: 'self',
3592
3510
  destination: '+15551234567',
3593
3511
  }, mockSocket, ctx);
3594
3512
 
@@ -3604,7 +3522,6 @@ describe('outbound voice verification', () => {
3604
3522
  type: 'guardian_verification',
3605
3523
  action: 'start_outbound',
3606
3524
  channel: 'voice',
3607
- assistantId: 'self',
3608
3525
  }, mockSocket, ctx);
3609
3526
 
3610
3527
  const resp = lastResponse();
@@ -3637,7 +3554,6 @@ describe('M1–M4 hardening coverage', () => {
3637
3554
  type: 'guardian_verification',
3638
3555
  action: 'start_outbound',
3639
3556
  channel: 'sms',
3640
- assistantId: 'self',
3641
3557
  destination: '+15551234567',
3642
3558
  }, mockSocket, ctx);
3643
3559
 
@@ -3658,7 +3574,6 @@ describe('M1–M4 hardening coverage', () => {
3658
3574
  type: 'guardian_verification',
3659
3575
  action: 'start_outbound',
3660
3576
  channel: 'sms',
3661
- assistantId: 'self',
3662
3577
  destination: '+15551234567',
3663
3578
  }, mockSocket, startCtx);
3664
3579
 
@@ -3673,7 +3588,6 @@ describe('M1–M4 hardening coverage', () => {
3673
3588
  type: 'guardian_verification',
3674
3589
  action: 'resend_outbound',
3675
3590
  channel: 'sms',
3676
- assistantId: 'self',
3677
3591
  }, mockSocket, ctx);
3678
3592
 
3679
3593
  const resp = lastResponse();
@@ -3692,7 +3606,6 @@ describe('M1–M4 hardening coverage', () => {
3692
3606
  type: 'guardian_verification',
3693
3607
  action: 'start_outbound',
3694
3608
  channel: 'telegram',
3695
- assistantId: 'self',
3696
3609
  destination: '@someuser',
3697
3610
  }, mockSocket, ctx);
3698
3611
 
@@ -302,24 +302,18 @@ describe('ChannelReadinessService', () => {
302
302
  expect(snapshot.reasons).toEqual([]);
303
303
  });
304
304
 
305
- test('remote cache is scoped per assistantId', async () => {
306
- const remoteCalls: Record<string, number> = {};
307
- const probe: ChannelProbe = {
308
- channel: 'sms',
309
- runLocalChecks: () => [{ name: 'local', passed: true, message: 'ok' }],
310
- async runRemoteChecks(context) {
311
- const key = context?.assistantId ?? '__default__';
312
- remoteCalls[key] = (remoteCalls[key] ?? 0) + 1;
313
- return [{ name: 'remote', passed: true, message: `ok-${key}` }];
314
- },
315
- };
305
+ test('remote cache uses fixed internal scope (no per-assistantId scoping)', async () => {
306
+ const probe = makeProbe(
307
+ 'sms',
308
+ [{ name: 'local', passed: true, message: 'ok' }],
309
+ [{ name: 'remote', passed: true, message: 'ok' }],
310
+ );
316
311
  service.registerProbe(probe);
317
312
 
318
- await service.getReadiness('sms', true, 'ast-alpha');
319
- await service.getReadiness('sms', true, 'ast-beta');
320
- await service.getReadiness('sms', true, 'ast-alpha');
313
+ // All calls share the same cache key since there is no assistantId dimension
314
+ await service.getReadiness('sms', true);
315
+ await service.getReadiness('sms', true);
321
316
 
322
- expect(remoteCalls['ast-alpha']).toBe(1);
323
- expect(remoteCalls['ast-beta']).toBe(1);
317
+ expect(probe.remoteCallCount).toBe(1);
324
318
  });
325
319
  });
@@ -63,7 +63,7 @@ mock.module('../config/loader.js', () => ({
63
63
  setNestedValue: () => {},
64
64
  }));
65
65
 
66
- import { _resetLegacyDeprecationWarning,check, classifyRisk, generateAllowlistOptions, generateScopeOptions } from '../permissions/checker.js';
66
+ import { _resetLegacyDeprecationWarning,check, classifyRisk, generateAllowlistOptions, generateScopeOptions, SCOPE_AWARE_TOOLS } from '../permissions/checker.js';
67
67
  import { getDefaultRuleTemplates } from '../permissions/defaults.js';
68
68
  import { addRule, clearCache, findHighestPriorityRule } from '../permissions/trust-store.js';
69
69
  import type { TrustRule } from '../permissions/types.js';
@@ -1341,21 +1341,42 @@ describe('Permission Checker', () => {
1341
1341
  expect(options[2]).toEqual({ label: 'everywhere', scope: 'everywhere' });
1342
1342
  });
1343
1343
 
1344
- test('scope options are always project parent → everywhere regardless of tool', () => {
1344
+ test('scope-aware tools all produce the same directory-based ordering', () => {
1345
1345
  const workingDir = join(homedir(), 'projects', 'myapp');
1346
1346
 
1347
- // Non-host tool
1348
- const nonHostOpts = generateScopeOptions(workingDir, 'bash');
1349
- expect(nonHostOpts[0].scope).toBe(workingDir);
1350
- expect(nonHostOpts[nonHostOpts.length - 1].scope).toBe('everywhere');
1347
+ const bashOpts = generateScopeOptions(workingDir, 'bash');
1348
+ expect(bashOpts[0].scope).toBe(workingDir);
1349
+ expect(bashOpts[bashOpts.length - 1].scope).toBe('everywhere');
1351
1350
 
1352
- // Host tool same order now
1353
- const hostOpts = generateScopeOptions(workingDir, 'host_bash');
1354
- expect(hostOpts[0].scope).toBe(workingDir);
1355
- expect(hostOpts[hostOpts.length - 1].scope).toBe('everywhere');
1351
+ const hostBashOpts = generateScopeOptions(workingDir, 'host_bash');
1352
+ expect(bashOpts.map(o => o.scope)).toEqual(hostBashOpts.map(o => o.scope));
1356
1353
 
1357
- // Same ordering for both
1358
- expect(nonHostOpts.map(o => o.scope)).toEqual(hostOpts.map(o => o.scope));
1354
+ const fileOpts = generateScopeOptions(workingDir, 'file_write');
1355
+ expect(bashOpts.map(o => o.scope)).toEqual(fileOpts.map(o => o.scope));
1356
+ });
1357
+
1358
+ test('returns empty for non-scoped tools', () => {
1359
+ const workingDir = join(homedir(), 'projects', 'myapp');
1360
+ expect(generateScopeOptions(workingDir, 'web_fetch')).toHaveLength(0);
1361
+ expect(generateScopeOptions(workingDir, 'browser_navigate')).toHaveLength(0);
1362
+ expect(generateScopeOptions(workingDir, 'skill_load')).toHaveLength(0);
1363
+ expect(generateScopeOptions(workingDir, 'credential_store')).toHaveLength(0);
1364
+ expect(generateScopeOptions(workingDir, 'computer_use_click')).toHaveLength(0);
1365
+ expect(generateScopeOptions(workingDir, 'my_custom_mcp_tool')).toHaveLength(0);
1366
+ });
1367
+
1368
+ test('returns directory options when toolName is omitted (backward compat)', () => {
1369
+ const options = generateScopeOptions('/home/user/project');
1370
+ expect(options).toHaveLength(3);
1371
+ expect(options[0].scope).toBe('/home/user/project');
1372
+ });
1373
+
1374
+ test('SCOPE_AWARE_TOOLS contains only filesystem and shell tools', () => {
1375
+ expect(SCOPE_AWARE_TOOLS).toEqual(new Set([
1376
+ 'bash', 'host_bash',
1377
+ 'file_read', 'file_write', 'file_edit',
1378
+ 'host_file_read', 'host_file_write', 'host_file_edit',
1379
+ ]));
1359
1380
  });
1360
1381
  });
1361
1382
 
@@ -583,6 +583,10 @@ describe('AssistantConfigSchema', () => {
583
583
  userConsultTimeoutSeconds: 120,
584
584
  ttsPlaybackDelayMs: 3000,
585
585
  accessRequestPollIntervalMs: 500,
586
+ guardianWaitUpdateInitialIntervalMs: 5000,
587
+ guardianWaitUpdateInitialWindowMs: 30000,
588
+ guardianWaitUpdateSteadyMinIntervalMs: 7000,
589
+ guardianWaitUpdateSteadyMaxIntervalMs: 10000,
586
590
  disclosure: {
587
591
  enabled: true,
588
592
  text: 'At the very beginning of the call, introduce yourself as an assistant calling on behalf of the person you represent. Do not say "AI assistant".',