@vellumai/assistant 0.4.17 → 0.4.18

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 (515) hide show
  1. package/eslint.config.mjs +2 -2
  2. package/package.json +1 -1
  3. package/src/__tests__/access-request-decision.test.ts +128 -120
  4. package/src/__tests__/account-registry.test.ts +121 -110
  5. package/src/__tests__/active-skill-tools.test.ts +200 -172
  6. package/src/__tests__/actor-token-service.test.ts +341 -274
  7. package/src/__tests__/agent-loop-thinking.test.ts +28 -19
  8. package/src/__tests__/agent-loop.test.ts +798 -378
  9. package/src/__tests__/anthropic-provider.test.ts +405 -247
  10. package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
  11. package/src/__tests__/app-bundler.test.ts +112 -79
  12. package/src/__tests__/app-executors.test.ts +205 -178
  13. package/src/__tests__/app-git-history.test.ts +90 -73
  14. package/src/__tests__/app-git-service.test.ts +67 -53
  15. package/src/__tests__/app-open-proxy.test.ts +29 -25
  16. package/src/__tests__/approval-conversation-turn.test.ts +100 -81
  17. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
  18. package/src/__tests__/approval-message-composer.test.ts +119 -119
  19. package/src/__tests__/approval-primitive.test.ts +264 -233
  20. package/src/__tests__/approval-routes-http.test.ts +4 -3
  21. package/src/__tests__/asset-materialize-tool.test.ts +250 -178
  22. package/src/__tests__/asset-search-tool.test.ts +251 -191
  23. package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
  24. package/src/__tests__/assistant-attachments.test.ts +254 -186
  25. package/src/__tests__/assistant-event-hub.test.ts +105 -63
  26. package/src/__tests__/assistant-event.test.ts +66 -58
  27. package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
  28. package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
  29. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
  31. package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
  32. package/src/__tests__/attachments-store.test.ts +240 -183
  33. package/src/__tests__/attachments.test.ts +70 -62
  34. package/src/__tests__/audit-log-rotation.test.ts +50 -35
  35. package/src/__tests__/browser-fill-credential.test.ts +169 -101
  36. package/src/__tests__/browser-manager.test.ts +97 -75
  37. package/src/__tests__/browser-runtime-check.test.ts +16 -15
  38. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
  39. package/src/__tests__/browser-skill-endstate.test.ts +97 -72
  40. package/src/__tests__/bundle-scanner.test.ts +47 -22
  41. package/src/__tests__/bundled-asset.test.ts +74 -47
  42. package/src/__tests__/call-constants.test.ts +19 -19
  43. package/src/__tests__/call-controller.test.ts +0 -1
  44. package/src/__tests__/call-conversation-messages.test.ts +90 -65
  45. package/src/__tests__/call-domain.test.ts +149 -121
  46. package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
  47. package/src/__tests__/call-pointer-messages.test.ts +213 -154
  48. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
  49. package/src/__tests__/call-recovery.test.ts +232 -212
  50. package/src/__tests__/call-routes-http.test.ts +0 -1
  51. package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
  52. package/src/__tests__/call-state-machine.test.ts +62 -51
  53. package/src/__tests__/call-state.test.ts +89 -75
  54. package/src/__tests__/call-store.test.ts +387 -316
  55. package/src/__tests__/callback-handoff-copy.test.ts +84 -82
  56. package/src/__tests__/canonical-guardian-store.test.ts +331 -280
  57. package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
  58. package/src/__tests__/channel-approval.test.ts +139 -137
  59. package/src/__tests__/channel-approvals.test.ts +0 -1
  60. package/src/__tests__/channel-delivery-store.test.ts +232 -194
  61. package/src/__tests__/channel-guardian.test.ts +5 -3
  62. package/src/__tests__/channel-invite-transport.test.ts +107 -92
  63. package/src/__tests__/channel-policy.test.ts +42 -38
  64. package/src/__tests__/channel-readiness-service.test.ts +119 -102
  65. package/src/__tests__/channel-reply-delivery.test.ts +147 -118
  66. package/src/__tests__/channel-retry-sweep.test.ts +153 -110
  67. package/src/__tests__/checker.test.ts +3309 -1850
  68. package/src/__tests__/clarification-resolver.test.ts +91 -79
  69. package/src/__tests__/classifier.test.ts +64 -54
  70. package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
  71. package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
  72. package/src/__tests__/clawhub.test.ts +92 -82
  73. package/src/__tests__/cli.test.ts +30 -30
  74. package/src/__tests__/clipboard.test.ts +53 -46
  75. package/src/__tests__/commit-guarantee.test.ts +59 -52
  76. package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
  77. package/src/__tests__/compaction.benchmark.test.ts +33 -31
  78. package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
  79. package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
  80. package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
  81. package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
  82. package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
  83. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
  84. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
  85. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
  86. package/src/__tests__/computer-use-tools.test.ts +121 -98
  87. package/src/__tests__/config-schema.test.ts +443 -347
  88. package/src/__tests__/config-watcher.test.ts +96 -81
  89. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
  90. package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
  91. package/src/__tests__/conflict-policy.test.ts +151 -80
  92. package/src/__tests__/conflict-store.test.ts +203 -157
  93. package/src/__tests__/connection-policy.test.ts +89 -59
  94. package/src/__tests__/contacts-tools.test.ts +247 -178
  95. package/src/__tests__/context-memory-e2e.test.ts +306 -214
  96. package/src/__tests__/context-token-estimator.test.ts +114 -74
  97. package/src/__tests__/context-window-manager.test.ts +269 -167
  98. package/src/__tests__/contradiction-checker.test.ts +161 -135
  99. package/src/__tests__/conversation-attention-store.test.ts +350 -290
  100. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  101. package/src/__tests__/conversation-pairing.test.ts +220 -113
  102. package/src/__tests__/conversation-store.test.ts +390 -235
  103. package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
  104. package/src/__tests__/credential-broker-server-use.test.ts +283 -243
  105. package/src/__tests__/credential-broker.test.ts +128 -74
  106. package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
  107. package/src/__tests__/credential-metadata-store.test.ts +360 -311
  108. package/src/__tests__/credential-policy-validate.test.ts +81 -65
  109. package/src/__tests__/credential-resolve.test.ts +212 -145
  110. package/src/__tests__/credential-security-e2e.test.ts +144 -103
  111. package/src/__tests__/credential-security-invariants.test.ts +253 -208
  112. package/src/__tests__/credential-selection.test.ts +254 -146
  113. package/src/__tests__/credential-vault-unit.test.ts +531 -341
  114. package/src/__tests__/credential-vault.test.ts +761 -484
  115. package/src/__tests__/daemon-assistant-events.test.ts +91 -66
  116. package/src/__tests__/daemon-lifecycle.test.ts +258 -190
  117. package/src/__tests__/daemon-server-session-init.test.ts +0 -1
  118. package/src/__tests__/date-context.test.ts +314 -249
  119. package/src/__tests__/db-migration-rollback.test.ts +259 -130
  120. package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
  121. package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
  122. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
  123. package/src/__tests__/dictation-mode-detection.test.ts +77 -55
  124. package/src/__tests__/dictation-profile-store.test.ts +70 -56
  125. package/src/__tests__/dictation-text-processing.test.ts +53 -35
  126. package/src/__tests__/diff.test.ts +102 -98
  127. package/src/__tests__/domain-normalize.test.ts +54 -54
  128. package/src/__tests__/domain-policy.test.ts +71 -55
  129. package/src/__tests__/dynamic-page-surface.test.ts +31 -33
  130. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
  131. package/src/__tests__/edit-engine.test.ts +56 -56
  132. package/src/__tests__/elevenlabs-client.test.ts +117 -91
  133. package/src/__tests__/elevenlabs-config.test.ts +32 -31
  134. package/src/__tests__/email-classifier.test.ts +15 -12
  135. package/src/__tests__/email-cli.test.ts +121 -108
  136. package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
  137. package/src/__tests__/encrypted-store.test.ts +180 -154
  138. package/src/__tests__/entity-extractor.test.ts +108 -87
  139. package/src/__tests__/entity-search.test.ts +664 -258
  140. package/src/__tests__/ephemeral-permissions.test.ts +224 -188
  141. package/src/__tests__/event-bus.test.ts +81 -77
  142. package/src/__tests__/extract-email.test.ts +29 -20
  143. package/src/__tests__/file-edit-tool.test.ts +62 -44
  144. package/src/__tests__/file-ops-service.test.ts +131 -114
  145. package/src/__tests__/file-read-tool.test.ts +48 -31
  146. package/src/__tests__/file-write-tool.test.ts +43 -37
  147. package/src/__tests__/filesystem-tools.test.ts +238 -209
  148. package/src/__tests__/followup-tools.test.ts +237 -162
  149. package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
  150. package/src/__tests__/frontmatter.test.ts +96 -81
  151. package/src/__tests__/fuzzy-match-property.test.ts +75 -81
  152. package/src/__tests__/fuzzy-match.test.ts +71 -65
  153. package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
  154. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  155. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  156. package/src/__tests__/gemini-image-service.test.ts +113 -100
  157. package/src/__tests__/gemini-provider.test.ts +297 -220
  158. package/src/__tests__/get-weather.test.ts +188 -114
  159. package/src/__tests__/gmail-integration.test.ts +0 -1
  160. package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
  161. package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
  162. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
  163. package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
  164. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
  165. package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
  166. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
  167. package/src/__tests__/guardian-action-store.test.ts +158 -109
  168. package/src/__tests__/guardian-action-sweep.test.ts +114 -100
  169. package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
  170. package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
  171. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
  172. package/src/__tests__/guardian-dispatch.test.ts +316 -256
  173. package/src/__tests__/guardian-grant-minting.test.ts +247 -178
  174. package/src/__tests__/guardian-outbound-http.test.ts +5 -3
  175. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
  176. package/src/__tests__/guardian-question-copy.test.ts +17 -17
  177. package/src/__tests__/guardian-question-mode.test.ts +134 -100
  178. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  179. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  180. package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
  181. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  182. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
  183. package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
  184. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
  185. package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
  186. package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
  187. package/src/__tests__/handlers-slack-config.test.ts +63 -54
  188. package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
  189. package/src/__tests__/handlers-telegram-config.test.ts +662 -329
  190. package/src/__tests__/handlers-twitter-config.test.ts +525 -298
  191. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
  192. package/src/__tests__/headless-browser-interactions.test.ts +444 -280
  193. package/src/__tests__/headless-browser-navigate.test.ts +116 -79
  194. package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
  195. package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
  196. package/src/__tests__/heartbeat-service.test.ts +76 -58
  197. package/src/__tests__/history-repair-observability.test.ts +14 -14
  198. package/src/__tests__/history-repair.test.ts +171 -167
  199. package/src/__tests__/home-base-bootstrap.test.ts +30 -27
  200. package/src/__tests__/hooks-blocking.test.ts +86 -37
  201. package/src/__tests__/hooks-cli.test.ts +104 -68
  202. package/src/__tests__/hooks-config.test.ts +81 -43
  203. package/src/__tests__/hooks-discovery.test.ts +106 -96
  204. package/src/__tests__/hooks-integration.test.ts +78 -72
  205. package/src/__tests__/hooks-manager.test.ts +99 -61
  206. package/src/__tests__/hooks-runner.test.ts +94 -71
  207. package/src/__tests__/hooks-settings.test.ts +69 -64
  208. package/src/__tests__/hooks-templates.test.ts +85 -54
  209. package/src/__tests__/hooks-ts-runner.test.ts +82 -45
  210. package/src/__tests__/hooks-watch.test.ts +32 -22
  211. package/src/__tests__/host-file-edit-tool.test.ts +190 -148
  212. package/src/__tests__/host-file-read-tool.test.ts +86 -63
  213. package/src/__tests__/host-file-write-tool.test.ts +98 -64
  214. package/src/__tests__/host-shell-tool.test.ts +342 -233
  215. package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
  216. package/src/__tests__/ingress-member-store.test.ts +163 -159
  217. package/src/__tests__/ingress-reconcile.test.ts +0 -1
  218. package/src/__tests__/ingress-routes-http.test.ts +441 -356
  219. package/src/__tests__/ingress-url-consistency.test.ts +125 -64
  220. package/src/__tests__/integration-status.test.ts +93 -73
  221. package/src/__tests__/intent-routing.test.ts +148 -118
  222. package/src/__tests__/invite-redemption-service.test.ts +163 -121
  223. package/src/__tests__/ipc-blob-store.test.ts +104 -91
  224. package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
  225. package/src/__tests__/ipc-contract.test.ts +24 -23
  226. package/src/__tests__/ipc-protocol.test.ts +52 -46
  227. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
  228. package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
  229. package/src/__tests__/ipc-validate.test.ts +240 -179
  230. package/src/__tests__/key-migration.test.ts +123 -90
  231. package/src/__tests__/keychain.test.ts +150 -123
  232. package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
  233. package/src/__tests__/llm-usage-store.test.ts +112 -87
  234. package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
  235. package/src/__tests__/managed-store.test.ts +411 -360
  236. package/src/__tests__/mcp-cli.test.ts +189 -123
  237. package/src/__tests__/mcp-health-check.test.ts +26 -21
  238. package/src/__tests__/media-generate-image.test.ts +122 -99
  239. package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
  240. package/src/__tests__/media-visibility-policy.test.ts +86 -38
  241. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
  242. package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
  243. package/src/__tests__/memory-query-builder.test.ts +32 -33
  244. package/src/__tests__/memory-recall-quality.test.ts +761 -407
  245. package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
  246. package/src/__tests__/memory-regressions.test.ts +3725 -2642
  247. package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
  248. package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
  249. package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
  250. package/src/__tests__/messaging-send-tool.test.ts +36 -29
  251. package/src/__tests__/migration-cli-flows.test.ts +69 -53
  252. package/src/__tests__/migration-ordering.test.ts +103 -86
  253. package/src/__tests__/mime-builder.test.ts +55 -32
  254. package/src/__tests__/mock-signup-server.test.ts +384 -246
  255. package/src/__tests__/model-intents.test.ts +61 -37
  256. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
  257. package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
  258. package/src/__tests__/non-member-access-request.test.ts +3 -2
  259. package/src/__tests__/notification-broadcaster.test.ts +99 -81
  260. package/src/__tests__/notification-decision-fallback.test.ts +223 -178
  261. package/src/__tests__/notification-decision-strategy.test.ts +375 -337
  262. package/src/__tests__/notification-deep-link.test.ts +67 -61
  263. package/src/__tests__/notification-guardian-path.test.ts +248 -206
  264. package/src/__tests__/notification-routing-intent.test.ts +166 -93
  265. package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
  266. package/src/__tests__/notification-thread-candidates.test.ts +64 -61
  267. package/src/__tests__/oauth-callback-registry.test.ts +40 -30
  268. package/src/__tests__/oauth-connect-handler.test.ts +109 -89
  269. package/src/__tests__/oauth-scope-policy.test.ts +63 -55
  270. package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
  271. package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
  272. package/src/__tests__/onboarding-template-contract.test.ts +93 -94
  273. package/src/__tests__/openai-provider.test.ts +366 -274
  274. package/src/__tests__/pairing-concurrent.test.ts +18 -12
  275. package/src/__tests__/pairing-routes.test.ts +45 -41
  276. package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
  277. package/src/__tests__/parser.test.ts +316 -226
  278. package/src/__tests__/path-classifier.test.ts +24 -25
  279. package/src/__tests__/path-policy.test.ts +187 -147
  280. package/src/__tests__/phone.test.ts +36 -36
  281. package/src/__tests__/platform-move-helper.test.ts +48 -40
  282. package/src/__tests__/platform-socket-path.test.ts +23 -24
  283. package/src/__tests__/platform-workspace-migration.test.ts +464 -414
  284. package/src/__tests__/platform.test.ts +61 -53
  285. package/src/__tests__/playbook-execution.test.ts +397 -265
  286. package/src/__tests__/playbook-tools.test.ts +267 -196
  287. package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
  288. package/src/__tests__/pricing.test.ts +316 -136
  289. package/src/__tests__/profile-compiler.test.ts +206 -188
  290. package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
  291. package/src/__tests__/provider-error-scenarios.test.ts +212 -158
  292. package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
  293. package/src/__tests__/provider-registry-ollama.test.ts +13 -9
  294. package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
  295. package/src/__tests__/proxy-approval-callback.test.ts +180 -119
  296. package/src/__tests__/public-ingress-urls.test.ts +112 -94
  297. package/src/__tests__/qdrant-manager.test.ts +147 -98
  298. package/src/__tests__/ratelimit.test.ts +152 -82
  299. package/src/__tests__/recording-handler.test.ts +273 -151
  300. package/src/__tests__/recording-intent-fallback.test.ts +94 -75
  301. package/src/__tests__/recording-intent-handler.test.ts +0 -1
  302. package/src/__tests__/recording-intent.test.ts +578 -379
  303. package/src/__tests__/recording-state-machine.test.ts +530 -316
  304. package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
  305. package/src/__tests__/recurrence-engine.test.ts +81 -41
  306. package/src/__tests__/recurrence-types.test.ts +63 -44
  307. package/src/__tests__/relay-server.test.ts +2131 -1602
  308. package/src/__tests__/reminder-store.test.ts +158 -80
  309. package/src/__tests__/reminder.test.ts +113 -109
  310. package/src/__tests__/remote-skill-policy.test.ts +96 -72
  311. package/src/__tests__/request-file-tool.test.ts +74 -67
  312. package/src/__tests__/response-tier.test.ts +131 -74
  313. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  314. package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
  315. package/src/__tests__/runtime-events-sse.test.ts +0 -1
  316. package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
  317. package/src/__tests__/sandbox-host-parity.test.ts +377 -301
  318. package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
  319. package/src/__tests__/schedule-store.test.ts +268 -205
  320. package/src/__tests__/schedule-tools.test.ts +702 -524
  321. package/src/__tests__/scheduler-recurrence.test.ts +240 -130
  322. package/src/__tests__/scoped-approval-grants.test.ts +258 -168
  323. package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
  324. package/src/__tests__/script-proxy-certs.test.ts +38 -35
  325. package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
  326. package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
  327. package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
  328. package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
  329. package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
  330. package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
  331. package/src/__tests__/script-proxy-policy.test.ts +308 -153
  332. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
  333. package/src/__tests__/script-proxy-router.test.ts +111 -77
  334. package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
  335. package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
  336. package/src/__tests__/secret-allowlist.test.ts +105 -90
  337. package/src/__tests__/secret-ingress-handler.test.ts +41 -30
  338. package/src/__tests__/secret-onetime-send.test.ts +67 -50
  339. package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
  340. package/src/__tests__/secret-response-routing.test.ts +50 -41
  341. package/src/__tests__/secret-scanner-executor.test.ts +152 -111
  342. package/src/__tests__/secret-scanner.test.ts +495 -413
  343. package/src/__tests__/secure-keys.test.ts +132 -121
  344. package/src/__tests__/send-endpoint-busy.test.ts +0 -1
  345. package/src/__tests__/send-notification-tool.test.ts +43 -42
  346. package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
  347. package/src/__tests__/sequence-store.test.ts +335 -167
  348. package/src/__tests__/server-history-render.test.ts +341 -202
  349. package/src/__tests__/session-abort-tool-results.test.ts +133 -70
  350. package/src/__tests__/session-confirmation-signals.test.ts +252 -160
  351. package/src/__tests__/session-conflict-gate.test.ts +775 -585
  352. package/src/__tests__/session-error.test.ts +222 -191
  353. package/src/__tests__/session-evictor.test.ts +79 -62
  354. package/src/__tests__/session-init.benchmark.test.ts +170 -108
  355. package/src/__tests__/session-load-history-repair.test.ts +273 -139
  356. package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
  357. package/src/__tests__/session-pre-run-repair.test.ts +106 -59
  358. package/src/__tests__/session-profile-injection.test.ts +198 -130
  359. package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
  360. package/src/__tests__/session-queue.test.ts +624 -321
  361. package/src/__tests__/session-runtime-assembly.test.ts +425 -329
  362. package/src/__tests__/session-runtime-workspace.test.ts +69 -61
  363. package/src/__tests__/session-skill-tools.test.ts +973 -678
  364. package/src/__tests__/session-slash-known.test.ts +185 -133
  365. package/src/__tests__/session-slash-queue.test.ts +147 -81
  366. package/src/__tests__/session-slash-unknown.test.ts +135 -90
  367. package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
  368. package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
  369. package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
  370. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
  371. package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
  372. package/src/__tests__/session-undo.test.ts +43 -30
  373. package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
  374. package/src/__tests__/session-workspace-injection.test.ts +245 -117
  375. package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
  376. package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
  377. package/src/__tests__/shell-credential-ref.test.ts +126 -90
  378. package/src/__tests__/shell-identity.test.ts +134 -111
  379. package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
  380. package/src/__tests__/shell-parser-property.test.ts +435 -288
  381. package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
  382. package/src/__tests__/size-guard.test.ts +42 -44
  383. package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
  384. package/src/__tests__/skill-feature-flags.test.ts +75 -47
  385. package/src/__tests__/skill-include-graph.test.ts +143 -148
  386. package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
  387. package/src/__tests__/skill-load-tool.test.ts +371 -199
  388. package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
  389. package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
  390. package/src/__tests__/skill-script-runner-host.test.ts +460 -250
  391. package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
  392. package/src/__tests__/skill-script-runner.test.ts +115 -74
  393. package/src/__tests__/skill-tool-factory.test.ts +140 -96
  394. package/src/__tests__/skill-tool-manifest.test.ts +306 -210
  395. package/src/__tests__/skill-version-hash.test.ts +70 -56
  396. package/src/__tests__/skills.test.ts +0 -1
  397. package/src/__tests__/slack-channel-config.test.ts +127 -84
  398. package/src/__tests__/slack-skill.test.ts +60 -47
  399. package/src/__tests__/slash-commands-catalog.test.ts +37 -31
  400. package/src/__tests__/slash-commands-parser.test.ts +71 -64
  401. package/src/__tests__/slash-commands-resolver.test.ts +143 -107
  402. package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
  403. package/src/__tests__/speaker-identification.test.ts +28 -25
  404. package/src/__tests__/starter-bundle.test.ts +27 -23
  405. package/src/__tests__/starter-task-flow.test.ts +67 -52
  406. package/src/__tests__/subagent-manager-notify.test.ts +154 -108
  407. package/src/__tests__/subagent-tools.test.ts +311 -270
  408. package/src/__tests__/subagent-types.test.ts +40 -40
  409. package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
  410. package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
  411. package/src/__tests__/swarm-orchestrator.test.ts +135 -101
  412. package/src/__tests__/swarm-plan-validator.test.ts +125 -73
  413. package/src/__tests__/swarm-recursion.test.ts +58 -46
  414. package/src/__tests__/swarm-router-planner.test.ts +99 -74
  415. package/src/__tests__/swarm-session-integration.test.ts +148 -91
  416. package/src/__tests__/swarm-tool.test.ts +65 -45
  417. package/src/__tests__/swarm-worker-backend.test.ts +59 -45
  418. package/src/__tests__/swarm-worker-runner.test.ts +133 -118
  419. package/src/__tests__/system-prompt.test.ts +290 -256
  420. package/src/__tests__/task-compiler.test.ts +176 -120
  421. package/src/__tests__/task-management-tools.test.ts +561 -456
  422. package/src/__tests__/task-memory-cleanup.test.ts +627 -362
  423. package/src/__tests__/task-runner.test.ts +117 -94
  424. package/src/__tests__/task-scheduler.test.ts +113 -84
  425. package/src/__tests__/task-tools.test.ts +349 -264
  426. package/src/__tests__/terminal-sandbox.test.ts +138 -108
  427. package/src/__tests__/terminal-tools.test.ts +350 -305
  428. package/src/__tests__/thread-seed-composer.test.ts +307 -180
  429. package/src/__tests__/tool-approval-handler.test.ts +238 -137
  430. package/src/__tests__/tool-audit-listener.test.ts +69 -69
  431. package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
  432. package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
  433. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
  434. package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
  435. package/src/__tests__/tool-executor-redaction.test.ts +112 -109
  436. package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
  437. package/src/__tests__/tool-executor.test.ts +1274 -674
  438. package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
  439. package/src/__tests__/tool-metrics-listener.test.ts +97 -85
  440. package/src/__tests__/tool-notification-listener.test.ts +42 -25
  441. package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
  442. package/src/__tests__/tool-policy.test.ts +44 -25
  443. package/src/__tests__/tool-profiling-listener.test.ts +99 -93
  444. package/src/__tests__/tool-result-truncation.test.ts +5 -4
  445. package/src/__tests__/tool-trace-listener.test.ts +131 -111
  446. package/src/__tests__/top-level-renderer.test.ts +62 -58
  447. package/src/__tests__/top-level-scanner.test.ts +68 -64
  448. package/src/__tests__/trace-emitter.test.ts +56 -56
  449. package/src/__tests__/trust-context-guards.test.ts +65 -65
  450. package/src/__tests__/trust-store.test.ts +1239 -806
  451. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  452. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  453. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  454. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  455. package/src/__tests__/trusted-contact-verification.test.ts +251 -231
  456. package/src/__tests__/turn-commit.test.ts +259 -200
  457. package/src/__tests__/twilio-provider.test.ts +140 -126
  458. package/src/__tests__/twilio-rest.test.ts +22 -18
  459. package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
  460. package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
  461. package/src/__tests__/twilio-routes.test.ts +0 -1
  462. package/src/__tests__/twitter-auth-handler.test.ts +184 -139
  463. package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
  464. package/src/__tests__/twitter-cli-routing.test.ts +146 -99
  465. package/src/__tests__/twitter-oauth-client.test.ts +82 -65
  466. package/src/__tests__/update-bulletin-format.test.ts +69 -66
  467. package/src/__tests__/update-bulletin-state.test.ts +66 -60
  468. package/src/__tests__/update-bulletin.test.ts +150 -114
  469. package/src/__tests__/update-template-contract.test.ts +15 -10
  470. package/src/__tests__/url-safety.test.ts +288 -265
  471. package/src/__tests__/user-reference.test.ts +32 -32
  472. package/src/__tests__/view-image-tool.test.ts +118 -96
  473. package/src/__tests__/voice-invite-redemption.test.ts +111 -106
  474. package/src/__tests__/voice-quality.test.ts +117 -102
  475. package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
  476. package/src/__tests__/voice-session-bridge.test.ts +351 -216
  477. package/src/__tests__/weather-skill-regression.test.ts +170 -120
  478. package/src/__tests__/web-fetch.test.ts +664 -526
  479. package/src/__tests__/web-search.test.ts +379 -213
  480. package/src/__tests__/work-item-output.test.ts +90 -53
  481. package/src/__tests__/workspace-git-service.test.ts +437 -356
  482. package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
  483. package/src/__tests__/workspace-lifecycle.test.ts +98 -64
  484. package/src/__tests__/workspace-policy.test.ts +139 -71
  485. package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
  486. package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
  487. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
  488. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
  489. package/src/config/bundled-skills/messaging/SKILL.md +4 -3
  490. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
  491. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
  492. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
  493. package/src/config/bundled-tool-registry.ts +2 -0
  494. package/src/config/env.ts +3 -4
  495. package/src/memory/db-connection.ts +16 -10
  496. package/src/messaging/providers/gmail/adapter.ts +10 -3
  497. package/src/messaging/providers/gmail/client.ts +280 -72
  498. package/src/runtime/auth/__tests__/context.test.ts +75 -65
  499. package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
  500. package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
  501. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
  502. package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
  503. package/src/runtime/auth/__tests__/policy.test.ts +9 -9
  504. package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
  505. package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
  506. package/src/runtime/auth/__tests__/subject.test.ts +54 -54
  507. package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
  508. package/src/runtime/auth/scopes.ts +3 -0
  509. package/src/runtime/auth/token-service.ts +4 -1
  510. package/src/runtime/auth/types.ts +2 -1
  511. package/src/runtime/http-server.ts +2 -1
  512. package/src/security/secure-keys.ts +103 -53
  513. package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
  514. package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
  515. package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
