@vellumai/assistant 0.4.35 → 0.4.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/AGENTS.md +1 -1
  2. package/ARCHITECTURE.md +44 -49
  3. package/README.md +32 -20
  4. package/docs/architecture/keychain-broker.md +186 -0
  5. package/docs/architecture/security.md +110 -116
  6. package/docs/runbook-trusted-contacts.md +2 -2
  7. package/docs/skills.md +25 -25
  8. package/package.json +4 -1
  9. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +11 -2
  10. package/src/__tests__/actor-token-service.test.ts +1 -0
  11. package/src/__tests__/amazon-cdp-integration.test.ts +74 -0
  12. package/src/__tests__/assistant-feature-flags-integration.test.ts +38 -9
  13. package/src/__tests__/assistant-id-boundary-guard.test.ts +29 -0
  14. package/src/__tests__/browser-fill-credential.test.ts +1 -1
  15. package/src/__tests__/bundle-scanner.test.ts +1 -1
  16. package/src/__tests__/channel-guardian.test.ts +102 -102
  17. package/src/__tests__/channel-invite-transport.test.ts +155 -256
  18. package/src/__tests__/channel-readiness-routes.test.ts +336 -0
  19. package/src/__tests__/checker.test.ts +6 -6
  20. package/src/__tests__/chrome-cdp.test.ts +350 -0
  21. package/src/__tests__/computer-use-session-lifecycle.test.ts +3 -3
  22. package/src/__tests__/computer-use-session-working-dir.test.ts +86 -52
  23. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +1 -1
  24. package/src/__tests__/config-loader-migration.test.ts +85 -0
  25. package/src/__tests__/conversation-pairing.test.ts +370 -5
  26. package/src/__tests__/credential-broker-browser-fill.test.ts +1 -10
  27. package/src/__tests__/credential-broker-server-use.test.ts +1 -10
  28. package/src/__tests__/credential-security-e2e.test.ts +7 -1
  29. package/src/__tests__/credential-security-invariants.test.ts +14 -20
  30. package/src/__tests__/credential-vault-unit.test.ts +1 -11
  31. package/src/__tests__/credential-vault.test.ts +5 -19
  32. package/src/__tests__/credentials-cli.test.ts +806 -0
  33. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +23 -4
  34. package/src/__tests__/email-invite-adapter.test.ts +78 -0
  35. package/src/__tests__/email-service-config-fallback.test.ts +102 -0
  36. package/src/__tests__/encrypted-store.test.ts +6 -6
  37. package/src/__tests__/ephemeral-permissions.test.ts +3 -3
  38. package/src/__tests__/gateway-only-enforcement.test.ts +5 -1
  39. package/src/__tests__/guardian-actions-endpoint.test.ts +70 -12
  40. package/src/__tests__/guardian-outbound-http.test.ts +53 -47
  41. package/src/__tests__/handle-user-message-secret-resume.test.ts +23 -0
  42. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +32 -23
  43. package/src/__tests__/handlers-telegram-config.test.ts +8 -2
  44. package/src/__tests__/handlers-twitter-config.test.ts +2 -2
  45. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +108 -7
  46. package/src/__tests__/ingress-reconcile.test.ts +6 -0
  47. package/src/__tests__/intent-routing.test.ts +23 -4
  48. package/src/__tests__/invite-routes-http.test.ts +12 -0
  49. package/src/__tests__/ipc-snapshot.test.ts +8 -2
  50. package/src/__tests__/keychain-broker-client.test.ts +543 -0
  51. package/src/__tests__/llm-usage-store.test.ts +344 -0
  52. package/src/__tests__/mcp-client-auth.test.ts +2 -2
  53. package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
  54. package/src/__tests__/migration-transport.test.ts +49 -0
  55. package/src/__tests__/notification-broadcaster.test.ts +205 -5
  56. package/src/__tests__/notification-deep-link.test.ts +365 -1
  57. package/src/__tests__/oauth-connect-handler.test.ts +2 -2
  58. package/src/__tests__/onboarding-starter-tasks.test.ts +17 -4
  59. package/src/__tests__/proxy-approval-callback.test.ts +1 -1
  60. package/src/__tests__/recording-handler.test.ts +1 -1
  61. package/src/__tests__/recording-intent-handler.test.ts +6 -1
  62. package/src/__tests__/recording-state-machine.test.ts +1 -1
  63. package/src/__tests__/relay-server.test.ts +9 -1
  64. package/src/__tests__/ride-shotgun-handler.test.ts +499 -0
  65. package/src/__tests__/runtime-attachment-metadata.test.ts +160 -1
  66. package/src/__tests__/script-proxy-injection-runtime.test.ts +299 -2
  67. package/src/__tests__/script-proxy-profile-template-fallback.test.ts +1 -1
  68. package/src/__tests__/secret-onetime-send.test.ts +8 -2
  69. package/src/__tests__/secure-keys.test.ts +175 -216
  70. package/src/__tests__/session-confirmation-signals.test.ts +1 -1
  71. package/src/__tests__/session-messaging-secret-redirect.test.ts +1 -1
  72. package/src/__tests__/session-queue.test.ts +2 -1
  73. package/src/__tests__/session-tool-setup-app-refresh.test.ts +2 -2
  74. package/src/__tests__/skill-feature-flags-integration.test.ts +29 -4
  75. package/src/__tests__/skill-feature-flags.test.ts +12 -9
  76. package/src/__tests__/skill-load-feature-flag.test.ts +26 -5
  77. package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
  78. package/src/__tests__/skills.test.ts +34 -4
  79. package/src/__tests__/slack-channel-config.test.ts +2 -2
  80. package/src/__tests__/system-prompt.test.ts +26 -4
  81. package/src/__tests__/telegram-bot-username-resolution.test.ts +212 -0
  82. package/src/__tests__/telegram-invite-adapter.test.ts +164 -0
  83. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
  84. package/src/__tests__/tool-permission-simulate-handler.test.ts +8 -2
  85. package/src/__tests__/trusted-contact-approval-notifier.test.ts +9 -1
  86. package/src/__tests__/twitter-auth-handler.test.ts +2 -2
  87. package/src/__tests__/twitter-oauth-client.test.ts +1 -1
  88. package/src/__tests__/usage-routes.test.ts +339 -0
  89. package/src/__tests__/whatsapp-invite-adapter.test.ts +94 -0
  90. package/src/agent/loop.ts +3 -0
  91. package/src/amazon/checkout.ts +0 -1
  92. package/src/approvals/guardian-request-resolvers.ts +9 -1
  93. package/src/bundler/app-bundler.ts +28 -12
  94. package/src/bundler/bundle-scanner.ts +1 -1
  95. package/src/bundler/bundle-signer.ts +3 -3
  96. package/src/bundler/manifest.ts +1 -1
  97. package/src/bundler/signature-verifier.ts +3 -3
  98. package/src/channels/config.ts +1 -1
  99. package/src/cli/AGENTS.md +63 -0
  100. package/src/cli/__tests__/notifications.test.ts +470 -0
  101. package/src/cli/amazon.ts +344 -167
  102. package/src/cli/audit.ts +85 -0
  103. package/src/cli/autonomy.ts +369 -0
  104. package/src/cli/channels.ts +51 -0
  105. package/src/cli/completions.ts +208 -0
  106. package/src/cli/config.ts +220 -0
  107. package/src/cli/contacts.ts +471 -0
  108. package/src/cli/credentials.ts +564 -0
  109. package/src/cli/default-action.ts +14 -0
  110. package/src/cli/dev.ts +131 -0
  111. package/src/cli/doctor.ts +398 -0
  112. package/src/cli/email.ts +491 -0
  113. package/src/cli/influencer.ts +72 -0
  114. package/src/cli/integrations.ts +248 -57
  115. package/src/cli/keys.ts +114 -0
  116. package/src/cli/map.ts +46 -54
  117. package/src/cli/mcp.ts +111 -3
  118. package/src/cli/{config-commands.ts → memory.ts} +133 -242
  119. package/src/cli/notifications.ts +407 -0
  120. package/src/cli/program.ts +65 -0
  121. package/src/cli/reference.ts +48 -0
  122. package/src/cli/sequence.ts +154 -0
  123. package/src/cli/sessions.ts +262 -0
  124. package/src/cli/trust.ts +177 -0
  125. package/src/cli/twitter.ts +323 -106
  126. package/src/config/__tests__/build-cli-reference-section.test.ts +49 -0
  127. package/src/config/bundled-skills/amazon/SKILL.md +2 -2
  128. package/src/config/bundled-skills/app-builder/TOOLS.json +26 -0
  129. package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +13 -0
  130. package/src/config/bundled-skills/contacts/SKILL.md +178 -10
  131. package/src/config/bundled-skills/doordash/doordash-cli.ts +23 -168
  132. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +135 -34
  133. package/src/config/bundled-skills/messaging/tools/shared.ts +4 -1
  134. package/src/config/bundled-skills/twilio-setup/SKILL.md +70 -17
  135. package/src/config/bundled-tool-registry.ts +2 -0
  136. package/src/config/core-schema.ts +7 -0
  137. package/src/config/feature-flag-registry.json +16 -0
  138. package/src/config/loader.ts +26 -0
  139. package/src/config/schema.ts +4 -0
  140. package/src/config/skill-state.ts +0 -13
  141. package/src/config/system-prompt.ts +27 -0
  142. package/src/contacts/contact-store.ts +25 -0
  143. package/src/daemon/computer-use-session.ts +1 -1
  144. package/src/daemon/handlers/apps.ts +1 -0
  145. package/src/daemon/handlers/config-channels.ts +3 -3
  146. package/src/daemon/handlers/config-dispatch.ts +29 -0
  147. package/src/daemon/handlers/config-inbox.ts +4 -3
  148. package/src/daemon/handlers/config.ts +3 -43
  149. package/src/daemon/handlers/contacts.ts +34 -0
  150. package/src/daemon/handlers/index.ts +17 -3
  151. package/src/daemon/handlers/session-user-message.ts +7 -0
  152. package/src/daemon/handlers/sessions.ts +21 -2
  153. package/src/daemon/handlers/shared.ts +17 -0
  154. package/src/daemon/ipc-contract/apps.ts +2 -0
  155. package/src/daemon/ipc-contract/computer-use.ts +9 -0
  156. package/src/daemon/ipc-contract/contacts.ts +3 -3
  157. package/src/daemon/ipc-contract/inbox.ts +2 -0
  158. package/src/daemon/ipc-contract/messages.ts +4 -0
  159. package/src/daemon/ipc-contract/sessions.ts +8 -0
  160. package/src/daemon/ipc-contract-inventory.json +1 -0
  161. package/src/daemon/lifecycle.ts +0 -5
  162. package/src/daemon/ride-shotgun-handler.ts +139 -25
  163. package/src/daemon/session-agent-loop-handlers.ts +100 -0
  164. package/src/daemon/session-agent-loop.ts +72 -0
  165. package/src/daemon/session-tool-setup.ts +7 -0
  166. package/src/daemon/session.ts +23 -1
  167. package/src/daemon/tool-side-effects.ts +39 -1
  168. package/src/email/service.ts +59 -2
  169. package/src/index.ts +2 -60
  170. package/src/mcp/mcp-oauth-provider.ts +90 -8
  171. package/src/media/app-icon-generator.ts +86 -0
  172. package/src/memory/db-init.ts +11 -0
  173. package/src/memory/llm-usage-store.ts +186 -0
  174. package/src/memory/migrations/137-usage-dashboard-indexes.ts +26 -0
  175. package/src/memory/migrations/139-drop-usage-composite-indexes.ts +30 -0
  176. package/src/memory/migrations/index.ts +2 -0
  177. package/src/memory/schema-migration.ts +1 -0
  178. package/src/memory/shared-app-links-store.ts +1 -1
  179. package/src/messaging/registry.ts +27 -0
  180. package/src/notifications/README.md +79 -70
  181. package/src/notifications/broadcaster.ts +2 -1
  182. package/src/notifications/conversation-pairing.ts +147 -13
  183. package/src/notifications/copy-composer.ts +7 -3
  184. package/src/notifications/destination-resolver.ts +14 -1
  185. package/src/notifications/emit-signal.ts +3 -2
  186. package/src/notifications/signal.ts +105 -1
  187. package/src/notifications/types.ts +16 -0
  188. package/src/permissions/checker.ts +29 -3
  189. package/src/permissions/prompter.ts +11 -3
  190. package/src/runtime/access-request-helper.ts +2 -1
  191. package/src/runtime/auth/route-policy.ts +7 -1
  192. package/src/runtime/channel-invite-transport.ts +40 -63
  193. package/src/runtime/channel-invite-transports/email.ts +13 -39
  194. package/src/runtime/channel-invite-transports/slack.ts +5 -34
  195. package/src/runtime/channel-invite-transports/sms.ts +8 -29
  196. package/src/runtime/channel-invite-transports/telegram.ts +69 -28
  197. package/src/runtime/channel-invite-transports/voice.ts +0 -7
  198. package/src/runtime/channel-invite-transports/whatsapp.ts +43 -0
  199. package/src/runtime/channel-readiness-service.ts +202 -45
  200. package/src/runtime/confirmation-request-guardian-bridge.ts +2 -1
  201. package/src/runtime/guardian-outbound-actions.ts +8 -5
  202. package/src/runtime/http-server.ts +2 -0
  203. package/src/runtime/invite-instruction-generator.ts +178 -0
  204. package/src/runtime/invite-service.ts +22 -25
  205. package/src/runtime/migrations/migration-transport.ts +13 -0
  206. package/src/runtime/routes/app-routes.ts +1 -1
  207. package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +8 -7
  208. package/src/runtime/routes/channel-readiness-routes.ts +30 -11
  209. package/src/runtime/routes/contact-routes.ts +54 -26
  210. package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +1 -1
  211. package/src/runtime/routes/inbound-stages/escalation-intercept.ts +2 -1
  212. package/src/runtime/routes/inbound-stages/verification-intercept.ts +2 -1
  213. package/src/runtime/routes/integration-routes.ts +1 -1
  214. package/src/runtime/routes/invite-routes.ts +1 -1
  215. package/src/runtime/routes/secret-routes.ts +31 -7
  216. package/src/runtime/routes/twilio-routes.ts +32 -1
  217. package/src/runtime/routes/usage-routes.ts +114 -0
  218. package/src/runtime/tool-grant-request-helper.ts +2 -1
  219. package/src/security/encrypted-store.ts +9 -5
  220. package/src/security/keychain-broker-client.ts +393 -0
  221. package/src/security/secure-keys.ts +106 -321
  222. package/src/tools/apps/executors.ts +73 -0
  223. package/src/tools/browser/auto-navigate.ts +15 -6
  224. package/src/tools/browser/chrome-cdp.ts +211 -0
  225. package/src/tools/browser/network-recorder.test.ts +83 -0
  226. package/src/tools/browser/network-recorder.ts +8 -7
  227. package/src/tools/browser/x-auto-navigate.ts +12 -6
  228. package/src/tools/credentials/policy-types.ts +24 -0
  229. package/src/tools/credentials/vault.ts +22 -27
  230. package/src/tools/network/script-proxy/session-manager.ts +47 -3
  231. package/src/tools/permission-checker.ts +1 -0
  232. package/src/tools/types.ts +2 -0
  233. package/src/tools/ui-surface/definitions.ts +1 -2
  234. package/src/tools/watch/watch-state.ts +2 -0
  235. package/src/__tests__/key-migration.test.ts +0 -240
  236. package/src/__tests__/keychain.test.ts +0 -286
  237. package/src/cli/core-commands.ts +0 -899
  238. package/src/security/keychain-to-encrypted-migration.ts +0 -66
  239. package/src/security/keychain.ts +0 -490
