@vellumai/assistant 0.4.17 → 0.4.19

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 (528) hide show
  1. package/docs/runbook-trusted-contacts.md +5 -3
  2. package/eslint.config.mjs +2 -2
  3. package/package.json +1 -1
  4. package/src/__tests__/access-request-decision.test.ts +128 -120
  5. package/src/__tests__/account-registry.test.ts +121 -110
  6. package/src/__tests__/active-skill-tools.test.ts +200 -172
  7. package/src/__tests__/actor-token-service.test.ts +341 -274
  8. package/src/__tests__/agent-loop-thinking.test.ts +28 -19
  9. package/src/__tests__/agent-loop.test.ts +798 -378
  10. package/src/__tests__/anthropic-provider.test.ts +405 -247
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
  12. package/src/__tests__/app-bundler.test.ts +112 -79
  13. package/src/__tests__/app-executors.test.ts +205 -178
  14. package/src/__tests__/app-git-history.test.ts +90 -73
  15. package/src/__tests__/app-git-service.test.ts +67 -53
  16. package/src/__tests__/app-open-proxy.test.ts +29 -25
  17. package/src/__tests__/approval-conversation-turn.test.ts +100 -81
  18. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
  19. package/src/__tests__/approval-message-composer.test.ts +119 -119
  20. package/src/__tests__/approval-primitive.test.ts +264 -233
  21. package/src/__tests__/approval-routes-http.test.ts +4 -3
  22. package/src/__tests__/asset-materialize-tool.test.ts +250 -178
  23. package/src/__tests__/asset-search-tool.test.ts +251 -191
  24. package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
  25. package/src/__tests__/assistant-attachments.test.ts +254 -186
  26. package/src/__tests__/assistant-event-hub.test.ts +105 -63
  27. package/src/__tests__/assistant-event.test.ts +66 -58
  28. package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
  29. package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
  30. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
  31. package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
  32. package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
  33. package/src/__tests__/attachments-store.test.ts +240 -183
  34. package/src/__tests__/attachments.test.ts +70 -62
  35. package/src/__tests__/audit-log-rotation.test.ts +50 -35
  36. package/src/__tests__/browser-fill-credential.test.ts +169 -101
  37. package/src/__tests__/browser-manager.test.ts +97 -75
  38. package/src/__tests__/browser-runtime-check.test.ts +16 -15
  39. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
  40. package/src/__tests__/browser-skill-endstate.test.ts +97 -72
  41. package/src/__tests__/bundle-scanner.test.ts +47 -22
  42. package/src/__tests__/bundled-asset.test.ts +74 -47
  43. package/src/__tests__/call-constants.test.ts +19 -19
  44. package/src/__tests__/call-controller.test.ts +0 -1
  45. package/src/__tests__/call-conversation-messages.test.ts +90 -65
  46. package/src/__tests__/call-domain.test.ts +149 -121
  47. package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
  48. package/src/__tests__/call-pointer-messages.test.ts +213 -154
  49. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
  50. package/src/__tests__/call-recovery.test.ts +232 -212
  51. package/src/__tests__/call-routes-http.test.ts +0 -1
  52. package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
  53. package/src/__tests__/call-state-machine.test.ts +62 -51
  54. package/src/__tests__/call-state.test.ts +89 -75
  55. package/src/__tests__/call-store.test.ts +387 -316
  56. package/src/__tests__/callback-handoff-copy.test.ts +84 -82
  57. package/src/__tests__/canonical-guardian-store.test.ts +331 -280
  58. package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
  59. package/src/__tests__/channel-approval.test.ts +139 -137
  60. package/src/__tests__/channel-approvals.test.ts +7 -2
  61. package/src/__tests__/channel-delivery-store.test.ts +232 -194
  62. package/src/__tests__/channel-guardian.test.ts +5 -3
  63. package/src/__tests__/channel-invite-transport.test.ts +107 -92
  64. package/src/__tests__/channel-policy.test.ts +42 -38
  65. package/src/__tests__/channel-readiness-service.test.ts +119 -102
  66. package/src/__tests__/channel-reply-delivery.test.ts +147 -118
  67. package/src/__tests__/channel-retry-sweep.test.ts +153 -110
  68. package/src/__tests__/checker.test.ts +3309 -1850
  69. package/src/__tests__/clarification-resolver.test.ts +91 -79
  70. package/src/__tests__/classifier.test.ts +64 -54
  71. package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
  72. package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
  73. package/src/__tests__/clawhub.test.ts +92 -82
  74. package/src/__tests__/cli.test.ts +30 -30
  75. package/src/__tests__/clipboard.test.ts +53 -46
  76. package/src/__tests__/commit-guarantee.test.ts +59 -52
  77. package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
  78. package/src/__tests__/compaction.benchmark.test.ts +33 -31
  79. package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
  80. package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
  81. package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
  82. package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
  83. package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
  84. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
  85. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
  86. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
  87. package/src/__tests__/computer-use-tools.test.ts +121 -98
  88. package/src/__tests__/config-schema.test.ts +443 -347
  89. package/src/__tests__/config-watcher.test.ts +96 -81
  90. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
  91. package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
  92. package/src/__tests__/conflict-policy.test.ts +151 -80
  93. package/src/__tests__/conflict-store.test.ts +203 -157
  94. package/src/__tests__/connection-policy.test.ts +89 -59
  95. package/src/__tests__/contacts-tools.test.ts +247 -178
  96. package/src/__tests__/context-memory-e2e.test.ts +306 -214
  97. package/src/__tests__/context-token-estimator.test.ts +114 -74
  98. package/src/__tests__/context-window-manager.test.ts +269 -167
  99. package/src/__tests__/contradiction-checker.test.ts +161 -135
  100. package/src/__tests__/conversation-attention-store.test.ts +350 -290
  101. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  102. package/src/__tests__/conversation-pairing.test.ts +220 -113
  103. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  104. package/src/__tests__/conversation-store.test.ts +390 -235
  105. package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
  106. package/src/__tests__/credential-broker-server-use.test.ts +283 -243
  107. package/src/__tests__/credential-broker.test.ts +128 -74
  108. package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
  109. package/src/__tests__/credential-metadata-store.test.ts +360 -311
  110. package/src/__tests__/credential-policy-validate.test.ts +81 -65
  111. package/src/__tests__/credential-resolve.test.ts +212 -145
  112. package/src/__tests__/credential-security-e2e.test.ts +144 -103
  113. package/src/__tests__/credential-security-invariants.test.ts +253 -208
  114. package/src/__tests__/credential-selection.test.ts +254 -146
  115. package/src/__tests__/credential-vault-unit.test.ts +531 -341
  116. package/src/__tests__/credential-vault.test.ts +761 -484
  117. package/src/__tests__/daemon-assistant-events.test.ts +91 -66
  118. package/src/__tests__/daemon-lifecycle.test.ts +258 -190
  119. package/src/__tests__/daemon-server-session-init.test.ts +2 -1
  120. package/src/__tests__/date-context.test.ts +314 -249
  121. package/src/__tests__/db-migration-rollback.test.ts +259 -130
  122. package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
  123. package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
  124. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
  125. package/src/__tests__/dictation-mode-detection.test.ts +77 -55
  126. package/src/__tests__/dictation-profile-store.test.ts +70 -56
  127. package/src/__tests__/dictation-text-processing.test.ts +53 -35
  128. package/src/__tests__/diff.test.ts +102 -98
  129. package/src/__tests__/domain-normalize.test.ts +54 -54
  130. package/src/__tests__/domain-policy.test.ts +71 -55
  131. package/src/__tests__/dynamic-page-surface.test.ts +31 -33
  132. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
  133. package/src/__tests__/edit-engine.test.ts +56 -56
  134. package/src/__tests__/elevenlabs-client.test.ts +117 -91
  135. package/src/__tests__/elevenlabs-config.test.ts +32 -31
  136. package/src/__tests__/email-classifier.test.ts +15 -12
  137. package/src/__tests__/email-cli.test.ts +121 -108
  138. package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
  139. package/src/__tests__/encrypted-store.test.ts +180 -154
  140. package/src/__tests__/entity-extractor.test.ts +108 -87
  141. package/src/__tests__/entity-search.test.ts +664 -258
  142. package/src/__tests__/ephemeral-permissions.test.ts +224 -188
  143. package/src/__tests__/event-bus.test.ts +81 -77
  144. package/src/__tests__/extract-email.test.ts +29 -20
  145. package/src/__tests__/file-edit-tool.test.ts +62 -44
  146. package/src/__tests__/file-ops-service.test.ts +131 -114
  147. package/src/__tests__/file-read-tool.test.ts +48 -31
  148. package/src/__tests__/file-write-tool.test.ts +43 -37
  149. package/src/__tests__/filesystem-tools.test.ts +238 -209
  150. package/src/__tests__/followup-tools.test.ts +237 -162
  151. package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
  152. package/src/__tests__/frontmatter.test.ts +96 -81
  153. package/src/__tests__/fuzzy-match-property.test.ts +75 -81
  154. package/src/__tests__/fuzzy-match.test.ts +71 -65
  155. package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
  156. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  157. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  158. package/src/__tests__/gemini-image-service.test.ts +113 -100
  159. package/src/__tests__/gemini-provider.test.ts +297 -220
  160. package/src/__tests__/get-weather.test.ts +188 -114
  161. package/src/__tests__/gmail-integration.test.ts +13 -5
  162. package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
  163. package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
  164. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
  165. package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
  166. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
  167. package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
  168. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
  169. package/src/__tests__/guardian-action-store.test.ts +158 -109
  170. package/src/__tests__/guardian-action-sweep.test.ts +114 -100
  171. package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
  172. package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
  173. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
  174. package/src/__tests__/guardian-dispatch.test.ts +316 -256
  175. package/src/__tests__/guardian-grant-minting.test.ts +247 -178
  176. package/src/__tests__/guardian-outbound-http.test.ts +5 -3
  177. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
  178. package/src/__tests__/guardian-question-copy.test.ts +17 -17
  179. package/src/__tests__/guardian-question-mode.test.ts +134 -100
  180. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  181. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  182. package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
  183. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  184. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
  185. package/src/__tests__/handle-user-message-secret-resume.test.ts +7 -2
  186. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
  187. package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
  188. package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
  189. package/src/__tests__/handlers-slack-config.test.ts +63 -54
  190. package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
  191. package/src/__tests__/handlers-telegram-config.test.ts +662 -329
  192. package/src/__tests__/handlers-twitter-config.test.ts +525 -298
  193. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +5 -2
  194. package/src/__tests__/headless-browser-interactions.test.ts +444 -280
  195. package/src/__tests__/headless-browser-navigate.test.ts +116 -79
  196. package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
  197. package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
  198. package/src/__tests__/heartbeat-service.test.ts +76 -58
  199. package/src/__tests__/history-repair-observability.test.ts +14 -14
  200. package/src/__tests__/history-repair.test.ts +171 -167
  201. package/src/__tests__/home-base-bootstrap.test.ts +30 -27
  202. package/src/__tests__/hooks-blocking.test.ts +86 -37
  203. package/src/__tests__/hooks-cli.test.ts +104 -68
  204. package/src/__tests__/hooks-config.test.ts +81 -43
  205. package/src/__tests__/hooks-discovery.test.ts +106 -96
  206. package/src/__tests__/hooks-integration.test.ts +78 -72
  207. package/src/__tests__/hooks-manager.test.ts +99 -61
  208. package/src/__tests__/hooks-runner.test.ts +94 -71
  209. package/src/__tests__/hooks-settings.test.ts +69 -64
  210. package/src/__tests__/hooks-templates.test.ts +85 -54
  211. package/src/__tests__/hooks-ts-runner.test.ts +82 -45
  212. package/src/__tests__/hooks-watch.test.ts +32 -22
  213. package/src/__tests__/host-file-edit-tool.test.ts +190 -148
  214. package/src/__tests__/host-file-read-tool.test.ts +86 -63
  215. package/src/__tests__/host-file-write-tool.test.ts +98 -64
  216. package/src/__tests__/host-shell-tool.test.ts +342 -233
  217. package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
  218. package/src/__tests__/ingress-member-store.test.ts +163 -159
  219. package/src/__tests__/ingress-reconcile.test.ts +13 -6
  220. package/src/__tests__/ingress-routes-http.test.ts +441 -356
  221. package/src/__tests__/ingress-url-consistency.test.ts +125 -64
  222. package/src/__tests__/integration-status.test.ts +93 -73
  223. package/src/__tests__/intent-routing.test.ts +148 -118
  224. package/src/__tests__/invite-redemption-service.test.ts +163 -121
  225. package/src/__tests__/ipc-blob-store.test.ts +104 -91
  226. package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
  227. package/src/__tests__/ipc-contract.test.ts +24 -23
  228. package/src/__tests__/ipc-protocol.test.ts +52 -46
  229. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
  230. package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
  231. package/src/__tests__/ipc-validate.test.ts +240 -179
  232. package/src/__tests__/key-migration.test.ts +123 -90
  233. package/src/__tests__/keychain.test.ts +150 -123
  234. package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
  235. package/src/__tests__/llm-usage-store.test.ts +112 -87
  236. package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
  237. package/src/__tests__/managed-store.test.ts +411 -360
  238. package/src/__tests__/mcp-cli.test.ts +190 -124
  239. package/src/__tests__/mcp-health-check.test.ts +26 -21
  240. package/src/__tests__/media-generate-image.test.ts +122 -99
  241. package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
  242. package/src/__tests__/media-visibility-policy.test.ts +86 -38
  243. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
  244. package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
  245. package/src/__tests__/memory-query-builder.test.ts +32 -33
  246. package/src/__tests__/memory-recall-quality.test.ts +761 -407
  247. package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
  248. package/src/__tests__/memory-regressions.test.ts +3725 -2642
  249. package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
  250. package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
  251. package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
  252. package/src/__tests__/messaging-send-tool.test.ts +36 -29
  253. package/src/__tests__/migration-cli-flows.test.ts +69 -53
  254. package/src/__tests__/migration-ordering.test.ts +103 -86
  255. package/src/__tests__/mime-builder.test.ts +55 -32
  256. package/src/__tests__/mock-signup-server.test.ts +384 -246
  257. package/src/__tests__/model-intents.test.ts +61 -37
  258. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
  259. package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
  260. package/src/__tests__/non-member-access-request.test.ts +3 -2
  261. package/src/__tests__/notification-broadcaster.test.ts +99 -81
  262. package/src/__tests__/notification-decision-fallback.test.ts +223 -178
  263. package/src/__tests__/notification-decision-strategy.test.ts +375 -337
  264. package/src/__tests__/notification-deep-link.test.ts +67 -61
  265. package/src/__tests__/notification-guardian-path.test.ts +248 -206
  266. package/src/__tests__/notification-routing-intent.test.ts +166 -93
  267. package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
  268. package/src/__tests__/notification-thread-candidates.test.ts +64 -61
  269. package/src/__tests__/oauth-callback-registry.test.ts +40 -30
  270. package/src/__tests__/oauth-connect-handler.test.ts +109 -89
  271. package/src/__tests__/oauth-scope-policy.test.ts +63 -55
  272. package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
  273. package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
  274. package/src/__tests__/onboarding-template-contract.test.ts +93 -94
  275. package/src/__tests__/openai-provider.test.ts +366 -274
  276. package/src/__tests__/pairing-concurrent.test.ts +18 -12
  277. package/src/__tests__/pairing-routes.test.ts +45 -41
  278. package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
  279. package/src/__tests__/parser.test.ts +316 -226
  280. package/src/__tests__/path-classifier.test.ts +24 -25
  281. package/src/__tests__/path-policy.test.ts +187 -147
  282. package/src/__tests__/phone.test.ts +36 -36
  283. package/src/__tests__/platform-move-helper.test.ts +48 -40
  284. package/src/__tests__/platform-socket-path.test.ts +23 -24
  285. package/src/__tests__/platform-workspace-migration.test.ts +464 -414
  286. package/src/__tests__/platform.test.ts +61 -53
  287. package/src/__tests__/playbook-execution.test.ts +397 -265
  288. package/src/__tests__/playbook-tools.test.ts +267 -196
  289. package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
  290. package/src/__tests__/pricing.test.ts +316 -136
  291. package/src/__tests__/profile-compiler.test.ts +206 -188
  292. package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
  293. package/src/__tests__/provider-error-scenarios.test.ts +212 -158
  294. package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
  295. package/src/__tests__/provider-registry-ollama.test.ts +13 -9
  296. package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
  297. package/src/__tests__/proxy-approval-callback.test.ts +180 -119
  298. package/src/__tests__/public-ingress-urls.test.ts +112 -94
  299. package/src/__tests__/qdrant-manager.test.ts +147 -98
  300. package/src/__tests__/ratelimit.test.ts +152 -82
  301. package/src/__tests__/recording-handler.test.ts +273 -151
  302. package/src/__tests__/recording-intent-fallback.test.ts +94 -75
  303. package/src/__tests__/recording-intent-handler.test.ts +9 -2
  304. package/src/__tests__/recording-intent.test.ts +578 -379
  305. package/src/__tests__/recording-state-machine.test.ts +530 -316
  306. package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
  307. package/src/__tests__/recurrence-engine.test.ts +81 -41
  308. package/src/__tests__/recurrence-types.test.ts +63 -44
  309. package/src/__tests__/relay-server.test.ts +2131 -1602
  310. package/src/__tests__/reminder-store.test.ts +158 -80
  311. package/src/__tests__/reminder.test.ts +113 -109
  312. package/src/__tests__/remote-skill-policy.test.ts +96 -72
  313. package/src/__tests__/request-file-tool.test.ts +74 -67
  314. package/src/__tests__/response-tier.test.ts +131 -74
  315. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  316. package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
  317. package/src/__tests__/runtime-events-sse.test.ts +0 -1
  318. package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
  319. package/src/__tests__/sandbox-host-parity.test.ts +377 -301
  320. package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
  321. package/src/__tests__/schedule-store.test.ts +268 -205
  322. package/src/__tests__/schedule-tools.test.ts +702 -524
  323. package/src/__tests__/scheduler-recurrence.test.ts +240 -130
  324. package/src/__tests__/scoped-approval-grants.test.ts +258 -168
  325. package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
  326. package/src/__tests__/script-proxy-certs.test.ts +38 -35
  327. package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
  328. package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
  329. package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
  330. package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
  331. package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
  332. package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
  333. package/src/__tests__/script-proxy-policy.test.ts +308 -153
  334. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
  335. package/src/__tests__/script-proxy-router.test.ts +111 -77
  336. package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
  337. package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
  338. package/src/__tests__/secret-allowlist.test.ts +105 -90
  339. package/src/__tests__/secret-ingress-handler.test.ts +41 -30
  340. package/src/__tests__/secret-onetime-send.test.ts +67 -50
  341. package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
  342. package/src/__tests__/secret-response-routing.test.ts +50 -41
  343. package/src/__tests__/secret-scanner-executor.test.ts +152 -111
  344. package/src/__tests__/secret-scanner.test.ts +495 -413
  345. package/src/__tests__/secure-keys.test.ts +132 -121
  346. package/src/__tests__/send-endpoint-busy.test.ts +8 -3
  347. package/src/__tests__/send-notification-tool.test.ts +43 -42
  348. package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
  349. package/src/__tests__/sequence-store.test.ts +335 -167
  350. package/src/__tests__/server-history-render.test.ts +341 -202
  351. package/src/__tests__/session-abort-tool-results.test.ts +133 -70
  352. package/src/__tests__/session-confirmation-signals.test.ts +252 -160
  353. package/src/__tests__/session-conflict-gate.test.ts +775 -585
  354. package/src/__tests__/session-error.test.ts +222 -191
  355. package/src/__tests__/session-evictor.test.ts +79 -62
  356. package/src/__tests__/session-init.benchmark.test.ts +170 -108
  357. package/src/__tests__/session-load-history-repair.test.ts +273 -139
  358. package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
  359. package/src/__tests__/session-pre-run-repair.test.ts +106 -59
  360. package/src/__tests__/session-profile-injection.test.ts +198 -130
  361. package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
  362. package/src/__tests__/session-queue.test.ts +624 -321
  363. package/src/__tests__/session-runtime-assembly.test.ts +425 -329
  364. package/src/__tests__/session-runtime-workspace.test.ts +69 -61
  365. package/src/__tests__/session-skill-tools.test.ts +973 -678
  366. package/src/__tests__/session-slash-known.test.ts +185 -133
  367. package/src/__tests__/session-slash-queue.test.ts +147 -81
  368. package/src/__tests__/session-slash-unknown.test.ts +135 -90
  369. package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
  370. package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
  371. package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
  372. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
  373. package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
  374. package/src/__tests__/session-undo.test.ts +43 -30
  375. package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
  376. package/src/__tests__/session-workspace-injection.test.ts +245 -117
  377. package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
  378. package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
  379. package/src/__tests__/shell-credential-ref.test.ts +126 -90
  380. package/src/__tests__/shell-identity.test.ts +134 -111
  381. package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
  382. package/src/__tests__/shell-parser-property.test.ts +435 -288
  383. package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
  384. package/src/__tests__/size-guard.test.ts +42 -44
  385. package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
  386. package/src/__tests__/skill-feature-flags.test.ts +75 -47
  387. package/src/__tests__/skill-include-graph.test.ts +143 -148
  388. package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
  389. package/src/__tests__/skill-load-tool.test.ts +371 -199
  390. package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
  391. package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
  392. package/src/__tests__/skill-script-runner-host.test.ts +460 -250
  393. package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
  394. package/src/__tests__/skill-script-runner.test.ts +115 -74
  395. package/src/__tests__/skill-tool-factory.test.ts +140 -96
  396. package/src/__tests__/skill-tool-manifest.test.ts +306 -210
  397. package/src/__tests__/skill-version-hash.test.ts +70 -56
  398. package/src/__tests__/skills.test.ts +0 -1
  399. package/src/__tests__/slack-channel-config.test.ts +127 -84
  400. package/src/__tests__/slack-skill.test.ts +60 -47
  401. package/src/__tests__/slash-commands-catalog.test.ts +37 -31
  402. package/src/__tests__/slash-commands-parser.test.ts +71 -64
  403. package/src/__tests__/slash-commands-resolver.test.ts +143 -107
  404. package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
  405. package/src/__tests__/sms-messaging-provider.test.ts +4 -0
  406. package/src/__tests__/speaker-identification.test.ts +28 -25
  407. package/src/__tests__/starter-bundle.test.ts +27 -23
  408. package/src/__tests__/starter-task-flow.test.ts +67 -52
  409. package/src/__tests__/subagent-manager-notify.test.ts +154 -108
  410. package/src/__tests__/subagent-tools.test.ts +311 -270
  411. package/src/__tests__/subagent-types.test.ts +40 -40
  412. package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
  413. package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
  414. package/src/__tests__/swarm-orchestrator.test.ts +135 -101
  415. package/src/__tests__/swarm-plan-validator.test.ts +125 -73
  416. package/src/__tests__/swarm-recursion.test.ts +58 -46
  417. package/src/__tests__/swarm-router-planner.test.ts +99 -74
  418. package/src/__tests__/swarm-session-integration.test.ts +148 -91
  419. package/src/__tests__/swarm-tool.test.ts +65 -45
  420. package/src/__tests__/swarm-worker-backend.test.ts +59 -45
  421. package/src/__tests__/swarm-worker-runner.test.ts +133 -118
  422. package/src/__tests__/system-prompt.test.ts +311 -256
  423. package/src/__tests__/task-compiler.test.ts +176 -120
  424. package/src/__tests__/task-management-tools.test.ts +561 -456
  425. package/src/__tests__/task-memory-cleanup.test.ts +627 -362
  426. package/src/__tests__/task-runner.test.ts +117 -94
  427. package/src/__tests__/task-scheduler.test.ts +113 -84
  428. package/src/__tests__/task-tools.test.ts +349 -264
  429. package/src/__tests__/terminal-sandbox.test.ts +138 -108
  430. package/src/__tests__/terminal-tools.test.ts +350 -305
  431. package/src/__tests__/thread-seed-composer.test.ts +307 -180
  432. package/src/__tests__/tool-approval-handler.test.ts +238 -137
  433. package/src/__tests__/tool-audit-listener.test.ts +69 -69
  434. package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
  435. package/src/__tests__/tool-execution-abort-cleanup.test.ts +155 -146
  436. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
  437. package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
  438. package/src/__tests__/tool-executor-redaction.test.ts +112 -109
  439. package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
  440. package/src/__tests__/tool-executor.test.ts +1274 -674
  441. package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
  442. package/src/__tests__/tool-metrics-listener.test.ts +97 -85
  443. package/src/__tests__/tool-notification-listener.test.ts +42 -25
  444. package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
  445. package/src/__tests__/tool-policy.test.ts +44 -25
  446. package/src/__tests__/tool-profiling-listener.test.ts +99 -93
  447. package/src/__tests__/tool-result-truncation.test.ts +5 -4
  448. package/src/__tests__/tool-trace-listener.test.ts +131 -111
  449. package/src/__tests__/top-level-renderer.test.ts +62 -58
  450. package/src/__tests__/top-level-scanner.test.ts +68 -64
  451. package/src/__tests__/trace-emitter.test.ts +56 -56
  452. package/src/__tests__/trust-context-guards.test.ts +65 -65
  453. package/src/__tests__/trust-store.test.ts +1239 -806
  454. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  455. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  456. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  457. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  458. package/src/__tests__/trusted-contact-verification.test.ts +251 -231
  459. package/src/__tests__/turn-commit.test.ts +259 -200
  460. package/src/__tests__/twilio-provider.test.ts +140 -126
  461. package/src/__tests__/twilio-rest.test.ts +22 -18
  462. package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
  463. package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
  464. package/src/__tests__/twilio-routes.test.ts +0 -1
  465. package/src/__tests__/twitter-auth-handler.test.ts +184 -139
  466. package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
  467. package/src/__tests__/twitter-cli-routing.test.ts +146 -99
  468. package/src/__tests__/twitter-oauth-client.test.ts +82 -65
  469. package/src/__tests__/update-bulletin-format.test.ts +69 -66
  470. package/src/__tests__/update-bulletin-state.test.ts +66 -60
  471. package/src/__tests__/update-bulletin.test.ts +150 -114
  472. package/src/__tests__/update-template-contract.test.ts +15 -10
  473. package/src/__tests__/url-safety.test.ts +288 -265
  474. package/src/__tests__/user-reference.test.ts +32 -32
  475. package/src/__tests__/view-image-tool.test.ts +118 -96
  476. package/src/__tests__/voice-invite-redemption.test.ts +111 -106
  477. package/src/__tests__/voice-quality.test.ts +117 -102
  478. package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
  479. package/src/__tests__/voice-session-bridge.test.ts +351 -216
  480. package/src/__tests__/weather-skill-regression.test.ts +170 -120
  481. package/src/__tests__/web-fetch.test.ts +664 -526
  482. package/src/__tests__/web-search.test.ts +379 -213
  483. package/src/__tests__/work-item-output.test.ts +90 -53
  484. package/src/__tests__/workspace-git-service.test.ts +437 -356
  485. package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
  486. package/src/__tests__/workspace-lifecycle.test.ts +98 -64
  487. package/src/__tests__/workspace-policy.test.ts +139 -71
  488. package/src/cli/mcp.ts +81 -28
  489. package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
  490. package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
  491. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
  492. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
  493. package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +6 -11
  494. package/src/config/bundled-skills/messaging/SKILL.md +4 -3
  495. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
  496. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
  497. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -2
  498. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
  499. package/src/config/bundled-skills/sms-setup/SKILL.md +8 -16
  500. package/src/config/bundled-skills/telegram-setup/SKILL.md +3 -3
  501. package/src/config/bundled-skills/trusted-contacts/SKILL.md +13 -25
  502. package/src/config/bundled-skills/twilio-setup/SKILL.md +13 -23
  503. package/src/config/bundled-tool-registry.ts +2 -0
  504. package/src/config/env.ts +3 -4
  505. package/src/config/system-prompt.ts +32 -0
  506. package/src/mcp/client.ts +2 -7
  507. package/src/memory/db-connection.ts +16 -10
  508. package/src/messaging/providers/gmail/adapter.ts +10 -3
  509. package/src/messaging/providers/gmail/client.ts +280 -72
  510. package/src/runtime/auth/__tests__/context.test.ts +75 -65
  511. package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
  512. package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
  513. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
  514. package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
  515. package/src/runtime/auth/__tests__/policy.test.ts +9 -9
  516. package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
  517. package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
  518. package/src/runtime/auth/__tests__/subject.test.ts +54 -54
  519. package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
  520. package/src/runtime/auth/scopes.ts +3 -0
  521. package/src/runtime/auth/token-service.ts +4 -1
  522. package/src/runtime/auth/types.ts +2 -1
  523. package/src/runtime/http-server.ts +2 -1
  524. package/src/security/secure-keys.ts +120 -54
  525. package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
  526. package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
  527. package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
  528. package/src/tools/terminal/safe-env.ts +7 -0