@@ -1,24 +1,23 @@
1
- import { mkdtempSync, rmSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
1
+ import { mkdtempSync, rmSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
4
5
 
5
- import { afterAll, beforeEach, describe, expect, mock, test } from 'bun:test';
6
+ const testDir = mkdtempSync(join(tmpdir(), "canonical-guardian-store-test-"));
6
7
 
7
- const testDir = mkdtempSync(join(tmpdir(), 'canonical-guardian-store-test-'));
8
-
9
- mock.module('../util/platform.js', () => ({
8
+ mock.module("../util/platform.js", () => ({
10
9
  getDataDir: () => testDir,
11
- isMacOS: () => process.platform === 'darwin',
12
- isLinux: () => process.platform === 'linux',
13
- isWindows: () => process.platform === 'win32',
14
- getSocketPath: () => join(testDir, 'test.sock'),
15
- getPidPath: () => join(testDir, 'test.pid'),
16
- getDbPath: () => join(testDir, 'test.db'),
17
- getLogPath: () => join(testDir, 'test.log'),
10
+ isMacOS: () => process.platform === "darwin",
11
+ isLinux: () => process.platform === "linux",
12
+ isWindows: () => process.platform === "win32",
13
+ getSocketPath: () => join(testDir, "test.sock"),
14
+ getPidPath: () => join(testDir, "test.pid"),
15
+ getDbPath: () => join(testDir, "test.db"),
16
+ getLogPath: () => join(testDir, "test.log"),
18
17
  ensureDataDir: () => {},
19
18
  }));
20
19
 
21
- mock.module('../util/logger.js', () => ({
20
+ mock.module("../util/logger.js", () => ({
22
21
  getLogger: () =>
23
22
  new Proxy({} as Record<string, unknown>, {
24
23
  get: () => () => {},
@@ -36,22 +35,22 @@ import {
36
35
  resolveCanonicalGuardianRequest,
37
36
  updateCanonicalGuardianDelivery,
38
37
  updateCanonicalGuardianRequest,
39
- } from '../memory/canonical-guardian-store.js';
40
- import { getDb, initializeDb, resetDb } from '../memory/db.js';
38
+ } from "../memory/canonical-guardian-store.js";
39
+ import { getDb, initializeDb, resetDb } from "../memory/db.js";
41
40
 
42
41
  initializeDb();
43
42
 
44
43
  // All decisionable kinds (tool_approval, pending_question, access_request)
45
44
  // require a guardianPrincipalId. Use a constant for test fixtures.
46
- const TEST_PRINCIPAL = 'test-principal-id';
45
+ const TEST_PRINCIPAL = "test-principal-id";
47
46
 
48
47
  function resetTables(): void {
49
48
  const db = getDb();
50
- db.run('DELETE FROM canonical_guardian_deliveries');
51
- db.run('DELETE FROM canonical_guardian_requests');
49
+ db.run("DELETE FROM canonical_guardian_deliveries");
50
+ db.run("DELETE FROM canonical_guardian_requests");
52
51
  }
53
52
 
54
- describe('canonical-guardian-store', () => {
53
+ describe("canonical-guardian-store", () => {
55
54
  beforeEach(() => {
56
55
  resetTables();
57
56
  });
@@ -67,196 +66,240 @@ describe('canonical-guardian-store', () => {
67
66
 
68
67
  // ── createCanonicalGuardianRequest ────────────────────────────────
69
68
 
70
- test('creates a request with all fields populated', () => {
69
+ test("creates a request with all fields populated", () => {
71
70
  const req = createCanonicalGuardianRequest({
72
- kind: 'tool_approval',
73
- sourceType: 'voice',
74
- sourceChannel: 'twilio',
75
- conversationId: 'conv-1',
76
- requesterExternalUserId: 'user-1',
77
- guardianExternalUserId: 'guardian-1',
78
- guardianPrincipalId: TEST_PRINCIPAL,
79
- callSessionId: 'session-1',
80
- pendingQuestionId: 'pq-1',
81
- questionText: 'Can I run this tool?',
82
- requestCode: 'ABC123',
83
- toolName: 'file_edit',
84
- inputDigest: 'sha256:deadbeef',
71
+ kind: "tool_approval",
72
+ sourceType: "voice",
73
+ sourceChannel: "twilio",
74
+ conversationId: "conv-1",
75
+ requesterExternalUserId: "user-1",
76
+ guardianExternalUserId: "guardian-1",
77
+ guardianPrincipalId: TEST_PRINCIPAL,
78
+ callSessionId: "session-1",
79
+ pendingQuestionId: "pq-1",
80
+ questionText: "Can I run this tool?",
81
+ requestCode: "ABC123",
82
+ toolName: "file_edit",
83
+ inputDigest: "sha256:deadbeef",
85
84
  expiresAt: new Date(Date.now() + 60_000).toISOString(),
86
85
  });
87
86
 
88
87
  expect(req.id).toBeTruthy();
89
- expect(req.kind).toBe('tool_approval');
90
- expect(req.sourceType).toBe('voice');
91
- expect(req.sourceChannel).toBe('twilio');
92
- expect(req.status).toBe('pending');
93
- expect(req.toolName).toBe('file_edit');
88
+ expect(req.kind).toBe("tool_approval");
89
+ expect(req.sourceType).toBe("voice");
90
+ expect(req.sourceChannel).toBe("twilio");
91
+ expect(req.status).toBe("pending");
92
+ expect(req.toolName).toBe("file_edit");
94
93
  expect(req.createdAt).toBeTruthy();
95
94
  expect(req.updatedAt).toBeTruthy();
96
95
  });
97
96
 
98
- test('creates a request with minimal fields', () => {
97
+ test("creates a request with minimal fields", () => {
99
98
  const req = createCanonicalGuardianRequest({
100
- kind: 'access_request',
101
- sourceType: 'channel',
99
+ kind: "access_request",
100
+ sourceType: "channel",
102
101
  guardianPrincipalId: TEST_PRINCIPAL,
103
102
  });
104
103
 
105
104
  expect(req.id).toBeTruthy();
106
- expect(req.kind).toBe('access_request');
107
- expect(req.sourceType).toBe('channel');
105
+ expect(req.kind).toBe("access_request");
106
+ expect(req.sourceType).toBe("channel");
108
107
  expect(req.sourceChannel).toBeNull();
109
108
  expect(req.conversationId).toBeNull();
110
109
  expect(req.toolName).toBeNull();
111
- expect(req.status).toBe('pending');
110
+ expect(req.status).toBe("pending");
112
111
  });
113
112
 
114
113
  // ── getCanonicalGuardianRequest ───────────────────────────────────
115
114
 
116
- test('gets a request by ID', () => {
115
+ test("gets a request by ID", () => {
117
116
  const created = createCanonicalGuardianRequest({
118
- kind: 'tool_approval',
119
- sourceType: 'voice',
117
+ kind: "tool_approval",
118
+ sourceType: "voice",
120
119
  guardianPrincipalId: TEST_PRINCIPAL,
121
120
  });
122
121
 
123
122
  const fetched = getCanonicalGuardianRequest(created.id);
124
123
  expect(fetched).not.toBeNull();
125
124
  expect(fetched!.id).toBe(created.id);
126
- expect(fetched!.kind).toBe('tool_approval');
125
+ expect(fetched!.kind).toBe("tool_approval");
127
126
  });
128
127
 
129
- test('returns null for nonexistent ID', () => {
130
- const fetched = getCanonicalGuardianRequest('nonexistent');
128
+ test("returns null for nonexistent ID", () => {
129
+ const fetched = getCanonicalGuardianRequest("nonexistent");
131
130
  expect(fetched).toBeNull();
132
131
  });
133
132
 
134
133
  // ── listCanonicalGuardianRequests ─────────────────────────────────
135
134
 
136
- test('lists all requests with no filters', () => {
137
- createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
138
- createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel', guardianPrincipalId: TEST_PRINCIPAL });
135
+ test("lists all requests with no filters", () => {
136
+ createCanonicalGuardianRequest({
137
+ kind: "tool_approval",
138
+ sourceType: "voice",
139
+ guardianPrincipalId: TEST_PRINCIPAL,
140
+ });
141
+ createCanonicalGuardianRequest({
142
+ kind: "access_request",
143
+ sourceType: "channel",
144
+ guardianPrincipalId: TEST_PRINCIPAL,
145
+ });
139
146
 
140
147
  const all = listCanonicalGuardianRequests();
141
148
  expect(all).toHaveLength(2);
142
149
  });
143
150
 
144
- test('filters by status', () => {
145
- createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
146
- const req2 = createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel', guardianPrincipalId: TEST_PRINCIPAL });
147
- updateCanonicalGuardianRequest(req2.id, { status: 'approved' });
151
+ test("filters by status", () => {
152
+ createCanonicalGuardianRequest({
153
+ kind: "tool_approval",
154
+ sourceType: "voice",
155
+ guardianPrincipalId: TEST_PRINCIPAL,
156
+ });
157
+ const req2 = createCanonicalGuardianRequest({
158
+ kind: "access_request",
159
+ sourceType: "channel",
160
+ guardianPrincipalId: TEST_PRINCIPAL,
161
+ });
162
+ updateCanonicalGuardianRequest(req2.id, { status: "approved" });
148
163
 
149
- const pending = listCanonicalGuardianRequests({ status: 'pending' });
164
+ const pending = listCanonicalGuardianRequests({ status: "pending" });
150
165
  expect(pending).toHaveLength(1);
151
- expect(pending[0].kind).toBe('tool_approval');
166
+ expect(pending[0].kind).toBe("tool_approval");
152
167
 
153
- const approved = listCanonicalGuardianRequests({ status: 'approved' });
168
+ const approved = listCanonicalGuardianRequests({ status: "approved" });
154
169
  expect(approved).toHaveLength(1);
155
- expect(approved[0].kind).toBe('access_request');
170
+ expect(approved[0].kind).toBe("access_request");
156
171
  });
157
172
 
158
- test('filters by guardianExternalUserId', () => {
173
+ test("filters by guardianExternalUserId", () => {
159
174
  createCanonicalGuardianRequest({
160
- kind: 'tool_approval',
161
- sourceType: 'voice',
162
- guardianExternalUserId: 'guardian-A',
175
+ kind: "tool_approval",
176
+ sourceType: "voice",
177
+ guardianExternalUserId: "guardian-A",
163
178
  guardianPrincipalId: TEST_PRINCIPAL,
164
179
  });
165
180
  createCanonicalGuardianRequest({
166
- kind: 'tool_approval',
167
- sourceType: 'voice',
168
- guardianExternalUserId: 'guardian-B',
181
+ kind: "tool_approval",
182
+ sourceType: "voice",
183
+ guardianExternalUserId: "guardian-B",
169
184
  guardianPrincipalId: TEST_PRINCIPAL,
170
185
  });
171
186
 
172
- const filtered = listCanonicalGuardianRequests({ guardianExternalUserId: 'guardian-A' });
187
+ const filtered = listCanonicalGuardianRequests({
188
+ guardianExternalUserId: "guardian-A",
189
+ });
173
190
  expect(filtered).toHaveLength(1);
174
- expect(filtered[0].guardianExternalUserId).toBe('guardian-A');
191
+ expect(filtered[0].guardianExternalUserId).toBe("guardian-A");
175
192
  });
176
193
 
177
- test('filters by conversationId', () => {
194
+ test("filters by conversationId", () => {
178
195
  createCanonicalGuardianRequest({
179
- kind: 'tool_approval',
180
- sourceType: 'voice',
181
- conversationId: 'conv-X',
196
+ kind: "tool_approval",
197
+ sourceType: "voice",
198
+ conversationId: "conv-X",
182
199
  guardianPrincipalId: TEST_PRINCIPAL,
183
200
  });
184
201
  createCanonicalGuardianRequest({
185
- kind: 'tool_approval',
186
- sourceType: 'voice',
187
- conversationId: 'conv-Y',
202
+ kind: "tool_approval",
203
+ sourceType: "voice",
204
+ conversationId: "conv-Y",
188
205
  guardianPrincipalId: TEST_PRINCIPAL,
189
206
  });
190
207
 
191
- const filtered = listCanonicalGuardianRequests({ conversationId: 'conv-X' });
208
+ const filtered = listCanonicalGuardianRequests({
209
+ conversationId: "conv-X",
210
+ });
192
211
  expect(filtered).toHaveLength(1);
193
212
  });
194
213
 
195
- test('filters by sourceType', () => {
196
- createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
197
- createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'channel', guardianPrincipalId: TEST_PRINCIPAL });
198
- createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'desktop', guardianPrincipalId: TEST_PRINCIPAL });
214
+ test("filters by sourceType", () => {
215
+ createCanonicalGuardianRequest({
216
+ kind: "tool_approval",
217
+ sourceType: "voice",
218
+ guardianPrincipalId: TEST_PRINCIPAL,
219
+ });
220
+ createCanonicalGuardianRequest({
221
+ kind: "tool_approval",
222
+ sourceType: "channel",
223
+ guardianPrincipalId: TEST_PRINCIPAL,
224
+ });
225
+ createCanonicalGuardianRequest({
226
+ kind: "tool_approval",
227
+ sourceType: "desktop",
228
+ guardianPrincipalId: TEST_PRINCIPAL,
229
+ });
199
230
 
200
- const voiceOnly = listCanonicalGuardianRequests({ sourceType: 'voice' });
231
+ const voiceOnly = listCanonicalGuardianRequests({ sourceType: "voice" });
201
232
  expect(voiceOnly).toHaveLength(1);
202
233
  });
203
234
 
204
- test('filters by kind', () => {
205
- createCanonicalGuardianRequest({ kind: 'tool_approval', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
206
- createCanonicalGuardianRequest({ kind: 'pending_question', sourceType: 'voice', guardianPrincipalId: TEST_PRINCIPAL });
207
- createCanonicalGuardianRequest({ kind: 'access_request', sourceType: 'channel', guardianPrincipalId: TEST_PRINCIPAL });
235
+ test("filters by kind", () => {
236
+ createCanonicalGuardianRequest({
237
+ kind: "tool_approval",
238
+ sourceType: "voice",
239
+ guardianPrincipalId: TEST_PRINCIPAL,
240
+ });
241
+ createCanonicalGuardianRequest({
242
+ kind: "pending_question",
243
+ sourceType: "voice",
244
+ guardianPrincipalId: TEST_PRINCIPAL,
245
+ });
246
+ createCanonicalGuardianRequest({
247
+ kind: "access_request",
248
+ sourceType: "channel",
249
+ guardianPrincipalId: TEST_PRINCIPAL,
250
+ });
208
251
 
209
- const toolOnly = listCanonicalGuardianRequests({ kind: 'tool_approval' });
252
+ const toolOnly = listCanonicalGuardianRequests({ kind: "tool_approval" });
210
253
  expect(toolOnly).toHaveLength(1);
211
254
  });
212
255
 
213
- test('combines multiple filters', () => {
256
+ test("combines multiple filters", () => {
214
257
  createCanonicalGuardianRequest({
215
- kind: 'tool_approval',
216
- sourceType: 'voice',
217
- guardianExternalUserId: 'guardian-A',
258
+ kind: "tool_approval",
259
+ sourceType: "voice",
260
+ guardianExternalUserId: "guardian-A",
218
261
  guardianPrincipalId: TEST_PRINCIPAL,
219
262
  });
220
263
  createCanonicalGuardianRequest({
221
- kind: 'tool_approval',
222
- sourceType: 'channel',
223
- guardianExternalUserId: 'guardian-A',
264
+ kind: "tool_approval",
265
+ sourceType: "channel",
266
+ guardianExternalUserId: "guardian-A",
224
267
  guardianPrincipalId: TEST_PRINCIPAL,
225
268
  });
226
269
  createCanonicalGuardianRequest({
227
- kind: 'access_request',
228
- sourceType: 'voice',
229
- guardianExternalUserId: 'guardian-A',
270
+ kind: "access_request",
271
+ sourceType: "voice",
272
+ guardianExternalUserId: "guardian-A",
230
273
  guardianPrincipalId: TEST_PRINCIPAL,
231
274
  });
232
275
 
233
276
  const filtered = listCanonicalGuardianRequests({
234
- kind: 'tool_approval',
235
- sourceType: 'voice',
236
- guardianExternalUserId: 'guardian-A',
277
+ kind: "tool_approval",
278
+ sourceType: "voice",
279
+ guardianExternalUserId: "guardian-A",
237
280
  });
238
281
  expect(filtered).toHaveLength(1);
239
282
  });
240
283
 
241
284
  // ── updateCanonicalGuardianRequest ────────────────────────────────
242
285
 
243
- test('updates request fields', () => {
286
+ test("updates request fields", () => {
244
287
  const req = createCanonicalGuardianRequest({
245
- kind: 'tool_approval',
246
- sourceType: 'voice',
288
+ kind: "tool_approval",
289
+ sourceType: "voice",
247
290
  guardianPrincipalId: TEST_PRINCIPAL,
248
291
  });
249
292
 
250
293
  const updated = updateCanonicalGuardianRequest(req.id, {
251
- status: 'approved',
252
- answerText: 'Looks good',
253
- decidedByExternalUserId: 'guardian-1',
294
+ status: "approved",
295
+ answerText: "Looks good",
296
+ decidedByExternalUserId: "guardian-1",
254
297
  });
255
298
 
256
299
  expect(updated).not.toBeNull();
257
- expect(updated!.status).toBe('approved');
258
- expect(updated!.answerText).toBe('Looks good');
259
- expect(updated!.decidedByExternalUserId).toBe('guardian-1');
300
+ expect(updated!.status).toBe("approved");
301
+ expect(updated!.answerText).toBe("Looks good");
302
+ expect(updated!.decidedByExternalUserId).toBe("guardian-1");
260
303
  // updatedAt should be at least as recent as the original (may be the
261
304
  // same millisecond when create+update run back-to-back in tests).
262
305
  expect(new Date(updated!.updatedAt).getTime()).toBeGreaterThanOrEqual(
@@ -264,201 +307,203 @@ describe('canonical-guardian-store', () => {
264
307
  );
265
308
  });
266
309
 
267
- test('returns null when updating nonexistent request', () => {
268
- const updated = updateCanonicalGuardianRequest('nonexistent', { status: 'approved' });
310
+ test("returns null when updating nonexistent request", () => {
311
+ const updated = updateCanonicalGuardianRequest("nonexistent", {
312
+ status: "approved",
313
+ });
269
314
  expect(updated).toBeNull();
270
315
  });
271
316
 
272
317
  // ── resolveCanonicalGuardianRequest (CAS) ─────────────────────────
273
318
 
274
- test('resolves a pending request to approved', () => {
319
+ test("resolves a pending request to approved", () => {
275
320
  const req = createCanonicalGuardianRequest({
276
- kind: 'tool_approval',
277
- sourceType: 'voice',
321
+ kind: "tool_approval",
322
+ sourceType: "voice",
278
323
  guardianPrincipalId: TEST_PRINCIPAL,
279
324
  });
280
325
 
281
- const resolved = resolveCanonicalGuardianRequest(req.id, 'pending', {
282
- status: 'approved',
283
- answerText: 'Approved by guardian',
284
- decidedByExternalUserId: 'guardian-1',
326
+ const resolved = resolveCanonicalGuardianRequest(req.id, "pending", {
327
+ status: "approved",
328
+ answerText: "Approved by guardian",
329
+ decidedByExternalUserId: "guardian-1",
285
330
  });
286
331
 
287
332
  expect(resolved).not.toBeNull();
288
- expect(resolved!.status).toBe('approved');
289
- expect(resolved!.answerText).toBe('Approved by guardian');
290
- expect(resolved!.decidedByExternalUserId).toBe('guardian-1');
333
+ expect(resolved!.status).toBe("approved");
334
+ expect(resolved!.answerText).toBe("Approved by guardian");
335
+ expect(resolved!.decidedByExternalUserId).toBe("guardian-1");
291
336
  });
292
337
 
293
- test('resolves a pending request to denied', () => {
338
+ test("resolves a pending request to denied", () => {
294
339
  const req = createCanonicalGuardianRequest({
295
- kind: 'tool_approval',
296
- sourceType: 'channel',
340
+ kind: "tool_approval",
341
+ sourceType: "channel",
297
342
  guardianPrincipalId: TEST_PRINCIPAL,
298
343
  });
299
344
 
300
- const resolved = resolveCanonicalGuardianRequest(req.id, 'pending', {
301
- status: 'denied',
302
- answerText: 'Not allowed',
345
+ const resolved = resolveCanonicalGuardianRequest(req.id, "pending", {
346
+ status: "denied",
347
+ answerText: "Not allowed",
303
348
  });
304
349
 
305
350
  expect(resolved).not.toBeNull();
306
- expect(resolved!.status).toBe('denied');
351
+ expect(resolved!.status).toBe("denied");
307
352
  });
308
353
 
309
- test('CAS fails when expectedStatus does not match', () => {
354
+ test("CAS fails when expectedStatus does not match", () => {
310
355
  const req = createCanonicalGuardianRequest({
311
- kind: 'tool_approval',
312
- sourceType: 'voice',
356
+ kind: "tool_approval",
357
+ sourceType: "voice",
313
358
  guardianPrincipalId: TEST_PRINCIPAL,
314
359
  });
315
360
 
316
361
  // Try to resolve with wrong expected status
317
- const result = resolveCanonicalGuardianRequest(req.id, 'approved', {
318
- status: 'denied',
362
+ const result = resolveCanonicalGuardianRequest(req.id, "approved", {
363
+ status: "denied",
319
364
  });
320
365
 
321
366
  expect(result).toBeNull();
322
367
 
323
368
  // Verify the request is unchanged
324
369
  const unchanged = getCanonicalGuardianRequest(req.id);
325
- expect(unchanged!.status).toBe('pending');
370
+ expect(unchanged!.status).toBe("pending");
326
371
  });
327
372
 
328
- test('CAS race condition: two concurrent resolves, only one succeeds', () => {
373
+ test("CAS race condition: two concurrent resolves, only one succeeds", () => {
329
374
  const req = createCanonicalGuardianRequest({
330
- kind: 'tool_approval',
331
- sourceType: 'voice',
375
+ kind: "tool_approval",
376
+ sourceType: "voice",
332
377
  guardianPrincipalId: TEST_PRINCIPAL,
333
378
  });
334
379
 
335
380
  // First resolve succeeds
336
- const first = resolveCanonicalGuardianRequest(req.id, 'pending', {
337
- status: 'approved',
338
- answerText: 'First approver',
339
- decidedByExternalUserId: 'guardian-1',
381
+ const first = resolveCanonicalGuardianRequest(req.id, "pending", {
382
+ status: "approved",
383
+ answerText: "First approver",
384
+ decidedByExternalUserId: "guardian-1",
340
385
  });
341
386
  expect(first).not.toBeNull();
342
- expect(first!.status).toBe('approved');
387
+ expect(first!.status).toBe("approved");
343
388
 
344
389
  // Second resolve fails because status is no longer 'pending'
345
- const second = resolveCanonicalGuardianRequest(req.id, 'pending', {
346
- status: 'denied',
347
- answerText: 'Second denier',
348
- decidedByExternalUserId: 'guardian-2',
390
+ const second = resolveCanonicalGuardianRequest(req.id, "pending", {
391
+ status: "denied",
392
+ answerText: "Second denier",
393
+ decidedByExternalUserId: "guardian-2",
349
394
  });
350
395
  expect(second).toBeNull();
351
396
 
352
397
  // Verify the first decision stuck
353
398
  const final = getCanonicalGuardianRequest(req.id);
354
- expect(final!.status).toBe('approved');
355
- expect(final!.answerText).toBe('First approver');
356
- expect(final!.decidedByExternalUserId).toBe('guardian-1');
399
+ expect(final!.status).toBe("approved");
400
+ expect(final!.answerText).toBe("First approver");
401
+ expect(final!.decidedByExternalUserId).toBe("guardian-1");
357
402
  });
358
403
 
359
- test('CAS returns null for nonexistent request', () => {
360
- const result = resolveCanonicalGuardianRequest('nonexistent', 'pending', {
361
- status: 'approved',
404
+ test("CAS returns null for nonexistent request", () => {
405
+ const result = resolveCanonicalGuardianRequest("nonexistent", "pending", {
406
+ status: "approved",
362
407
  });
363
408
  expect(result).toBeNull();
364
409
  });
365
410
 
366
411
  // ── Voice-originated and channel-originated request shapes ────────
367
412
 
368
- test('voice-originated request shape is representable', () => {
413
+ test("voice-originated request shape is representable", () => {
369
414
  const req = createCanonicalGuardianRequest({
370
- kind: 'pending_question',
371
- sourceType: 'voice',
372
- sourceChannel: 'twilio',
373
- conversationId: 'conv-voice-1',
374
- guardianExternalUserId: 'guardian-phone',
375
- guardianPrincipalId: TEST_PRINCIPAL,
376
- callSessionId: 'call-123',
377
- pendingQuestionId: 'pq-456',
378
- questionText: 'What is the gate code?',
379
- requestCode: 'A1B2C3',
415
+ kind: "pending_question",
416
+ sourceType: "voice",
417
+ sourceChannel: "twilio",
418
+ conversationId: "conv-voice-1",
419
+ guardianExternalUserId: "guardian-phone",
420
+ guardianPrincipalId: TEST_PRINCIPAL,
421
+ callSessionId: "call-123",
422
+ pendingQuestionId: "pq-456",
423
+ questionText: "What is the gate code?",
424
+ requestCode: "A1B2C3",
380
425
  expiresAt: new Date(Date.now() + 30_000).toISOString(),
381
426
  });
382
427
 
383
- expect(req.sourceType).toBe('voice');
384
- expect(req.callSessionId).toBe('call-123');
385
- expect(req.pendingQuestionId).toBe('pq-456');
386
- expect(req.requestCode).toBe('A1B2C3');
428
+ expect(req.sourceType).toBe("voice");
429
+ expect(req.callSessionId).toBe("call-123");
430
+ expect(req.pendingQuestionId).toBe("pq-456");
431
+ expect(req.requestCode).toBe("A1B2C3");
387
432
  });
388
433
 
389
- test('channel-originated request shape is representable', () => {
434
+ test("channel-originated request shape is representable", () => {
390
435
  const req = createCanonicalGuardianRequest({
391
- kind: 'tool_approval',
392
- sourceType: 'channel',
393
- sourceChannel: 'telegram',
394
- conversationId: 'conv-tg-1',
395
- requesterExternalUserId: 'requester-tg-user',
396
- guardianExternalUserId: 'guardian-tg-user',
397
- guardianPrincipalId: TEST_PRINCIPAL,
398
- toolName: 'execute_code',
399
- inputDigest: 'sha256:abcdef',
436
+ kind: "tool_approval",
437
+ sourceType: "channel",
438
+ sourceChannel: "telegram",
439
+ conversationId: "conv-tg-1",
440
+ requesterExternalUserId: "requester-tg-user",
441
+ guardianExternalUserId: "guardian-tg-user",
442
+ guardianPrincipalId: TEST_PRINCIPAL,
443
+ toolName: "execute_code",
444
+ inputDigest: "sha256:abcdef",
400
445
  expiresAt: new Date(Date.now() + 120_000).toISOString(),
401
446
  });
402
447
 
403
- expect(req.sourceType).toBe('channel');
404
- expect(req.sourceChannel).toBe('telegram');
405
- expect(req.requesterExternalUserId).toBe('requester-tg-user');
406
- expect(req.toolName).toBe('execute_code');
448
+ expect(req.sourceType).toBe("channel");
449
+ expect(req.sourceChannel).toBe("telegram");
450
+ expect(req.requesterExternalUserId).toBe("requester-tg-user");
451
+ expect(req.toolName).toBe("execute_code");
407
452
  // Voice-specific fields are null for channel requests
408
453
  expect(req.callSessionId).toBeNull();
409
454
  expect(req.pendingQuestionId).toBeNull();
410
455
  });
411
456
 
412
- test('desktop-originated request shape is representable', () => {
457
+ test("desktop-originated request shape is representable", () => {
413
458
  const req = createCanonicalGuardianRequest({
414
- kind: 'access_request',
415
- sourceType: 'desktop',
416
- conversationId: 'conv-desktop-1',
417
- guardianExternalUserId: 'guardian-desktop',
459
+ kind: "access_request",
460
+ sourceType: "desktop",
461
+ conversationId: "conv-desktop-1",
462
+ guardianExternalUserId: "guardian-desktop",
418
463
  guardianPrincipalId: TEST_PRINCIPAL,
419
- questionText: 'User wants to access settings',
464
+ questionText: "User wants to access settings",
420
465
  });
421
466
 
422
- expect(req.sourceType).toBe('desktop');
467
+ expect(req.sourceType).toBe("desktop");
423
468
  expect(req.sourceChannel).toBeNull();
424
469
  expect(req.callSessionId).toBeNull();
425
470
  });
426
471
 
427
472
  // ── Canonical Guardian Deliveries ─────────────────────────────────
428
473
 
429
- test('creates and lists deliveries for a request', () => {
474
+ test("creates and lists deliveries for a request", () => {
430
475
  const req = createCanonicalGuardianRequest({
431
- kind: 'tool_approval',
432
- sourceType: 'voice',
476
+ kind: "tool_approval",
477
+ sourceType: "voice",
433
478
  guardianPrincipalId: TEST_PRINCIPAL,
434
479
  });
435
480
 
436
481
  const d1 = createCanonicalGuardianDelivery({
437
482
  requestId: req.id,
438
- destinationChannel: 'telegram',
439
- destinationChatId: 'chat-123',
483
+ destinationChannel: "telegram",
484
+ destinationChatId: "chat-123",
440
485
  });
441
486
  createCanonicalGuardianDelivery({
442
487
  requestId: req.id,
443
- destinationChannel: 'sms',
444
- destinationChatId: 'chat-456',
488
+ destinationChannel: "sms",
489
+ destinationChatId: "chat-456",
445
490
  });
446
491
 
447
492
  expect(d1.id).toBeTruthy();
448
493
  expect(d1.requestId).toBe(req.id);
449
- expect(d1.destinationChannel).toBe('telegram');
450
- expect(d1.status).toBe('pending');
494
+ expect(d1.destinationChannel).toBe("telegram");
495
+ expect(d1.status).toBe("pending");
451
496
 
452
497
  const deliveries = listCanonicalGuardianDeliveries(req.id);
453
498
  expect(deliveries).toHaveLength(2);
454
499
  const channels = deliveries.map((d) => d.destinationChannel).sort();
455
- expect(channels).toEqual(['sms', 'telegram']);
500
+ expect(channels).toEqual(["sms", "telegram"]);
456
501
  });
457
502
 
458
- test('lists empty deliveries for a request with none', () => {
503
+ test("lists empty deliveries for a request with none", () => {
459
504
  const req = createCanonicalGuardianRequest({
460
- kind: 'tool_approval',
461
- sourceType: 'voice',
505
+ kind: "tool_approval",
506
+ sourceType: "voice",
462
507
  guardianPrincipalId: TEST_PRINCIPAL,
463
508
  });
464
509
 
@@ -466,204 +511,210 @@ describe('canonical-guardian-store', () => {
466
511
  expect(deliveries).toHaveLength(0);
467
512
  });
468
513
 
469
- test('lists pending requests by destination conversation', () => {
514
+ test("lists pending requests by destination conversation", () => {
470
515
  const pendingReq = createCanonicalGuardianRequest({
471
- kind: 'pending_question',
472
- sourceType: 'voice',
516
+ kind: "pending_question",
517
+ sourceType: "voice",
473
518
  guardianPrincipalId: TEST_PRINCIPAL,
474
519
  });
475
520
  const resolvedReq = createCanonicalGuardianRequest({
476
- kind: 'pending_question',
477
- sourceType: 'voice',
521
+ kind: "pending_question",
522
+ sourceType: "voice",
478
523
  guardianPrincipalId: TEST_PRINCIPAL,
479
524
  });
480
- updateCanonicalGuardianRequest(resolvedReq.id, { status: 'approved' });
525
+ updateCanonicalGuardianRequest(resolvedReq.id, { status: "approved" });
481
526
 
482
527
  createCanonicalGuardianDelivery({
483
528
  requestId: pendingReq.id,
484
- destinationChannel: 'vellum',
485
- destinationConversationId: 'conv-guardian-1',
529
+ destinationChannel: "vellum",
530
+ destinationConversationId: "conv-guardian-1",
486
531
  });
487
532
  createCanonicalGuardianDelivery({
488
533
  requestId: resolvedReq.id,
489
- destinationChannel: 'vellum',
490
- destinationConversationId: 'conv-guardian-1',
534
+ destinationChannel: "vellum",
535
+ destinationConversationId: "conv-guardian-1",
491
536
  });
492
537
 
493
- const pending = listPendingCanonicalGuardianRequestsByDestinationConversation(
494
- 'conv-guardian-1',
495
- 'vellum',
496
- );
538
+ const pending =
539
+ listPendingCanonicalGuardianRequestsByDestinationConversation(
540
+ "conv-guardian-1",
541
+ "vellum",
542
+ );
497
543
  expect(pending).toHaveLength(1);
498
544
  expect(pending[0].id).toBe(pendingReq.id);
499
545
  });
500
546
 
501
- test('destination conversation lookup deduplicates request IDs', () => {
547
+ test("destination conversation lookup deduplicates request IDs", () => {
502
548
  const req = createCanonicalGuardianRequest({
503
- kind: 'pending_question',
504
- sourceType: 'voice',
549
+ kind: "pending_question",
550
+ sourceType: "voice",
505
551
  guardianPrincipalId: TEST_PRINCIPAL,
506
552
  });
507
553
 
508
554
  createCanonicalGuardianDelivery({
509
555
  requestId: req.id,
510
- destinationChannel: 'vellum',
511
- destinationConversationId: 'conv-guardian-2',
556
+ destinationChannel: "vellum",
557
+ destinationConversationId: "conv-guardian-2",
512
558
  });
513
559
  createCanonicalGuardianDelivery({
514
560
  requestId: req.id,
515
- destinationChannel: 'telegram',
516
- destinationConversationId: 'conv-guardian-2',
561
+ destinationChannel: "telegram",
562
+ destinationConversationId: "conv-guardian-2",
517
563
  });
518
564
 
519
- const pending = listPendingCanonicalGuardianRequestsByDestinationConversation('conv-guardian-2');
565
+ const pending =
566
+ listPendingCanonicalGuardianRequestsByDestinationConversation(
567
+ "conv-guardian-2",
568
+ );
520
569
  expect(pending).toHaveLength(1);
521
570
  expect(pending[0].id).toBe(req.id);
522
571
  });
523
572
 
524
- test('updates delivery status', () => {
573
+ test("updates delivery status", () => {
525
574
  const req = createCanonicalGuardianRequest({
526
- kind: 'tool_approval',
527
- sourceType: 'voice',
575
+ kind: "tool_approval",
576
+ sourceType: "voice",
528
577
  guardianPrincipalId: TEST_PRINCIPAL,
529
578
  });
530
579
  const delivery = createCanonicalGuardianDelivery({
531
580
  requestId: req.id,
532
- destinationChannel: 'telegram',
581
+ destinationChannel: "telegram",
533
582
  });
534
583
 
535
584
  const updated = updateCanonicalGuardianDelivery(delivery.id, {
536
- status: 'sent',
537
- destinationMessageId: 'msg-789',
585
+ status: "sent",
586
+ destinationMessageId: "msg-789",
538
587
  });
539
588
 
540
589
  expect(updated).not.toBeNull();
541
- expect(updated!.status).toBe('sent');
542
- expect(updated!.destinationMessageId).toBe('msg-789');
590
+ expect(updated!.status).toBe("sent");
591
+ expect(updated!.destinationMessageId).toBe("msg-789");
543
592
  });
544
593
 
545
- test('returns null when updating nonexistent delivery', () => {
546
- const updated = updateCanonicalGuardianDelivery('nonexistent', { status: 'sent' });
594
+ test("returns null when updating nonexistent delivery", () => {
595
+ const updated = updateCanonicalGuardianDelivery("nonexistent", {
596
+ status: "sent",
597
+ });
547
598
  expect(updated).toBeNull();
548
599
  });
549
600
 
550
601
  // ── listPendingCanonicalGuardianRequestsByDestinationChat ──────────
551
602
 
552
- test('returns pending requests matching (destinationChannel, destinationChatId)', () => {
603
+ test("returns pending requests matching (destinationChannel, destinationChatId)", () => {
553
604
  const req = createCanonicalGuardianRequest({
554
- kind: 'pending_question',
555
- sourceType: 'voice',
605
+ kind: "pending_question",
606
+ sourceType: "voice",
556
607
  guardianPrincipalId: TEST_PRINCIPAL,
557
608
  });
558
609
  createCanonicalGuardianDelivery({
559
610
  requestId: req.id,
560
- destinationChannel: 'telegram',
561
- destinationChatId: 'guardian-chat-100',
611
+ destinationChannel: "telegram",
612
+ destinationChatId: "guardian-chat-100",
562
613
  });
563
614
 
564
615
  const pending = listPendingCanonicalGuardianRequestsByDestinationChat(
565
- 'telegram',
566
- 'guardian-chat-100',
616
+ "telegram",
617
+ "guardian-chat-100",
567
618
  );
568
619
  expect(pending).toHaveLength(1);
569
620
  expect(pending[0].id).toBe(req.id);
570
621
  });
571
622
 
572
- test('excludes non-pending requests from destination chat lookup', () => {
623
+ test("excludes non-pending requests from destination chat lookup", () => {
573
624
  const pendingReq = createCanonicalGuardianRequest({
574
- kind: 'pending_question',
575
- sourceType: 'voice',
625
+ kind: "pending_question",
626
+ sourceType: "voice",
576
627
  guardianPrincipalId: TEST_PRINCIPAL,
577
628
  });
578
629
  const resolvedReq = createCanonicalGuardianRequest({
579
- kind: 'pending_question',
580
- sourceType: 'voice',
630
+ kind: "pending_question",
631
+ sourceType: "voice",
581
632
  guardianPrincipalId: TEST_PRINCIPAL,
582
633
  });
583
- updateCanonicalGuardianRequest(resolvedReq.id, { status: 'approved' });
634
+ updateCanonicalGuardianRequest(resolvedReq.id, { status: "approved" });
584
635
 
585
636
  createCanonicalGuardianDelivery({
586
637
  requestId: pendingReq.id,
587
- destinationChannel: 'telegram',
588
- destinationChatId: 'guardian-chat-200',
638
+ destinationChannel: "telegram",
639
+ destinationChatId: "guardian-chat-200",
589
640
  });
590
641
  createCanonicalGuardianDelivery({
591
642
  requestId: resolvedReq.id,
592
- destinationChannel: 'telegram',
593
- destinationChatId: 'guardian-chat-200',
643
+ destinationChannel: "telegram",
644
+ destinationChatId: "guardian-chat-200",
594
645
  });
595
646
 
596
647
  const pending = listPendingCanonicalGuardianRequestsByDestinationChat(
597
- 'telegram',
598
- 'guardian-chat-200',
648
+ "telegram",
649
+ "guardian-chat-200",
599
650
  );
600
651
  expect(pending).toHaveLength(1);
601
652
  expect(pending[0].id).toBe(pendingReq.id);
602
653
  });
603
654
 
604
- test('deduplicates when multiple delivery rows point to same request', () => {
655
+ test("deduplicates when multiple delivery rows point to same request", () => {
605
656
  const req = createCanonicalGuardianRequest({
606
- kind: 'pending_question',
607
- sourceType: 'voice',
657
+ kind: "pending_question",
658
+ sourceType: "voice",
608
659
  guardianPrincipalId: TEST_PRINCIPAL,
609
660
  });
610
661
 
611
662
  // Two delivery rows targeting the same chat for the same request
612
663
  createCanonicalGuardianDelivery({
613
664
  requestId: req.id,
614
- destinationChannel: 'telegram',
615
- destinationChatId: 'guardian-chat-300',
616
- destinationMessageId: 'msg-1',
665
+ destinationChannel: "telegram",
666
+ destinationChatId: "guardian-chat-300",
667
+ destinationMessageId: "msg-1",
617
668
  });
618
669
  createCanonicalGuardianDelivery({
619
670
  requestId: req.id,
620
- destinationChannel: 'telegram',
621
- destinationChatId: 'guardian-chat-300',
622
- destinationMessageId: 'msg-2',
671
+ destinationChannel: "telegram",
672
+ destinationChatId: "guardian-chat-300",
673
+ destinationMessageId: "msg-2",
623
674
  });
624
675
 
625
676
  const pending = listPendingCanonicalGuardianRequestsByDestinationChat(
626
- 'telegram',
627
- 'guardian-chat-300',
677
+ "telegram",
678
+ "guardian-chat-300",
628
679
  );
629
680
  expect(pending).toHaveLength(1);
630
681
  expect(pending[0].id).toBe(req.id);
631
682
  });
632
683
 
633
- test('channel mismatch does not match in destination chat lookup', () => {
684
+ test("channel mismatch does not match in destination chat lookup", () => {
634
685
  const req = createCanonicalGuardianRequest({
635
- kind: 'pending_question',
636
- sourceType: 'voice',
686
+ kind: "pending_question",
687
+ sourceType: "voice",
637
688
  guardianPrincipalId: TEST_PRINCIPAL,
638
689
  });
639
690
  createCanonicalGuardianDelivery({
640
691
  requestId: req.id,
641
- destinationChannel: 'telegram',
642
- destinationChatId: 'guardian-chat-400',
692
+ destinationChannel: "telegram",
693
+ destinationChatId: "guardian-chat-400",
643
694
  });
644
695
 
645
696
  const pending = listPendingCanonicalGuardianRequestsByDestinationChat(
646
- 'sms',
647
- 'guardian-chat-400',
697
+ "sms",
698
+ "guardian-chat-400",
648
699
  );
649
700
  expect(pending).toHaveLength(0);
650
701
  });
651
702
 
652
- test('chat mismatch does not match in destination chat lookup', () => {
703
+ test("chat mismatch does not match in destination chat lookup", () => {
653
704
  const req = createCanonicalGuardianRequest({
654
- kind: 'pending_question',
655
- sourceType: 'voice',
705
+ kind: "pending_question",
706
+ sourceType: "voice",
656
707
  guardianPrincipalId: TEST_PRINCIPAL,
657
708
  });
658
709
  createCanonicalGuardianDelivery({
659
710
  requestId: req.id,
660
- destinationChannel: 'telegram',
661
- destinationChatId: 'guardian-chat-500',
711
+ destinationChannel: "telegram",
712
+ destinationChatId: "guardian-chat-500",
662
713
  });
663
714
 
664
715
  const pending = listPendingCanonicalGuardianRequestsByDestinationChat(
665
- 'telegram',
666
- 'different-chat-id',
716
+ "telegram",
717
+ "different-chat-id",
667
718
  );
668
719
  expect(pending).toHaveLength(0);
669
720
  });