@@ -1358,7 +1358,7 @@ describe("IPC handler channel-aware guardian status", () => {
1358
1358
  resetTables();
1359
1359
  });
1360
1360
 
1361
- test("status action for telegram returns channel and assistantId fields", () => {
1361
+ test("status action for telegram returns channel and assistantId fields", async () => {
1362
1362
  const { ctx, lastResponse } = createMockCtx();
1363
1363
  const msg: GuardianVerificationRequest = {
1364
1364
  type: "guardian_verification",
@@ -1366,7 +1366,7 @@ describe("IPC handler channel-aware guardian status", () => {
1366
1366
  channel: "telegram",
1367
1367
  };
1368
1368
 
1369
- handleGuardianVerification(msg, mockSocket, ctx);
1369
+ await handleGuardianVerification(msg, mockSocket, ctx);
1370
1370
 
1371
1371
  const resp = lastResponse();
1372
1372
  expect(resp).not.toBeNull();
@@ -1377,7 +1377,7 @@ describe("IPC handler channel-aware guardian status", () => {
1377
1377
  expect(resp!.guardianDeliveryChatId).toBeUndefined();
1378
1378
  });
1379
1379
 
1380
- test("status action for sms returns channel: sms and assistantId: self", () => {
1380
+ test("status action for sms returns channel: sms and assistantId: self", async () => {
1381
1381
  const { ctx, lastResponse } = createMockCtx();
1382
1382
  const msg: GuardianVerificationRequest = {
1383
1383
  type: "guardian_verification",
@@ -1385,7 +1385,7 @@ describe("IPC handler channel-aware guardian status", () => {
1385
1385
  channel: "sms",
1386
1386
  };
1387
1387
 
1388
- handleGuardianVerification(msg, mockSocket, ctx);
1388
+ await handleGuardianVerification(msg, mockSocket, ctx);
1389
1389
 
1390
1390
  const resp = lastResponse();
1391
1391
  expect(resp).not.toBeNull();
@@ -1395,7 +1395,7 @@ describe("IPC handler channel-aware guardian status", () => {
1395
1395
  expect(resp!.bound).toBe(false);
1396
1396
  });
1397
1397
 
1398
- test("status action returns guardianDeliveryChatId when bound", () => {
1398
+ test("status action returns guardianDeliveryChatId when bound", async () => {
1399
1399
  createGuardianBinding({
1400
1400
  channel: "telegram",
1401
1401
  guardianExternalUserId: "user-42",
@@ -1410,7 +1410,7 @@ describe("IPC handler channel-aware guardian status", () => {
1410
1410
  channel: "telegram",
1411
1411
  };
1412
1412
 
1413
- handleGuardianVerification(msg, mockSocket, ctx);
1413
+ await handleGuardianVerification(msg, mockSocket, ctx);
1414
1414
 
1415
1415
  const resp = lastResponse();
1416
1416
  expect(resp).not.toBeNull();
@@ -1422,7 +1422,7 @@ describe("IPC handler channel-aware guardian status", () => {
1422
1422
  expect(resp!.assistantId).toBe("self");
1423
1423
  });
1424
1424
 
1425
- test("status action returns guardian username/displayName from binding metadata", () => {
1425
+ test("status action returns guardian username/displayName from binding metadata", async () => {
1426
1426
  createGuardianBinding({
1427
1427
  channel: "telegram",
1428
1428
  guardianExternalUserId: "user-43",
@@ -1462,7 +1462,7 @@ describe("IPC handler channel-aware guardian status", () => {
1462
1462
  channel: "telegram",
1463
1463
  };
1464
1464
 
1465
- handleGuardianVerification(msg, mockSocket, ctx);
1465
+ await handleGuardianVerification(msg, mockSocket, ctx);
1466
1466
 
1467
1467
  const resp = lastResponse();
1468
1468
  expect(resp).not.toBeNull();
@@ -1470,7 +1470,7 @@ describe("IPC handler channel-aware guardian status", () => {
1470
1470
  expect(resp!.guardianDisplayName).toBe("Guardian Name");
1471
1471
  });
1472
1472
 
1473
- test("status action defaults channel to telegram when omitted (backward compat)", () => {
1473
+ test("status action defaults channel to telegram when omitted (backward compat)", async () => {
1474
1474
  const { ctx, lastResponse } = createMockCtx();
1475
1475
  const msg: GuardianVerificationRequest = {
1476
1476
  type: "guardian_verification",
@@ -1478,7 +1478,7 @@ describe("IPC handler channel-aware guardian status", () => {
1478
1478
  // channel omitted — should default to 'telegram'
1479
1479
  };
1480
1480
 
1481
- handleGuardianVerification(msg, mockSocket, ctx);
1481
+ await handleGuardianVerification(msg, mockSocket, ctx);
1482
1482
 
1483
1483
  const resp = lastResponse();
1484
1484
  expect(resp).not.toBeNull();
@@ -1486,7 +1486,7 @@ describe("IPC handler channel-aware guardian status", () => {
1486
1486
  expect(resp!.assistantId).toBe("self");
1487
1487
  });
1488
1488
 
1489
- test("status action defaults assistantId to self when omitted (backward compat)", () => {
1489
+ test("status action defaults assistantId to self when omitted (backward compat)", async () => {
1490
1490
  const { ctx, lastResponse } = createMockCtx();
1491
1491
  const msg: GuardianVerificationRequest = {
1492
1492
  type: "guardian_verification",
@@ -1495,7 +1495,7 @@ describe("IPC handler channel-aware guardian status", () => {
1495
1495
  // assistantId omitted — should default to 'self'
1496
1496
  };
1497
1497
 
1498
- handleGuardianVerification(msg, mockSocket, ctx);
1498
+ await handleGuardianVerification(msg, mockSocket, ctx);
1499
1499
 
1500
1500
  const resp = lastResponse();
1501
1501
  expect(resp).not.toBeNull();
@@ -1503,7 +1503,7 @@ describe("IPC handler channel-aware guardian status", () => {
1503
1503
  expect(resp!.channel).toBe("sms");
1504
1504
  });
1505
1505
 
1506
- test("status action for unbound sms does not return guardianDeliveryChatId", () => {
1506
+ test("status action for unbound sms does not return guardianDeliveryChatId", async () => {
1507
1507
  const { ctx, lastResponse } = createMockCtx();
1508
1508
  const msg: GuardianVerificationRequest = {
1509
1509
  type: "guardian_verification",
@@ -1511,7 +1511,7 @@ describe("IPC handler channel-aware guardian status", () => {
1511
1511
  channel: "sms",
1512
1512
  };
1513
1513
 
1514
- handleGuardianVerification(msg, mockSocket, ctx);
1514
+ await handleGuardianVerification(msg, mockSocket, ctx);
1515
1515
 
1516
1516
  const resp = lastResponse();
1517
1517
  expect(resp).not.toBeNull();
@@ -1520,7 +1520,7 @@ describe("IPC handler channel-aware guardian status", () => {
1520
1520
  expect(resp!.guardianExternalUserId).toBeUndefined();
1521
1521
  });
1522
1522
 
1523
- test("status action includes hasPendingChallenge when challenge exists", () => {
1523
+ test("status action includes hasPendingChallenge when challenge exists", async () => {
1524
1524
  createVerificationChallenge("voice");
1525
1525
 
1526
1526
  const { ctx, lastResponse } = createMockCtx();
@@ -1530,7 +1530,7 @@ describe("IPC handler channel-aware guardian status", () => {
1530
1530
  channel: "voice",
1531
1531
  };
1532
1532
 
1533
- handleGuardianVerification(msg, mockSocket, ctx);
1533
+ await handleGuardianVerification(msg, mockSocket, ctx);
1534
1534
 
1535
1535
  const resp = lastResponse();
1536
1536
  expect(resp).not.toBeNull();
@@ -1538,7 +1538,7 @@ describe("IPC handler channel-aware guardian status", () => {
1538
1538
  expect(resp!.hasPendingChallenge).toBe(true);
1539
1539
  });
1540
1540
 
1541
- test("status action hasPendingChallenge is false when no challenge exists", () => {
1541
+ test("status action hasPendingChallenge is false when no challenge exists", async () => {
1542
1542
  const { ctx, lastResponse } = createMockCtx();
1543
1543
  const msg: GuardianVerificationRequest = {
1544
1544
  type: "guardian_verification",
@@ -1546,7 +1546,7 @@ describe("IPC handler channel-aware guardian status", () => {
1546
1546
  channel: "voice",
1547
1547
  };
1548
1548
 
1549
- handleGuardianVerification(msg, mockSocket, ctx);
1549
+ await handleGuardianVerification(msg, mockSocket, ctx);
1550
1550
 
1551
1551
  const resp = lastResponse();
1552
1552
  expect(resp).not.toBeNull();
@@ -1982,7 +1982,7 @@ describe("IPC handler voice guardian verification", () => {
1982
1982
  resetTables();
1983
1983
  });
1984
1984
 
1985
- test("create_challenge for voice returns a high-entropy hex secret", () => {
1985
+ test("create_challenge for voice returns a high-entropy hex secret", async () => {
1986
1986
  const { ctx, lastResponse } = createMockCtx();
1987
1987
  const msg: GuardianVerificationRequest = {
1988
1988
  type: "guardian_verification",
@@ -1990,7 +1990,7 @@ describe("IPC handler voice guardian verification", () => {
1990
1990
  channel: "voice",
1991
1991
  };
1992
1992
 
1993
- handleGuardianVerification(msg, mockSocket, ctx);
1993
+ await handleGuardianVerification(msg, mockSocket, ctx);
1994
1994
 
1995
1995
  const resp = lastResponse();
1996
1996
  expect(resp).not.toBeNull();
@@ -2002,7 +2002,7 @@ describe("IPC handler voice guardian verification", () => {
2002
2002
  expect(resp!.channel).toBe("voice");
2003
2003
  });
2004
2004
 
2005
- test("status for voice reflects unbound state", () => {
2005
+ test("status for voice reflects unbound state", async () => {
2006
2006
  const { ctx, lastResponse } = createMockCtx();
2007
2007
  const msg: GuardianVerificationRequest = {
2008
2008
  type: "guardian_verification",
@@ -2010,7 +2010,7 @@ describe("IPC handler voice guardian verification", () => {
2010
2010
  channel: "voice",
2011
2011
  };
2012
2012
 
2013
- handleGuardianVerification(msg, mockSocket, ctx);
2013
+ await handleGuardianVerification(msg, mockSocket, ctx);
2014
2014
 
2015
2015
  const resp = lastResponse();
2016
2016
  expect(resp).not.toBeNull();
@@ -2020,7 +2020,7 @@ describe("IPC handler voice guardian verification", () => {
2020
2020
  expect(resp!.guardianExternalUserId).toBeUndefined();
2021
2021
  });
2022
2022
 
2023
- test("status for voice reflects bound state", () => {
2023
+ test("status for voice reflects bound state", async () => {
2024
2024
  createGuardianBinding({
2025
2025
  channel: "voice",
2026
2026
  guardianExternalUserId: "voice-user-1",
@@ -2035,7 +2035,7 @@ describe("IPC handler voice guardian verification", () => {
2035
2035
  channel: "voice",
2036
2036
  };
2037
2037
 
2038
- handleGuardianVerification(msg, mockSocket, ctx);
2038
+ await handleGuardianVerification(msg, mockSocket, ctx);
2039
2039
 
2040
2040
  const resp = lastResponse();
2041
2041
  expect(resp).not.toBeNull();
@@ -2046,7 +2046,7 @@ describe("IPC handler voice guardian verification", () => {
2046
2046
  expect(resp!.channel).toBe("voice");
2047
2047
  });
2048
2048
 
2049
- test("revoke for voice clears active binding", () => {
2049
+ test("revoke for voice clears active binding", async () => {
2050
2050
  createGuardianBinding({
2051
2051
  channel: "voice",
2052
2052
  guardianExternalUserId: "voice-user-1",
@@ -2061,7 +2061,7 @@ describe("IPC handler voice guardian verification", () => {
2061
2061
  channel: "voice",
2062
2062
  };
2063
2063
 
2064
- handleGuardianVerification(msg, mockSocket, ctx);
2064
+ await handleGuardianVerification(msg, mockSocket, ctx);
2065
2065
 
2066
2066
  const resp = lastResponse();
2067
2067
  expect(resp).not.toBeNull();
@@ -2072,7 +2072,7 @@ describe("IPC handler voice guardian verification", () => {
2072
2072
  expect(getGuardianBinding("self", "voice")).toBeNull();
2073
2073
  });
2074
2074
 
2075
- test("revoke for voice does not affect telegram binding", () => {
2075
+ test("revoke for voice does not affect telegram binding", async () => {
2076
2076
  createGuardianBinding({
2077
2077
  channel: "voice",
2078
2078
  guardianExternalUserId: "voice-user-1",
@@ -2093,7 +2093,7 @@ describe("IPC handler voice guardian verification", () => {
2093
2093
  channel: "voice",
2094
2094
  };
2095
2095
 
2096
- handleGuardianVerification(msg, mockSocket, ctx);
2096
+ await handleGuardianVerification(msg, mockSocket, ctx);
2097
2097
 
2098
2098
  expect(getGuardianBinding("self", "voice")).toBeNull();
2099
2099
  expect(getGuardianBinding("self", "telegram")).not.toBeNull();
@@ -2568,7 +2568,7 @@ describe("outbound SMS verification", () => {
2568
2568
  resetTables();
2569
2569
  });
2570
2570
 
2571
- test("start_outbound creates session with expected E.164 identity and returns code", () => {
2571
+ test("start_outbound creates session with expected E.164 identity and returns code", async () => {
2572
2572
  const { ctx, lastResponse } = createMockCtx();
2573
2573
  const msg: GuardianVerificationRequest = {
2574
2574
  type: "guardian_verification",
@@ -2577,7 +2577,7 @@ describe("outbound SMS verification", () => {
2577
2577
  destination: "+15551234567",
2578
2578
  };
2579
2579
 
2580
- handleGuardianVerification(msg, mockSocket, ctx);
2580
+ await handleGuardianVerification(msg, mockSocket, ctx);
2581
2581
 
2582
2582
  const resp = lastResponse();
2583
2583
  expect(resp).not.toBeNull();
@@ -2596,7 +2596,7 @@ describe("outbound SMS verification", () => {
2596
2596
  expect(session!.destinationAddress).toBe("+15551234567");
2597
2597
  });
2598
2598
 
2599
- test("start_outbound rejects when active binding exists (rebind=false)", () => {
2599
+ test("start_outbound rejects when active binding exists (rebind=false)", async () => {
2600
2600
  // Create an existing guardian binding
2601
2601
  createGuardianBinding({
2602
2602
  channel: "sms",
@@ -2614,7 +2614,7 @@ describe("outbound SMS verification", () => {
2614
2614
  rebind: false,
2615
2615
  };
2616
2616
 
2617
- handleGuardianVerification(msg, mockSocket, ctx);
2617
+ await handleGuardianVerification(msg, mockSocket, ctx);
2618
2618
 
2619
2619
  const resp = lastResponse();
2620
2620
  expect(resp).not.toBeNull();
@@ -2622,7 +2622,7 @@ describe("outbound SMS verification", () => {
2622
2622
  expect(resp!.error).toBe("already_bound");
2623
2623
  });
2624
2624
 
2625
- test("start_outbound allows rebind when rebind=true", () => {
2625
+ test("start_outbound allows rebind when rebind=true", async () => {
2626
2626
  // Create an existing guardian binding
2627
2627
  createGuardianBinding({
2628
2628
  channel: "sms",
@@ -2640,7 +2640,7 @@ describe("outbound SMS verification", () => {
2640
2640
  rebind: true,
2641
2641
  };
2642
2642
 
2643
- handleGuardianVerification(msg, mockSocket, ctx);
2643
+ await handleGuardianVerification(msg, mockSocket, ctx);
2644
2644
 
2645
2645
  const resp = lastResponse();
2646
2646
  expect(resp).not.toBeNull();
@@ -2648,10 +2648,10 @@ describe("outbound SMS verification", () => {
2648
2648
  expect(resp!.verificationSessionId).toBeDefined();
2649
2649
  });
2650
2650
 
2651
- test("resend_outbound before cooldown is rejected", () => {
2651
+ test("resend_outbound before cooldown is rejected", async () => {
2652
2652
  // Start an outbound session first
2653
2653
  const { ctx: startCtx } = createMockCtx();
2654
- handleGuardianVerification(
2654
+ await handleGuardianVerification(
2655
2655
  {
2656
2656
  type: "guardian_verification",
2657
2657
  action: "start_outbound",
@@ -2664,7 +2664,7 @@ describe("outbound SMS verification", () => {
2664
2664
 
2665
2665
  // Immediately try to resend (before cooldown)
2666
2666
  const { ctx, lastResponse } = createMockCtx();
2667
- handleGuardianVerification(
2667
+ await handleGuardianVerification(
2668
2668
  {
2669
2669
  type: "guardian_verification",
2670
2670
  action: "resend_outbound",
@@ -2680,10 +2680,10 @@ describe("outbound SMS verification", () => {
2680
2680
  expect(resp!.error).toBe("rate_limited");
2681
2681
  });
2682
2682
 
2683
- test("resend_outbound after cooldown succeeds and increments sendCount", () => {
2683
+ test("resend_outbound after cooldown succeeds and increments sendCount", async () => {
2684
2684
  // Start an outbound session
2685
2685
  const { ctx: startCtx, lastResponse: startResp } = createMockCtx();
2686
- handleGuardianVerification(
2686
+ await handleGuardianVerification(
2687
2687
  {
2688
2688
  type: "guardian_verification",
2689
2689
  action: "start_outbound",
@@ -2709,7 +2709,7 @@ describe("outbound SMS verification", () => {
2709
2709
 
2710
2710
  // Now resend should succeed
2711
2711
  const { ctx, lastResponse } = createMockCtx();
2712
- handleGuardianVerification(
2712
+ await handleGuardianVerification(
2713
2713
  {
2714
2714
  type: "guardian_verification",
2715
2715
  action: "resend_outbound",
@@ -2726,10 +2726,10 @@ describe("outbound SMS verification", () => {
2726
2726
  expect(resp!.nextResendAt).toBeGreaterThan(Date.now());
2727
2727
  });
2728
2728
 
2729
- test("resend_outbound exceeding max sends is rejected", () => {
2729
+ test("resend_outbound exceeding max sends is rejected", async () => {
2730
2730
  // Start an outbound session
2731
2731
  const { ctx: startCtx } = createMockCtx();
2732
- handleGuardianVerification(
2732
+ await handleGuardianVerification(
2733
2733
  {
2734
2734
  type: "guardian_verification",
2735
2735
  action: "start_outbound",
@@ -2752,7 +2752,7 @@ describe("outbound SMS verification", () => {
2752
2752
 
2753
2753
  // Resend should be rejected due to max sends
2754
2754
  const { ctx, lastResponse } = createMockCtx();
2755
- handleGuardianVerification(
2755
+ await handleGuardianVerification(
2756
2756
  {
2757
2757
  type: "guardian_verification",
2758
2758
  action: "resend_outbound",
@@ -2768,10 +2768,10 @@ describe("outbound SMS verification", () => {
2768
2768
  expect(resp!.error).toBe("max_sends_exceeded");
2769
2769
  });
2770
2770
 
2771
- test("cancel_outbound revokes active session", () => {
2771
+ test("cancel_outbound revokes active session", async () => {
2772
2772
  // Start an outbound session
2773
2773
  const { ctx: startCtx } = createMockCtx();
2774
- handleGuardianVerification(
2774
+ await handleGuardianVerification(
2775
2775
  {
2776
2776
  type: "guardian_verification",
2777
2777
  action: "start_outbound",
@@ -2788,7 +2788,7 @@ describe("outbound SMS verification", () => {
2788
2788
 
2789
2789
  // Cancel it
2790
2790
  const { ctx, lastResponse } = createMockCtx();
2791
- handleGuardianVerification(
2791
+ await handleGuardianVerification(
2792
2792
  {
2793
2793
  type: "guardian_verification",
2794
2794
  action: "cancel_outbound",
@@ -2880,9 +2880,9 @@ describe("outbound SMS verification", () => {
2880
2880
  expect(alreadySms).toContain("already verified");
2881
2881
  });
2882
2882
 
2883
- test("start_outbound rejects unsupported channels", () => {
2883
+ test("start_outbound rejects unsupported channels", async () => {
2884
2884
  const { ctx, lastResponse } = createMockCtx();
2885
- handleGuardianVerification(
2885
+ await handleGuardianVerification(
2886
2886
  {
2887
2887
  type: "guardian_verification",
2888
2888
  action: "start_outbound",
@@ -2899,9 +2899,9 @@ describe("outbound SMS verification", () => {
2899
2899
  expect(resp!.error).toBe("unsupported_channel");
2900
2900
  });
2901
2901
 
2902
- test("start_outbound rejects missing destination", () => {
2902
+ test("start_outbound rejects missing destination", async () => {
2903
2903
  const { ctx, lastResponse } = createMockCtx();
2904
- handleGuardianVerification(
2904
+ await handleGuardianVerification(
2905
2905
  {
2906
2906
  type: "guardian_verification",
2907
2907
  action: "start_outbound",
@@ -2918,9 +2918,9 @@ describe("outbound SMS verification", () => {
2918
2918
  expect(resp!.error).toBe("missing_destination");
2919
2919
  });
2920
2920
 
2921
- test("start_outbound rejects unparseable phone number", () => {
2921
+ test("start_outbound rejects unparseable phone number", async () => {
2922
2922
  const { ctx, lastResponse } = createMockCtx();
2923
- handleGuardianVerification(
2923
+ await handleGuardianVerification(
2924
2924
  {
2925
2925
  type: "guardian_verification",
2926
2926
  action: "start_outbound",
@@ -2939,7 +2939,7 @@ describe("outbound SMS verification", () => {
2939
2939
 
2940
2940
  test("start_outbound normalizes formatted phone number for SMS", async () => {
2941
2941
  const { ctx, lastResponse } = createMockCtx();
2942
- handleGuardianVerification(
2942
+ await handleGuardianVerification(
2943
2943
  {
2944
2944
  type: "guardian_verification",
2945
2945
  action: "start_outbound",
@@ -2981,9 +2981,9 @@ describe("outbound SMS verification", () => {
2981
2981
  expect(sms).not.toContain("999999");
2982
2982
  });
2983
2983
 
2984
- test("cancel_outbound returns error when no active session", () => {
2984
+ test("cancel_outbound returns error when no active session", async () => {
2985
2985
  const { ctx, lastResponse } = createMockCtx();
2986
- handleGuardianVerification(
2986
+ await handleGuardianVerification(
2987
2987
  {
2988
2988
  type: "guardian_verification",
2989
2989
  action: "cancel_outbound",
@@ -3009,9 +3009,9 @@ describe("outbound Telegram verification", () => {
3009
3009
  resetTables();
3010
3010
  });
3011
3011
 
3012
- test("start_outbound for telegram with handle returns deep link URL, no outbound message", () => {
3012
+ test("start_outbound for telegram with handle returns deep link URL, no outbound message", async () => {
3013
3013
  const { ctx, lastResponse } = createMockCtx();
3014
- handleGuardianVerification(
3014
+ await handleGuardianVerification(
3015
3015
  {
3016
3016
  type: "guardian_verification",
3017
3017
  action: "start_outbound",
@@ -3044,9 +3044,9 @@ describe("outbound Telegram verification", () => {
3044
3044
  expect(session!.bootstrapTokenHash).not.toBeNull();
3045
3045
  });
3046
3046
 
3047
- test("start_outbound for telegram with handle (no @ prefix) returns deep link", () => {
3047
+ test("start_outbound for telegram with handle (no @ prefix) returns deep link", async () => {
3048
3048
  const { ctx, lastResponse } = createMockCtx();
3049
- handleGuardianVerification(
3049
+ await handleGuardianVerification(
3050
3050
  {
3051
3051
  type: "guardian_verification",
3052
3052
  action: "start_outbound",
@@ -3068,7 +3068,7 @@ describe("outbound Telegram verification", () => {
3068
3068
 
3069
3069
  test("start_outbound for telegram with known chat ID sends message, no deep link", async () => {
3070
3070
  const { ctx, lastResponse } = createMockCtx();
3071
- handleGuardianVerification(
3071
+ await handleGuardianVerification(
3072
3072
  {
3073
3073
  type: "guardian_verification",
3074
3074
  action: "start_outbound",
@@ -3105,11 +3105,11 @@ describe("outbound Telegram verification", () => {
3105
3105
  expect(telegramDeliverCalls[0].text).toContain("code you were given");
3106
3106
  });
3107
3107
 
3108
- test("start_outbound for telegram without bot username fails", () => {
3108
+ test("start_outbound for telegram without bot username fails", async () => {
3109
3109
  mockBotUsername = undefined;
3110
3110
 
3111
3111
  const { ctx, lastResponse } = createMockCtx();
3112
- handleGuardianVerification(
3112
+ await handleGuardianVerification(
3113
3113
  {
3114
3114
  type: "guardian_verification",
3115
3115
  action: "start_outbound",
@@ -3126,7 +3126,7 @@ describe("outbound Telegram verification", () => {
3126
3126
  expect(resp!.error).toBe("no_bot_username");
3127
3127
  });
3128
3128
 
3129
- test("start_outbound for telegram rejects when active binding exists (rebind=false)", () => {
3129
+ test("start_outbound for telegram rejects when active binding exists (rebind=false)", async () => {
3130
3130
  createGuardianBinding({
3131
3131
  channel: "telegram",
3132
3132
  guardianExternalUserId: "user-42",
@@ -3135,7 +3135,7 @@ describe("outbound Telegram verification", () => {
3135
3135
  });
3136
3136
 
3137
3137
  const { ctx, lastResponse } = createMockCtx();
3138
- handleGuardianVerification(
3138
+ await handleGuardianVerification(
3139
3139
  {
3140
3140
  type: "guardian_verification",
3141
3141
  action: "start_outbound",
@@ -3299,7 +3299,7 @@ describe("outbound Telegram verification", () => {
3299
3299
  test("resend_outbound for telegram works with known chat ID", async () => {
3300
3300
  // Start an outbound session with a known chat ID
3301
3301
  const { ctx: startCtx } = createMockCtx();
3302
- handleGuardianVerification(
3302
+ await handleGuardianVerification(
3303
3303
  {
3304
3304
  type: "guardian_verification",
3305
3305
  action: "start_outbound",
@@ -3321,7 +3321,7 @@ describe("outbound Telegram verification", () => {
3321
3321
  );
3322
3322
 
3323
3323
  const { ctx, lastResponse } = createMockCtx();
3324
- handleGuardianVerification(
3324
+ await handleGuardianVerification(
3325
3325
  {
3326
3326
  type: "guardian_verification",
3327
3327
  action: "resend_outbound",
@@ -3342,10 +3342,10 @@ describe("outbound Telegram verification", () => {
3342
3342
  expect(telegramDeliverCalls.length).toBeGreaterThanOrEqual(2);
3343
3343
  });
3344
3344
 
3345
- test("resend_outbound for pending_bootstrap session is rejected", () => {
3345
+ test("resend_outbound for pending_bootstrap session is rejected", async () => {
3346
3346
  // Start an outbound session with a handle (pending_bootstrap)
3347
3347
  const { ctx: startCtx } = createMockCtx();
3348
- handleGuardianVerification(
3348
+ await handleGuardianVerification(
3349
3349
  {
3350
3350
  type: "guardian_verification",
3351
3351
  action: "start_outbound",
@@ -3357,7 +3357,7 @@ describe("outbound Telegram verification", () => {
3357
3357
  );
3358
3358
 
3359
3359
  const { ctx, lastResponse } = createMockCtx();
3360
- handleGuardianVerification(
3360
+ await handleGuardianVerification(
3361
3361
  {
3362
3362
  type: "guardian_verification",
3363
3363
  action: "resend_outbound",
@@ -3373,10 +3373,10 @@ describe("outbound Telegram verification", () => {
3373
3373
  expect(resp!.error).toBe("pending_bootstrap");
3374
3374
  });
3375
3375
 
3376
- test("cancel_outbound for telegram revokes session", () => {
3376
+ test("cancel_outbound for telegram revokes session", async () => {
3377
3377
  // Start an outbound session
3378
3378
  const { ctx: startCtx } = createMockCtx();
3379
- handleGuardianVerification(
3379
+ await handleGuardianVerification(
3380
3380
  {
3381
3381
  type: "guardian_verification",
3382
3382
  action: "start_outbound",
@@ -3391,7 +3391,7 @@ describe("outbound Telegram verification", () => {
3391
3391
  expect(session).not.toBeNull();
3392
3392
 
3393
3393
  const { ctx, lastResponse } = createMockCtx();
3394
- handleGuardianVerification(
3394
+ await handleGuardianVerification(
3395
3395
  {
3396
3396
  type: "guardian_verification",
3397
3397
  action: "cancel_outbound",
@@ -3438,9 +3438,9 @@ describe("outbound Telegram verification", () => {
3438
3438
  expect(msg).not.toContain("999999");
3439
3439
  });
3440
3440
 
3441
- test("start_outbound for telegram with missing destination fails", () => {
3441
+ test("start_outbound for telegram with missing destination fails", async () => {
3442
3442
  const { ctx, lastResponse } = createMockCtx();
3443
- handleGuardianVerification(
3443
+ await handleGuardianVerification(
3444
3444
  {
3445
3445
  type: "guardian_verification",
3446
3446
  action: "start_outbound",
@@ -3456,10 +3456,10 @@ describe("outbound Telegram verification", () => {
3456
3456
  expect(resp!.error).toBe("missing_destination");
3457
3457
  });
3458
3458
 
3459
- test("rate limits apply to telegram outbound (per-session send cap)", () => {
3459
+ test("rate limits apply to telegram outbound (per-session send cap)", async () => {
3460
3460
  // Start an outbound session with a known chat ID
3461
3461
  const { ctx: startCtx } = createMockCtx();
3462
- handleGuardianVerification(
3462
+ await handleGuardianVerification(
3463
3463
  {
3464
3464
  type: "guardian_verification",
3465
3465
  action: "start_outbound",
@@ -3482,7 +3482,7 @@ describe("outbound Telegram verification", () => {
3482
3482
 
3483
3483
  // Resend should be rejected due to max sends
3484
3484
  const { ctx, lastResponse } = createMockCtx();
3485
- handleGuardianVerification(
3485
+ await handleGuardianVerification(
3486
3486
  {
3487
3487
  type: "guardian_verification",
3488
3488
  action: "resend_outbound",
@@ -3498,10 +3498,10 @@ describe("outbound Telegram verification", () => {
3498
3498
  expect(resp!.error).toBe("max_sends_exceeded");
3499
3499
  });
3500
3500
 
3501
- test("rate limits apply to telegram outbound (cooldown)", () => {
3501
+ test("rate limits apply to telegram outbound (cooldown)", async () => {
3502
3502
  // Start an outbound session with a known chat ID
3503
3503
  const { ctx: startCtx } = createMockCtx();
3504
- handleGuardianVerification(
3504
+ await handleGuardianVerification(
3505
3505
  {
3506
3506
  type: "guardian_verification",
3507
3507
  action: "start_outbound",
@@ -3514,7 +3514,7 @@ describe("outbound Telegram verification", () => {
3514
3514
 
3515
3515
  // Immediately try to resend (before cooldown)
3516
3516
  const { ctx, lastResponse } = createMockCtx();
3517
- handleGuardianVerification(
3517
+ await handleGuardianVerification(
3518
3518
  {
3519
3519
  type: "guardian_verification",
3520
3520
  action: "resend_outbound",
@@ -3542,7 +3542,7 @@ describe("outbound voice verification", () => {
3542
3542
 
3543
3543
  test("start_outbound for voice creates session with 6-digit code and initiates call", async () => {
3544
3544
  const { ctx, lastResponse } = createMockCtx();
3545
- handleGuardianVerification(
3545
+ await handleGuardianVerification(
3546
3546
  {
3547
3547
  type: "guardian_verification",
3548
3548
  action: "start_outbound",
@@ -3584,9 +3584,9 @@ describe("outbound voice verification", () => {
3584
3584
  );
3585
3585
  });
3586
3586
 
3587
- test("start_outbound for voice rejects unparseable phone number", () => {
3587
+ test("start_outbound for voice rejects unparseable phone number", async () => {
3588
3588
  const { ctx, lastResponse } = createMockCtx();
3589
- handleGuardianVerification(
3589
+ await handleGuardianVerification(
3590
3590
  {
3591
3591
  type: "guardian_verification",
3592
3592
  action: "start_outbound",
@@ -3605,7 +3605,7 @@ describe("outbound voice verification", () => {
3605
3605
 
3606
3606
  test("start_outbound for voice normalizes formatted phone number", async () => {
3607
3607
  const { ctx, lastResponse } = createMockCtx();
3608
- handleGuardianVerification(
3608
+ await handleGuardianVerification(
3609
3609
  {
3610
3610
  type: "guardian_verification",
3611
3611
  action: "start_outbound",
@@ -3639,7 +3639,7 @@ describe("outbound voice verification", () => {
3639
3639
  expect(lastCall.phoneNumber).toBe("+15551234567");
3640
3640
  });
3641
3641
 
3642
- test("start_outbound for voice rejects when binding exists (rebind=false)", () => {
3642
+ test("start_outbound for voice rejects when binding exists (rebind=false)", async () => {
3643
3643
  createGuardianBinding({
3644
3644
  channel: "voice",
3645
3645
  guardianExternalUserId: "+15551234567",
@@ -3648,7 +3648,7 @@ describe("outbound voice verification", () => {
3648
3648
  });
3649
3649
 
3650
3650
  const { ctx, lastResponse } = createMockCtx();
3651
- handleGuardianVerification(
3651
+ await handleGuardianVerification(
3652
3652
  {
3653
3653
  type: "guardian_verification",
3654
3654
  action: "start_outbound",
@@ -3669,7 +3669,7 @@ describe("outbound voice verification", () => {
3669
3669
  test("resend_outbound for voice initiates a new call with cooldown check", async () => {
3670
3670
  // Start an outbound session first
3671
3671
  const { ctx: startCtx } = createMockCtx();
3672
- handleGuardianVerification(
3672
+ await handleGuardianVerification(
3673
3673
  {
3674
3674
  type: "guardian_verification",
3675
3675
  action: "start_outbound",
@@ -3682,7 +3682,7 @@ describe("outbound voice verification", () => {
3682
3682
 
3683
3683
  // Immediately try to resend (before cooldown)
3684
3684
  const { ctx, lastResponse } = createMockCtx();
3685
- handleGuardianVerification(
3685
+ await handleGuardianVerification(
3686
3686
  {
3687
3687
  type: "guardian_verification",
3688
3688
  action: "resend_outbound",
@@ -3698,10 +3698,10 @@ describe("outbound voice verification", () => {
3698
3698
  expect(resp!.error).toBe("rate_limited");
3699
3699
  });
3700
3700
 
3701
- test("cancel_outbound for voice cancels session", () => {
3701
+ test("cancel_outbound for voice cancels session", async () => {
3702
3702
  // Start an outbound session first
3703
3703
  const { ctx: startCtx } = createMockCtx();
3704
- handleGuardianVerification(
3704
+ await handleGuardianVerification(
3705
3705
  {
3706
3706
  type: "guardian_verification",
3707
3707
  action: "start_outbound",
@@ -3714,7 +3714,7 @@ describe("outbound voice verification", () => {
3714
3714
 
3715
3715
  // Cancel the session
3716
3716
  const { ctx, lastResponse } = createMockCtx();
3717
- handleGuardianVerification(
3717
+ await handleGuardianVerification(
3718
3718
  {
3719
3719
  type: "guardian_verification",
3720
3720
  action: "cancel_outbound",
@@ -3733,7 +3733,7 @@ describe("outbound voice verification", () => {
3733
3733
  expect(session).toBeNull();
3734
3734
  });
3735
3735
 
3736
- test("rate limit enforcement: destination rate limit applies to voice", () => {
3736
+ test("rate limit enforcement: destination rate limit applies to voice", async () => {
3737
3737
  // Exhaust the per-destination rate limit by creating many sessions
3738
3738
  const db = getDb();
3739
3739
  const now = Date.now();
@@ -3756,7 +3756,7 @@ describe("outbound voice verification", () => {
3756
3756
  }
3757
3757
 
3758
3758
  const { ctx, lastResponse } = createMockCtx();
3759
- handleGuardianVerification(
3759
+ await handleGuardianVerification(
3760
3760
  {
3761
3761
  type: "guardian_verification",
3762
3762
  action: "start_outbound",
@@ -3773,9 +3773,9 @@ describe("outbound voice verification", () => {
3773
3773
  expect(resp!.error).toBe("rate_limited");
3774
3774
  });
3775
3775
 
3776
- test("start_outbound for voice requires destination", () => {
3776
+ test("start_outbound for voice requires destination", async () => {
3777
3777
  const { ctx, lastResponse } = createMockCtx();
3778
- handleGuardianVerification(
3778
+ await handleGuardianVerification(
3779
3779
  {
3780
3780
  type: "guardian_verification",
3781
3781
  action: "start_outbound",
@@ -3809,9 +3809,9 @@ describe("M1–M4 hardening coverage", () => {
3809
3809
 
3810
3810
  // ── M2: start_outbound for SMS returns secret in response ──
3811
3811
 
3812
- test("start_outbound for SMS response includes secret", () => {
3812
+ test("start_outbound for SMS response includes secret", async () => {
3813
3813
  const { ctx, lastResponse } = createMockCtx();
3814
- handleGuardianVerification(
3814
+ await handleGuardianVerification(
3815
3815
  {
3816
3816
  type: "guardian_verification",
3817
3817
  action: "start_outbound",
@@ -3832,10 +3832,10 @@ describe("M1–M4 hardening coverage", () => {
3832
3832
 
3833
3833
  // ── M2: resend_outbound for SMS returns secret in response ──
3834
3834
 
3835
- test("resend_outbound for SMS response includes secret", () => {
3835
+ test("resend_outbound for SMS response includes secret", async () => {
3836
3836
  // Start a session first
3837
3837
  const { ctx: startCtx } = createMockCtx();
3838
- handleGuardianVerification(
3838
+ await handleGuardianVerification(
3839
3839
  {
3840
3840
  type: "guardian_verification",
3841
3841
  action: "start_outbound",
@@ -3858,7 +3858,7 @@ describe("M1–M4 hardening coverage", () => {
3858
3858
 
3859
3859
  // Resend
3860
3860
  const { ctx, lastResponse } = createMockCtx();
3861
- handleGuardianVerification(
3861
+ await handleGuardianVerification(
3862
3862
  {
3863
3863
  type: "guardian_verification",
3864
3864
  action: "resend_outbound",
@@ -3878,9 +3878,9 @@ describe("M1–M4 hardening coverage", () => {
3878
3878
 
3879
3879
  // ── M2: start_outbound for Telegram bootstrap does NOT return secret ──
3880
3880
 
3881
- test("start_outbound for Telegram bootstrap (handle) does NOT return secret", () => {
3881
+ test("start_outbound for Telegram bootstrap (handle) does NOT return secret", async () => {
3882
3882
  const { ctx, lastResponse } = createMockCtx();
3883
- handleGuardianVerification(
3883
+ await handleGuardianVerification(
3884
3884
  {
3885
3885
  type: "guardian_verification",
3886
3886
  action: "start_outbound",