@@ -5,31 +5,31 @@
5
5
  * decision-model call is unavailable.
6
6
  */
7
7
 
8
- import { beforeEach, describe, expect, mock, test } from 'bun:test';
8
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
9
9
 
10
- mock.module('../channels/config.js', () => ({
11
- getDeliverableChannels: () => ['vellum', 'telegram', 'sms'],
10
+ mock.module("../channels/config.js", () => ({
11
+ getDeliverableChannels: () => ["vellum", "telegram", "sms"],
12
12
  }));
13
13
 
14
- mock.module('../config/loader.js', () => ({
14
+ mock.module("../config/loader.js", () => ({
15
15
  getConfig: () => ({
16
16
  ui: {},
17
-
17
+
18
18
  notifications: {
19
- decisionModelIntent: 'latency-optimized',
19
+ decisionModelIntent: "latency-optimized",
20
20
  },
21
21
  }),
22
22
  }));
23
23
 
24
- mock.module('../notifications/decisions-store.js', () => ({
24
+ mock.module("../notifications/decisions-store.js", () => ({
25
25
  createDecision: () => {},
26
26
  }));
27
27
 
28
- mock.module('../notifications/preference-summary.js', () => ({
28
+ mock.module("../notifications/preference-summary.js", () => ({
29
29
  getPreferenceSummary: () => undefined,
30
30
  }));
31
31
 
32
- mock.module('../notifications/thread-candidates.js', () => ({
32
+ mock.module("../notifications/thread-candidates.js", () => ({
33
33
  buildThreadCandidates: () => undefined,
34
34
  serializeCandidatesForPrompt: () => undefined,
35
35
  }));
@@ -37,41 +37,43 @@ mock.module('../notifications/thread-candidates.js', () => ({
37
37
  let configuredProvider: { sendMessage: () => Promise<unknown> } | null = null;
38
38
  let extractedToolUse: unknown = null;
39
39
 
40
- mock.module('../providers/provider-send-message.js', () => ({
40
+ mock.module("../providers/provider-send-message.js", () => ({
41
41
  getConfiguredProvider: () => configuredProvider,
42
42
  createTimeout: () => ({
43
43
  signal: new AbortController().signal,
44
44
  cleanup: () => {},
45
45
  }),
46
46
  extractToolUse: () => extractedToolUse,
47
- userMessage: (text: string) => ({ role: 'user', content: text }),
47
+ userMessage: (text: string) => ({ role: "user", content: text }),
48
48
  }));
49
49
 
50
- mock.module('../util/logger.js', () => ({
50
+ mock.module("../util/logger.js", () => ({
51
51
  getLogger: () =>
52
52
  new Proxy({} as Record<string, unknown>, {
53
53
  get: () => () => {},
54
54
  }),
55
55
  }));
56
56
 
57
- import { evaluateSignal } from '../notifications/decision-engine.js';
58
- import type { NotificationSignal } from '../notifications/signal.js';
59
- import type { NotificationChannel } from '../notifications/types.js';
57
+ import { evaluateSignal } from "../notifications/decision-engine.js";
58
+ import type { NotificationSignal } from "../notifications/signal.js";
59
+ import type { NotificationChannel } from "../notifications/types.js";
60
60
 
61
- function makeSignal(overrides?: Partial<NotificationSignal>): NotificationSignal {
61
+ function makeSignal(
62
+ overrides?: Partial<NotificationSignal>,
63
+ ): NotificationSignal {
62
64
  return {
63
- signalId: 'sig-fallback-guardian-1',
64
- assistantId: 'self',
65
+ signalId: "sig-fallback-guardian-1",
66
+ assistantId: "self",
65
67
  createdAt: Date.now(),
66
- sourceChannel: 'voice',
67
- sourceSessionId: 'call-session-1',
68
- sourceEventName: 'guardian.question',
68
+ sourceChannel: "voice",
69
+ sourceSessionId: "call-session-1",
70
+ sourceEventName: "guardian.question",
69
71
  contextPayload: {
70
- questionText: 'What is the gate code?',
72
+ questionText: "What is the gate code?",
71
73
  },
72
74
  attentionHints: {
73
75
  requiresAction: true,
74
- urgency: 'high',
76
+ urgency: "high",
75
77
  isAsyncBackground: false,
76
78
  visibleInSourceNow: false,
77
79
  },
@@ -79,223 +81,245 @@ function makeSignal(overrides?: Partial<NotificationSignal>): NotificationSignal
79
81
  };
80
82
  }
81
83
 
82
- describe('notification decision fallback copy', () => {
84
+ describe("notification decision fallback copy", () => {
83
85
  beforeEach(() => {
84
86
  configuredProvider = null;
85
87
  extractedToolUse = null;
86
88
  });
87
89
 
88
- test('uses human-friendly template copy for guardian.question', async () => {
90
+ test("uses human-friendly template copy for guardian.question", async () => {
89
91
  const signal = makeSignal();
90
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
92
+ const decision = await evaluateSignal(signal, [
93
+ "vellum",
94
+ ] as NotificationChannel[]);
91
95
 
92
96
  expect(decision.fallbackUsed).toBe(true);
93
- expect(decision.renderedCopy.vellum?.title).toBe('Guardian Question');
94
- expect(decision.renderedCopy.vellum?.body).toBe('What is the gate code?');
95
- expect(decision.renderedCopy.vellum?.title).not.toBe('guardian.question');
96
- expect(decision.renderedCopy.vellum?.body).not.toContain('Action required: guardian.question');
97
+ expect(decision.renderedCopy.vellum?.title).toBe("Guardian Question");
98
+ expect(decision.renderedCopy.vellum?.body).toBe("What is the gate code?");
99
+ expect(decision.renderedCopy.vellum?.title).not.toBe("guardian.question");
100
+ expect(decision.renderedCopy.vellum?.body).not.toContain(
101
+ "Action required: guardian.question",
102
+ );
97
103
  });
98
104
 
99
- test('enforces free-text answer instructions for guardian.question when requestCode exists', async () => {
105
+ test("enforces free-text answer instructions for guardian.question when requestCode exists", async () => {
100
106
  const signal = makeSignal({
101
107
  contextPayload: {
102
- requestId: 'req-pending-1',
103
- questionText: 'What is the gate code?',
104
- requestCode: 'A1B2C3',
105
- requestKind: 'pending_question',
106
- callSessionId: 'call-1',
108
+ requestId: "req-pending-1",
109
+ questionText: "What is the gate code?",
110
+ requestCode: "A1B2C3",
111
+ requestKind: "pending_question",
112
+ callSessionId: "call-1",
107
113
  activeGuardianRequestCount: 1,
108
114
  },
109
115
  });
110
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
116
+ const decision = await evaluateSignal(signal, [
117
+ "vellum",
118
+ ] as NotificationChannel[]);
111
119
 
112
120
  expect(decision.fallbackUsed).toBe(true);
113
- expect(decision.renderedCopy.vellum?.body).toContain('A1B2C3');
114
- expect(decision.renderedCopy.vellum?.body).toContain('<your answer>');
115
- expect(decision.renderedCopy.vellum?.body).not.toContain('approve');
116
- expect(decision.renderedCopy.vellum?.body).not.toContain('reject');
121
+ expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3");
122
+ expect(decision.renderedCopy.vellum?.body).toContain("<your answer>");
123
+ expect(decision.renderedCopy.vellum?.body).not.toContain("approve");
124
+ expect(decision.renderedCopy.vellum?.body).not.toContain("reject");
117
125
  });
118
126
 
119
- test('enforcement appends free-text answer instructions when LLM copy only mentions request code', async () => {
127
+ test("enforcement appends free-text answer instructions when LLM copy only mentions request code", async () => {
120
128
  configuredProvider = {
121
129
  sendMessage: async () => ({ content: [] }),
122
130
  };
123
131
  extractedToolUse = {
124
- name: 'record_notification_decision',
132
+ name: "record_notification_decision",
125
133
  input: {
126
134
  shouldNotify: true,
127
- selectedChannels: ['vellum'],
128
- reasoningSummary: 'LLM decision',
135
+ selectedChannels: ["vellum"],
136
+ reasoningSummary: "LLM decision",
129
137
  renderedCopy: {
130
138
  vellum: {
131
- title: 'Guardian Question',
132
- body: 'Use reference code A1B2C3 for this request.',
139
+ title: "Guardian Question",
140
+ body: "Use reference code A1B2C3 for this request.",
133
141
  },
134
142
  },
135
- dedupeKey: 'guardian-question-test',
143
+ dedupeKey: "guardian-question-test",
136
144
  confidence: 0.9,
137
145
  },
138
146
  };
139
147
 
140
148
  const signal = makeSignal({
141
149
  contextPayload: {
142
- requestId: 'req-pending-1',
143
- questionText: 'What is the gate code?',
144
- requestCode: 'A1B2C3',
145
- requestKind: 'pending_question',
146
- callSessionId: 'call-1',
150
+ requestId: "req-pending-1",
151
+ questionText: "What is the gate code?",
152
+ requestCode: "A1B2C3",
153
+ requestKind: "pending_question",
154
+ callSessionId: "call-1",
147
155
  activeGuardianRequestCount: 1,
148
156
  },
149
157
  });
150
158
 
151
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
159
+ const decision = await evaluateSignal(signal, [
160
+ "vellum",
161
+ ] as NotificationChannel[]);
152
162
 
153
163
  expect(decision.fallbackUsed).toBe(false);
154
- expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 <your answer>"');
155
- expect(decision.renderedCopy.vellum?.body).not.toContain('"A1B2C3 approve"');
164
+ expect(decision.renderedCopy.vellum?.body).toContain(
165
+ '"A1B2C3 <your answer>"',
166
+ );
167
+ expect(decision.renderedCopy.vellum?.body).not.toContain(
168
+ '"A1B2C3 approve"',
169
+ );
156
170
  expect(decision.renderedCopy.vellum?.body).not.toContain('"A1B2C3 reject"');
157
171
  });
158
172
 
159
- test('enforcement appends answer instructions when LLM copy incorrectly uses approve/reject wording', async () => {
173
+ test("enforcement appends answer instructions when LLM copy incorrectly uses approve/reject wording", async () => {
160
174
  configuredProvider = {
161
175
  sendMessage: async () => ({ content: [] }),
162
176
  };
163
177
  extractedToolUse = {
164
- name: 'record_notification_decision',
178
+ name: "record_notification_decision",
165
179
  input: {
166
180
  shouldNotify: true,
167
- selectedChannels: ['vellum'],
168
- reasoningSummary: 'LLM decision',
181
+ selectedChannels: ["vellum"],
182
+ reasoningSummary: "LLM decision",
169
183
  renderedCopy: {
170
184
  vellum: {
171
- title: 'Guardian Question',
185
+ title: "Guardian Question",
172
186
  body: 'Reference code: A1B2C3. Reply "A1B2C3 approve" or "A1B2C3 reject".',
173
187
  },
174
188
  },
175
- dedupeKey: 'guardian-question-wrong-instructions-test',
189
+ dedupeKey: "guardian-question-wrong-instructions-test",
176
190
  confidence: 0.9,
177
191
  },
178
192
  };
179
193
 
180
194
  const signal = makeSignal({
181
195
  contextPayload: {
182
- requestId: 'req-pending-approve-phrasing',
183
- questionText: 'What is the gate code?',
184
- requestCode: 'A1B2C3',
185
- requestKind: 'pending_question',
186
- callSessionId: 'call-1',
196
+ requestId: "req-pending-approve-phrasing",
197
+ questionText: "What is the gate code?",
198
+ requestCode: "A1B2C3",
199
+ requestKind: "pending_question",
200
+ callSessionId: "call-1",
187
201
  activeGuardianRequestCount: 1,
188
202
  },
189
203
  });
190
204
 
191
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
205
+ const decision = await evaluateSignal(signal, [
206
+ "vellum",
207
+ ] as NotificationChannel[]);
192
208
 
193
209
  expect(decision.fallbackUsed).toBe(false);
194
- expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 <your answer>"');
210
+ expect(decision.renderedCopy.vellum?.body).toContain(
211
+ '"A1B2C3 <your answer>"',
212
+ );
195
213
  });
196
214
 
197
- test('enforcement appends explicit approve/reject instructions for tool-approval guardian questions', async () => {
215
+ test("enforcement appends explicit approve/reject instructions for tool-approval guardian questions", async () => {
198
216
  configuredProvider = {
199
217
  sendMessage: async () => ({ content: [] }),
200
218
  };
201
219
  extractedToolUse = {
202
- name: 'record_notification_decision',
220
+ name: "record_notification_decision",
203
221
  input: {
204
222
  shouldNotify: true,
205
- selectedChannels: ['vellum'],
206
- reasoningSummary: 'LLM decision',
223
+ selectedChannels: ["vellum"],
224
+ reasoningSummary: "LLM decision",
207
225
  renderedCopy: {
208
226
  vellum: {
209
- title: 'Guardian Question',
210
- body: 'Use reference code A1B2C3 for this request.',
227
+ title: "Guardian Question",
228
+ body: "Use reference code A1B2C3 for this request.",
211
229
  },
212
230
  },
213
- dedupeKey: 'guardian-question-tool-approval-test',
231
+ dedupeKey: "guardian-question-tool-approval-test",
214
232
  confidence: 0.9,
215
233
  },
216
234
  };
217
235
 
218
236
  const signal = makeSignal({
219
237
  contextPayload: {
220
- requestId: 'req-grant-1',
221
- questionText: 'Allow running host_bash?',
222
- requestCode: 'A1B2C3',
223
- requestKind: 'tool_grant_request',
224
- toolName: 'host_bash',
238
+ requestId: "req-grant-1",
239
+ questionText: "Allow running host_bash?",
240
+ requestCode: "A1B2C3",
241
+ requestKind: "tool_grant_request",
242
+ toolName: "host_bash",
225
243
  },
226
244
  });
227
245
 
228
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
246
+ const decision = await evaluateSignal(signal, [
247
+ "vellum",
248
+ ] as NotificationChannel[]);
229
249
 
230
250
  expect(decision.fallbackUsed).toBe(false);
231
251
  expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 approve"');
232
252
  expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 reject"');
233
253
  });
234
254
 
235
- test('approval-mode enforcement removes conflicting answer-mode phrasing', async () => {
255
+ test("approval-mode enforcement removes conflicting answer-mode phrasing", async () => {
236
256
  configuredProvider = {
237
257
  sendMessage: async () => ({ content: [] }),
238
258
  };
239
259
  extractedToolUse = {
240
- name: 'record_notification_decision',
260
+ name: "record_notification_decision",
241
261
  input: {
242
262
  shouldNotify: true,
243
- selectedChannels: ['vellum'],
244
- reasoningSummary: 'LLM decision',
263
+ selectedChannels: ["vellum"],
264
+ reasoningSummary: "LLM decision",
245
265
  renderedCopy: {
246
266
  vellum: {
247
- title: 'Guardian Question',
267
+ title: "Guardian Question",
248
268
  body: 'Reference code: A1B2C3. Reply "A1B2C3 <your answer>".',
249
269
  },
250
270
  },
251
- dedupeKey: 'guardian-question-approval-removes-answer-test',
271
+ dedupeKey: "guardian-question-approval-removes-answer-test",
252
272
  confidence: 0.9,
253
273
  },
254
274
  };
255
275
 
256
276
  const signal = makeSignal({
257
277
  contextPayload: {
258
- requestId: 'req-grant-2',
259
- questionText: 'Allow running host_bash?',
260
- requestCode: 'A1B2C3',
261
- requestKind: 'tool_grant_request',
262
- toolName: 'host_bash',
278
+ requestId: "req-grant-2",
279
+ questionText: "Allow running host_bash?",
280
+ requestCode: "A1B2C3",
281
+ requestKind: "tool_grant_request",
282
+ toolName: "host_bash",
263
283
  },
264
284
  });
265
285
 
266
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
286
+ const decision = await evaluateSignal(signal, [
287
+ "vellum",
288
+ ] as NotificationChannel[]);
267
289
 
268
290
  expect(decision.fallbackUsed).toBe(false);
269
291
  expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 approve"');
270
292
  expect(decision.renderedCopy.vellum?.body).toContain('"A1B2C3 reject"');
271
- expect(decision.renderedCopy.vellum?.body).not.toContain('<your answer>');
293
+ expect(decision.renderedCopy.vellum?.body).not.toContain("<your answer>");
272
294
  });
273
295
  });
274
296
 
275
297
  // ── Access-request instruction enforcement ──────────────────────────────
276
298
 
277
- describe('access-request instruction enforcement', () => {
299
+ describe("access-request instruction enforcement", () => {
278
300
  beforeEach(() => {
279
301
  configuredProvider = null;
280
302
  extractedToolUse = null;
281
303
  });
282
304
 
283
- function makeAccessRequestSignal(overrides?: Partial<NotificationSignal>): NotificationSignal {
305
+ function makeAccessRequestSignal(
306
+ overrides?: Partial<NotificationSignal>,
307
+ ): NotificationSignal {
284
308
  return {
285
- signalId: 'sig-access-req-1',
286
- assistantId: 'self',
309
+ signalId: "sig-access-req-1",
310
+ assistantId: "self",
287
311
  createdAt: Date.now(),
288
- sourceChannel: 'telegram',
289
- sourceSessionId: 'tg-session-1',
290
- sourceEventName: 'ingress.access_request',
312
+ sourceChannel: "telegram",
313
+ sourceSessionId: "tg-session-1",
314
+ sourceEventName: "ingress.access_request",
291
315
  contextPayload: {
292
- senderIdentifier: 'Alice',
293
- requestCode: 'A1B2C3',
294
- sourceChannel: 'telegram',
316
+ senderIdentifier: "Alice",
317
+ requestCode: "A1B2C3",
318
+ sourceChannel: "telegram",
295
319
  },
296
320
  attentionHints: {
297
321
  requiresAction: true,
298
- urgency: 'high',
322
+ urgency: "high",
299
323
  isAsyncBackground: false,
300
324
  visibleInSourceNow: false,
301
325
  },
@@ -303,203 +327,224 @@ describe('access-request instruction enforcement', () => {
303
327
  };
304
328
  }
305
329
 
306
- test('fallback copy includes access-request contract elements', async () => {
330
+ test("fallback copy includes access-request contract elements", async () => {
307
331
  const signal = makeAccessRequestSignal();
308
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
332
+ const decision = await evaluateSignal(signal, [
333
+ "vellum",
334
+ ] as NotificationChannel[]);
309
335
 
310
336
  expect(decision.fallbackUsed).toBe(true);
311
- expect(decision.renderedCopy.vellum?.body).toContain('A1B2C3');
312
- expect(decision.renderedCopy.vellum?.body).toContain('approve');
313
- expect(decision.renderedCopy.vellum?.body).toContain('reject');
314
- expect(decision.renderedCopy.vellum?.body).toContain('open invite flow');
337
+ expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3");
338
+ expect(decision.renderedCopy.vellum?.body).toContain("approve");
339
+ expect(decision.renderedCopy.vellum?.body).toContain("reject");
340
+ expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
315
341
  });
316
342
 
317
- test('enforcement appends contract when LLM copy is missing request code', async () => {
343
+ test("enforcement appends contract when LLM copy is missing request code", async () => {
318
344
  configuredProvider = {
319
345
  sendMessage: async () => ({ content: [] }),
320
346
  };
321
347
  extractedToolUse = {
322
- name: 'record_notification_decision',
348
+ name: "record_notification_decision",
323
349
  input: {
324
350
  shouldNotify: true,
325
- selectedChannels: ['vellum'],
326
- reasoningSummary: 'LLM decision',
351
+ selectedChannels: ["vellum"],
352
+ reasoningSummary: "LLM decision",
327
353
  renderedCopy: {
328
354
  vellum: {
329
- title: 'Access Request',
330
- body: 'Someone wants access to your assistant.',
355
+ title: "Access Request",
356
+ body: "Someone wants access to your assistant.",
331
357
  },
332
358
  },
333
- dedupeKey: 'access-req-missing-code',
359
+ dedupeKey: "access-req-missing-code",
334
360
  confidence: 0.9,
335
361
  },
336
362
  };
337
363
 
338
364
  const signal = makeAccessRequestSignal();
339
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
365
+ const decision = await evaluateSignal(signal, [
366
+ "vellum",
367
+ ] as NotificationChannel[]);
340
368
 
341
369
  expect(decision.fallbackUsed).toBe(false);
342
- expect(decision.renderedCopy.vellum?.body).toContain('A1B2C3');
343
- expect(decision.renderedCopy.vellum?.body).toContain('approve');
344
- expect(decision.renderedCopy.vellum?.body).toContain('reject');
345
- expect(decision.renderedCopy.vellum?.body).toContain('open invite flow');
370
+ expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3");
371
+ expect(decision.renderedCopy.vellum?.body).toContain("approve");
372
+ expect(decision.renderedCopy.vellum?.body).toContain("reject");
373
+ expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
346
374
  });
347
375
 
348
- test('enforcement appends contract when LLM copy has code but missing invite flow', async () => {
376
+ test("enforcement appends contract when LLM copy has code but missing invite flow", async () => {
349
377
  configuredProvider = {
350
378
  sendMessage: async () => ({ content: [] }),
351
379
  };
352
380
  extractedToolUse = {
353
- name: 'record_notification_decision',
381
+ name: "record_notification_decision",
354
382
  input: {
355
383
  shouldNotify: true,
356
- selectedChannels: ['vellum'],
357
- reasoningSummary: 'LLM decision',
384
+ selectedChannels: ["vellum"],
385
+ reasoningSummary: "LLM decision",
358
386
  renderedCopy: {
359
387
  vellum: {
360
- title: 'Access Request',
388
+ title: "Access Request",
361
389
  body: 'Alice wants access. Reply "A1B2C3 approve" or "A1B2C3 reject".',
362
390
  },
363
391
  },
364
- dedupeKey: 'access-req-missing-invite',
392
+ dedupeKey: "access-req-missing-invite",
365
393
  confidence: 0.9,
366
394
  },
367
395
  };
368
396
 
369
397
  const signal = makeAccessRequestSignal();
370
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
398
+ const decision = await evaluateSignal(signal, [
399
+ "vellum",
400
+ ] as NotificationChannel[]);
371
401
 
372
402
  expect(decision.fallbackUsed).toBe(false);
373
- expect(decision.renderedCopy.vellum?.body).toContain('open invite flow');
403
+ expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
374
404
  });
375
405
 
376
- test('enforcement does not duplicate when LLM copy already has all required elements', async () => {
377
- const fullBody = 'Alice wants access.\nReply "A1B2C3 approve" to grant access or "A1B2C3 reject" to deny.\nReply "open invite flow" to start Trusted Contacts invite flow.';
406
+ test("enforcement does not duplicate when LLM copy already has all required elements", async () => {
407
+ const fullBody =
408
+ 'Alice wants access.\nReply "A1B2C3 approve" to grant access or "A1B2C3 reject" to deny.\nReply "open invite flow" to start Trusted Contacts invite flow.';
378
409
  configuredProvider = {
379
410
  sendMessage: async () => ({ content: [] }),
380
411
  };
381
412
  extractedToolUse = {
382
- name: 'record_notification_decision',
413
+ name: "record_notification_decision",
383
414
  input: {
384
415
  shouldNotify: true,
385
- selectedChannels: ['vellum'],
386
- reasoningSummary: 'LLM decision',
416
+ selectedChannels: ["vellum"],
417
+ reasoningSummary: "LLM decision",
387
418
  renderedCopy: {
388
419
  vellum: {
389
- title: 'Access Request',
420
+ title: "Access Request",
390
421
  body: fullBody,
391
422
  },
392
423
  },
393
- dedupeKey: 'access-req-already-valid',
424
+ dedupeKey: "access-req-already-valid",
394
425
  confidence: 0.9,
395
426
  },
396
427
  };
397
428
 
398
429
  const signal = makeAccessRequestSignal();
399
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
430
+ const decision = await evaluateSignal(signal, [
431
+ "vellum",
432
+ ] as NotificationChannel[]);
400
433
 
401
434
  expect(decision.fallbackUsed).toBe(false);
402
435
  // Body should remain unchanged when all required elements are present
403
436
  expect(decision.renderedCopy.vellum?.body).toBe(fullBody);
404
437
  });
405
438
 
406
- test('enforcement also applies to deliveryText and threadSeedMessage', async () => {
439
+ test("enforcement also applies to deliveryText and threadSeedMessage", async () => {
407
440
  configuredProvider = {
408
441
  sendMessage: async () => ({ content: [] }),
409
442
  };
410
443
  extractedToolUse = {
411
- name: 'record_notification_decision',
444
+ name: "record_notification_decision",
412
445
  input: {
413
446
  shouldNotify: true,
414
- selectedChannels: ['telegram'],
415
- reasoningSummary: 'LLM decision',
447
+ selectedChannels: ["telegram"],
448
+ reasoningSummary: "LLM decision",
416
449
  renderedCopy: {
417
450
  telegram: {
418
- title: 'Access Request',
419
- body: 'Someone wants access.',
420
- deliveryText: 'Someone wants access.',
421
- threadSeedMessage: 'Someone wants access.',
451
+ title: "Access Request",
452
+ body: "Someone wants access.",
453
+ deliveryText: "Someone wants access.",
454
+ threadSeedMessage: "Someone wants access.",
422
455
  },
423
456
  },
424
- dedupeKey: 'access-req-multi-field',
457
+ dedupeKey: "access-req-multi-field",
425
458
  confidence: 0.9,
426
459
  },
427
460
  };
428
461
 
429
462
  const signal = makeAccessRequestSignal();
430
- const decision = await evaluateSignal(signal, ['telegram'] as NotificationChannel[]);
431
-
432
- expect(decision.renderedCopy.telegram?.deliveryText).toContain('A1B2C3');
433
- expect(decision.renderedCopy.telegram?.deliveryText).toContain('open invite flow');
434
- expect(decision.renderedCopy.telegram?.threadSeedMessage).toContain('A1B2C3');
435
- expect(decision.renderedCopy.telegram?.threadSeedMessage).toContain('open invite flow');
463
+ const decision = await evaluateSignal(signal, [
464
+ "telegram",
465
+ ] as NotificationChannel[]);
466
+
467
+ expect(decision.renderedCopy.telegram?.deliveryText).toContain("A1B2C3");
468
+ expect(decision.renderedCopy.telegram?.deliveryText).toContain(
469
+ "open invite flow",
470
+ );
471
+ expect(decision.renderedCopy.telegram?.threadSeedMessage).toContain(
472
+ "A1B2C3",
473
+ );
474
+ expect(decision.renderedCopy.telegram?.threadSeedMessage).toContain(
475
+ "open invite flow",
476
+ );
436
477
  });
437
478
 
438
- test('enforcement appends contract when LLM copy contains conflicting instructions', async () => {
479
+ test("enforcement appends contract when LLM copy contains conflicting instructions", async () => {
439
480
  configuredProvider = {
440
481
  sendMessage: async () => ({ content: [] }),
441
482
  };
442
483
  extractedToolUse = {
443
- name: 'record_notification_decision',
484
+ name: "record_notification_decision",
444
485
  input: {
445
486
  shouldNotify: true,
446
- selectedChannels: ['vellum'],
447
- reasoningSummary: 'LLM decision',
487
+ selectedChannels: ["vellum"],
488
+ reasoningSummary: "LLM decision",
448
489
  renderedCopy: {
449
490
  vellum: {
450
- title: 'Access Request',
491
+ title: "Access Request",
451
492
  body: 'Alice wants access. Just reply "yes" or "no" to decide.',
452
493
  },
453
494
  },
454
- dedupeKey: 'access-req-conflicting',
495
+ dedupeKey: "access-req-conflicting",
455
496
  confidence: 0.9,
456
497
  },
457
498
  };
458
499
 
459
500
  const signal = makeAccessRequestSignal();
460
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
501
+ const decision = await evaluateSignal(signal, [
502
+ "vellum",
503
+ ] as NotificationChannel[]);
461
504
 
462
505
  // Must contain the proper contract instructions despite conflicting LLM copy
463
- expect(decision.renderedCopy.vellum?.body).toContain('A1B2C3 approve');
464
- expect(decision.renderedCopy.vellum?.body).toContain('A1B2C3 reject');
465
- expect(decision.renderedCopy.vellum?.body).toContain('open invite flow');
506
+ expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3 approve");
507
+ expect(decision.renderedCopy.vellum?.body).toContain("A1B2C3 reject");
508
+ expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
466
509
  });
467
510
 
468
- test('enforcement appends invite directive when requestCode is absent', async () => {
511
+ test("enforcement appends invite directive when requestCode is absent", async () => {
469
512
  configuredProvider = {
470
513
  sendMessage: async () => ({ content: [] }),
471
514
  };
472
515
  extractedToolUse = {
473
- name: 'record_notification_decision',
516
+ name: "record_notification_decision",
474
517
  input: {
475
518
  shouldNotify: true,
476
- selectedChannels: ['vellum'],
477
- reasoningSummary: 'LLM decision',
519
+ selectedChannels: ["vellum"],
520
+ reasoningSummary: "LLM decision",
478
521
  renderedCopy: {
479
522
  vellum: {
480
- title: 'Access Request',
481
- body: 'Someone wants access to your assistant.',
523
+ title: "Access Request",
524
+ body: "Someone wants access to your assistant.",
482
525
  },
483
526
  },
484
- dedupeKey: 'access-req-no-code-invite',
527
+ dedupeKey: "access-req-no-code-invite",
485
528
  confidence: 0.9,
486
529
  },
487
530
  };
488
531
 
489
532
  const signal = makeAccessRequestSignal({
490
533
  contextPayload: {
491
- senderIdentifier: 'Alice',
492
- sourceChannel: 'telegram',
534
+ senderIdentifier: "Alice",
535
+ sourceChannel: "telegram",
493
536
  // No requestCode
494
537
  },
495
538
  });
496
- const decision = await evaluateSignal(signal, ['vellum'] as NotificationChannel[]);
539
+ const decision = await evaluateSignal(signal, [
540
+ "vellum",
541
+ ] as NotificationChannel[]);
497
542
 
498
543
  expect(decision.fallbackUsed).toBe(false);
499
544
  // Invite directive should still be enforced even without requestCode
500
- expect(decision.renderedCopy.vellum?.body).toContain('open invite flow');
545
+ expect(decision.renderedCopy.vellum?.body).toContain("open invite flow");
501
546
  // Approve/reject should NOT be present since there is no requestCode
502
- expect(decision.renderedCopy.vellum?.body).not.toContain('approve');
503
- expect(decision.renderedCopy.vellum?.body).not.toContain('reject');
547
+ expect(decision.renderedCopy.vellum?.body).not.toContain("approve");
548
+ expect(decision.renderedCopy.vellum?.body).not.toContain("reject");
504
549
  });
505
550
  });