@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,40 +1,49 @@
1
- import { randomBytes } from 'node:crypto';
2
- import { mkdirSync, rmSync } from 'node:fs';
3
- import { readFileSync } from 'node:fs';
4
- import { tmpdir } from 'node:os';
5
- import { join } from 'node:path';
6
- import { dirname,resolve } from 'node:path';
7
- import { fileURLToPath } from 'node:url';
8
-
9
- import { afterAll, afterEach, beforeEach, describe, expect, mock,test } from 'bun:test';
1
+ import { randomBytes } from "node:crypto";
2
+ import { mkdirSync, rmSync } from "node:fs";
3
+ import { readFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import { join } from "node:path";
6
+ import { dirname, resolve } from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import {
9
+ afterAll,
10
+ afterEach,
11
+ beforeEach,
12
+ describe,
13
+ expect,
14
+ mock,
15
+ test,
16
+ } from "bun:test";
10
17
 
11
18
  import {
12
19
  contextInjectionCases,
13
20
  directReadCases,
14
21
  logLeakageCases,
15
22
  policyMisuseCases,
16
- } from './fixtures/credential-security-fixtures.js';
23
+ } from "./fixtures/credential-security-fixtures.js";
17
24
 
18
25
  // ---------------------------------------------------------------------------
19
26
  // Mock logger
20
27
  // ---------------------------------------------------------------------------
21
28
 
22
- mock.module('../util/logger.js', () => ({
23
- getLogger: () => new Proxy({} as Record<string, unknown>, {
24
- get: () => () => {},
25
- }),
29
+ mock.module("../util/logger.js", () => ({
30
+ getLogger: () =>
31
+ new Proxy({} as Record<string, unknown>, {
32
+ get: () => () => {},
33
+ }),
26
34
  }));
27
35
 
28
36
  // ---------------------------------------------------------------------------
29
37
  // Use encrypted backend (no keychain) with a temp store path
30
38
  // ---------------------------------------------------------------------------
31
39
 
32
- import { _overrideDeps, _resetDeps } from '../security/keychain.js';
40
+ import { _overrideDeps, _resetDeps } from "../security/keychain.js";
33
41
 
34
42
  _overrideDeps({
35
43
  isMacOS: () => false,
36
44
  isLinux: () => false,
37
- execFileSync: (() => '') as unknown as typeof import('node:child_process').execFileSync,
45
+ execFileSync: (() =>
46
+ "") as unknown as typeof import("node:child_process").execFileSync,
38
47
  });
39
48
 
40
49
  // Restore process-level keychain deps so later test files are not affected
@@ -43,17 +52,20 @@ afterAll(() => {
43
52
  mock.restore();
44
53
  });
45
54
 
46
- import { _setStorePath } from '../security/encrypted-store.js';
47
- import { _resetBackend } from '../security/secure-keys.js';
55
+ import { _setStorePath } from "../security/encrypted-store.js";
56
+ import { _resetBackend } from "../security/secure-keys.js";
48
57
 
49
- const TEST_DIR = join(tmpdir(), `vellum-invariants-test-${randomBytes(4).toString('hex')}`);
50
- const STORE_PATH = join(TEST_DIR, 'keys.enc');
58
+ const TEST_DIR = join(
59
+ tmpdir(),
60
+ `vellum-invariants-test-${randomBytes(4).toString("hex")}`,
61
+ );
62
+ const STORE_PATH = join(TEST_DIR, "keys.enc");
51
63
 
52
64
  // ---------------------------------------------------------------------------
53
65
  // Mock registry to avoid double-registration
54
66
  // ---------------------------------------------------------------------------
55
67
 
56
- mock.module('../tools/registry.js', () => ({
68
+ mock.module("../tools/registry.js", () => ({
57
69
  registerTool: () => {},
58
70
  }));
59
71
 
@@ -61,11 +73,14 @@ mock.module('../tools/registry.js', () => ({
61
73
  // Imports under test
62
74
  // ---------------------------------------------------------------------------
63
75
 
64
- import { DEFAULT_CONFIG } from '../config/defaults.js';
65
- import { redactSensitiveFields } from '../security/redaction.js';
66
- import { setSecureKey } from '../security/secure-keys.js';
67
- import { CredentialBroker } from '../tools/credentials/broker.js';
68
- import { _setMetadataPath,upsertCredentialMetadata } from '../tools/credentials/metadata-store.js';
76
+ import { DEFAULT_CONFIG } from "../config/defaults.js";
77
+ import { redactSensitiveFields } from "../security/redaction.js";
78
+ import { setSecureKey } from "../security/secure-keys.js";
79
+ import { CredentialBroker } from "../tools/credentials/broker.js";
80
+ import {
81
+ _setMetadataPath,
82
+ upsertCredentialMetadata,
83
+ } from "../tools/credentials/metadata-store.js";
69
84
 
70
85
  /**
71
86
  * Security invariant test harness for credential storage hardening.
@@ -84,33 +99,41 @@ import { _setMetadataPath,upsertCredentialMetadata } from '../tools/credentials/
84
99
  // Invariant 1 — Context Injection Prevention
85
100
  // ---------------------------------------------------------------------------
86
101
 
87
- describe('Invariant 1: secrets never enter LLM context', () => {
102
+ describe("Invariant 1: secrets never enter LLM context", () => {
88
103
  for (const tc of contextInjectionCases) {
89
- if (tc.vector === 'tool_output' && tc.tool === 'credential_store' && tc.input.action === 'store') {
104
+ if (
105
+ tc.vector === "tool_output" &&
106
+ tc.tool === "credential_store" &&
107
+ tc.input.action === "store"
108
+ ) {
90
109
  // Store output never includes the value
91
110
  test(`${tc.label}: secret not in output`, () => {
92
111
  expect(tc.forbiddenValue).toBeTruthy();
93
112
  // Actual assertion is in credential-vault.test.ts baseline section
94
113
  });
95
- } else if (tc.vector === 'confirmation_payload') {
114
+ } else if (tc.vector === "confirmation_payload") {
96
115
  // PR 23 added redaction to confirmation_request payloads via redactSensitiveFields
97
116
  test(`${tc.label}: secret redacted from confirmation payload`, () => {
98
117
  const payload = { ...tc.input };
99
- const redacted = redactSensitiveFields(payload as Record<string, unknown>);
118
+ const redacted = redactSensitiveFields(
119
+ payload as Record<string, unknown>,
120
+ );
100
121
 
101
122
  // The 'value' key is in SENSITIVE_KEYS and gets redacted
102
- if ('value' in payload && payload.value != null) {
103
- expect(redacted.value).toBe('<redacted />');
123
+ if ("value" in payload && payload.value != null) {
124
+ expect(redacted.value).toBe("<redacted />");
104
125
  expect(redacted.value).not.toBe(tc.forbiddenValue);
105
126
  }
106
127
  });
107
- } else if (tc.vector === 'lifecycle_event') {
128
+ } else if (tc.vector === "lifecycle_event") {
108
129
  // PR 22 added recursive redaction in tool executor lifecycle events
109
130
  test(`${tc.label}: secret redacted from lifecycle event`, () => {
110
131
  const input = { ...tc.input };
111
- const redacted = redactSensitiveFields(input as Record<string, unknown>);
112
- if ('value' in input && input.value != null) {
113
- expect(redacted.value).toBe('<redacted />');
132
+ const redacted = redactSensitiveFields(
133
+ input as Record<string, unknown>,
134
+ );
135
+ if ("value" in input && input.value != null) {
136
+ expect(redacted.value).toBe("<redacted />");
114
137
  expect(redacted.value).not.toBe(tc.forbiddenValue);
115
138
  }
116
139
  });
@@ -123,15 +146,15 @@ describe('Invariant 1: secrets never enter LLM context', () => {
123
146
  }
124
147
 
125
148
  // PR 27 — secret ingress block scans inbound messages
126
- test('user message containing secret is blocked from entering history', () => {
149
+ test("user message containing secret is blocked from entering history", () => {
127
150
  // Mock config to enable block mode
128
- mock.module('../config/loader.js', () => ({
151
+ mock.module("../config/loader.js", () => ({
129
152
  applyNestedDefaults: (config: unknown) => config,
130
153
  getConfig: () => ({
131
154
  ui: {},
132
155
  secretDetection: {
133
156
  enabled: true,
134
- action: 'block',
157
+ action: "block",
135
158
  blockIngress: true,
136
159
  },
137
160
  }),
@@ -140,7 +163,7 @@ describe('Invariant 1: secrets never enter LLM context', () => {
140
163
  ui: {},
141
164
  secretDetection: {
142
165
  enabled: true,
143
- action: 'block',
166
+ action: "block",
144
167
  blockIngress: true,
145
168
  },
146
169
  }),
@@ -148,10 +171,10 @@ describe('Invariant 1: secrets never enter LLM context', () => {
148
171
 
149
172
  // Re-import to pick up the mock
150
173
  // eslint-disable-next-line @typescript-eslint/no-require-imports
151
- const { checkIngressForSecrets } = require('../security/secret-ingress.js');
174
+ const { checkIngressForSecrets } = require("../security/secret-ingress.js");
152
175
 
153
176
  // Build a fake AWS key at runtime to avoid pre-commit hook
154
- const fakeKey = ['AKIA', 'IOSFODNN7', 'REALKEY'].join('');
177
+ const fakeKey = ["AKIA", "IOSFODNN7", "REALKEY"].join("");
155
178
  const result = checkIngressForSecrets(`My key is ${fakeKey}`);
156
179
 
157
180
  expect(result.blocked).toBe(true);
@@ -166,7 +189,7 @@ describe('Invariant 1: secrets never enter LLM context', () => {
166
189
  // Invariant 2 — No Generic Plaintext Read API
167
190
  // ---------------------------------------------------------------------------
168
191
 
169
- describe('Invariant 2: no generic plaintext secret read API', () => {
192
+ describe("Invariant 2: no generic plaintext secret read API", () => {
170
193
  for (const tc of directReadCases) {
171
194
  test(`${tc.modulePath} does not export ${tc.exportName}`, async () => {
172
195
  const mod = await import(`../${tc.modulePath}.js`);
@@ -174,68 +197,68 @@ describe('Invariant 2: no generic plaintext secret read API', () => {
174
197
  });
175
198
  }
176
199
 
177
- test('browser_fill_credential does not import getCredentialValue', () => {
200
+ test("browser_fill_credential does not import getCredentialValue", () => {
178
201
  const thisDir = dirname(fileURLToPath(import.meta.url));
179
202
  const browserSrc = readFileSync(
180
- resolve(thisDir, '../tools/browser/headless-browser.ts'),
181
- 'utf-8',
203
+ resolve(thisDir, "../tools/browser/headless-browser.ts"),
204
+ "utf-8",
182
205
  );
183
- expect(browserSrc).not.toContain('getCredentialValue');
206
+ expect(browserSrc).not.toContain("getCredentialValue");
184
207
  });
185
208
 
186
- test('getSecureKey is only imported by authorized modules', () => {
209
+ test("getSecureKey is only imported by authorized modules", () => {
187
210
  // Hard boundary: only these production files may import getSecureKey.
188
211
  // Any new import must be reviewed for secret-leak risk and added here.
189
212
  const ALLOWED_IMPORTERS = new Set([
190
- 'security/secure-keys.ts', // self (re-export infrastructure)
191
- 'index.ts', // daemon startup / API key config
192
- 'config/loader.ts', // config management (API keys)
193
- 'tools/credentials/vault.ts', // credential store tool
194
- 'tools/credentials/broker.ts', // brokered credential access
195
- 'tools/network/web-search.ts', // web search API key lookup
196
- 'daemon/handlers.ts', // Vercel API token + integration OAuth
197
- 'daemon/handlers/config-integrations.ts', // Vercel API token + Twitter integration OAuth
198
- 'daemon/handlers/config-telegram.ts', // Telegram bot token management
199
- 'daemon/handlers/config-ingress.ts', // Ingress config (reads Twilio credentials for webhook sync)
200
- 'runtime/routes/twilio-routes.ts', // Twilio credential management (HTTP control-plane)
201
- 'security/token-manager.ts', // OAuth token refresh flow
202
- 'email/providers/index.ts', // email provider API key lookup
203
- 'tools/network/script-proxy/session-manager.ts', // proxy credential injection at runtime
204
- 'messaging/registry.ts', // checks stored credentials for connected providers
205
- 'calls/call-domain.ts', // caller identity resolution (user phone number lookup)
206
- 'calls/elevenlabs-config.ts', // ElevenLabs voice quality API key lookup
207
- 'calls/twilio-config.ts', // call infrastructure credential lookup
208
- 'calls/twilio-provider.ts', // call infrastructure credential lookup
209
- 'calls/twilio-rest.ts', // Twilio REST API credential lookup
210
- 'cli/config-commands.ts', // CLI credential management commands
211
- 'runtime/http-server.ts', // HTTP server credential lookup
212
- 'daemon/handlers/twitter-auth.ts', // Twitter OAuth token storage
213
- 'twitter/oauth-client.ts', // Twitter OAuth API client (reads access token for API calls)
214
- 'calls/elevenlabs-config.ts', // ElevenLabs credential lookup
215
- 'cli/config-commands.ts', // CLI config management
216
- 'messaging/providers/telegram-bot/adapter.ts', // Telegram bot token lookup for connectivity check
217
- 'messaging/providers/sms/adapter.ts', // Twilio credential lookup for SMS connectivity check
218
- 'runtime/channel-readiness-service.ts', // channel readiness probes for SMS/Telegram connectivity
219
- 'messaging/providers/whatsapp/adapter.ts', // WhatsApp credential lookup for connectivity check
220
- 'schedule/integration-status.ts', // integration status checks for scheduled reports
221
- 'daemon/handlers/oauth-connect.ts', // OAuth connect handler for integration setup
222
- 'daemon/handlers/config-slack-channel.ts', // Slack channel config credential management
213
+ "security/secure-keys.ts", // self (re-export infrastructure)
214
+ "index.ts", // daemon startup / API key config
215
+ "config/loader.ts", // config management (API keys)
216
+ "tools/credentials/vault.ts", // credential store tool
217
+ "tools/credentials/broker.ts", // brokered credential access
218
+ "tools/network/web-search.ts", // web search API key lookup
219
+ "daemon/handlers.ts", // Vercel API token + integration OAuth
220
+ "daemon/handlers/config-integrations.ts", // Vercel API token + Twitter integration OAuth
221
+ "daemon/handlers/config-telegram.ts", // Telegram bot token management
222
+ "daemon/handlers/config-ingress.ts", // Ingress config (reads Twilio credentials for webhook sync)
223
+ "runtime/routes/twilio-routes.ts", // Twilio credential management (HTTP control-plane)
224
+ "security/token-manager.ts", // OAuth token refresh flow
225
+ "email/providers/index.ts", // email provider API key lookup
226
+ "tools/network/script-proxy/session-manager.ts", // proxy credential injection at runtime
227
+ "messaging/registry.ts", // checks stored credentials for connected providers
228
+ "calls/call-domain.ts", // caller identity resolution (user phone number lookup)
229
+ "calls/elevenlabs-config.ts", // ElevenLabs voice quality API key lookup
230
+ "calls/twilio-config.ts", // call infrastructure credential lookup
231
+ "calls/twilio-provider.ts", // call infrastructure credential lookup
232
+ "calls/twilio-rest.ts", // Twilio REST API credential lookup
233
+ "cli/config-commands.ts", // CLI credential management commands
234
+ "runtime/http-server.ts", // HTTP server credential lookup
235
+ "daemon/handlers/twitter-auth.ts", // Twitter OAuth token storage
236
+ "twitter/oauth-client.ts", // Twitter OAuth API client (reads access token for API calls)
237
+ "calls/elevenlabs-config.ts", // ElevenLabs credential lookup
238
+ "cli/config-commands.ts", // CLI config management
239
+ "messaging/providers/telegram-bot/adapter.ts", // Telegram bot token lookup for connectivity check
240
+ "messaging/providers/sms/adapter.ts", // Twilio credential lookup for SMS connectivity check
241
+ "runtime/channel-readiness-service.ts", // channel readiness probes for SMS/Telegram connectivity
242
+ "messaging/providers/whatsapp/adapter.ts", // WhatsApp credential lookup for connectivity check
243
+ "schedule/integration-status.ts", // integration status checks for scheduled reports
244
+ "daemon/handlers/oauth-connect.ts", // OAuth connect handler for integration setup
245
+ "daemon/handlers/config-slack-channel.ts", // Slack channel config credential management
223
246
  ]);
224
247
 
225
248
  const thisDir = dirname(fileURLToPath(import.meta.url));
226
- const srcDir = resolve(thisDir, '..');
249
+ const srcDir = resolve(thisDir, "..");
227
250
  // eslint-disable-next-line @typescript-eslint/no-require-imports
228
- const { readdirSync, statSync } = require('node:fs');
251
+ const { readdirSync, statSync } = require("node:fs");
229
252
 
230
253
  // Recursively collect all .ts files in src/ (excluding __tests__)
231
254
  function collectTsFiles(dir: string, files: string[] = []): string[] {
232
255
  for (const entry of readdirSync(dir)) {
233
256
  const full = join(dir, entry);
234
- if (entry === '__tests__' || entry === 'node_modules') continue;
257
+ if (entry === "__tests__" || entry === "node_modules") continue;
235
258
  const s = statSync(full);
236
259
  if (s.isDirectory()) {
237
260
  collectTsFiles(full, files);
238
- } else if (entry.endsWith('.ts') && !entry.endsWith('.d.ts')) {
261
+ } else if (entry.endsWith(".ts") && !entry.endsWith(".d.ts")) {
239
262
  files.push(full);
240
263
  }
241
264
  }
@@ -246,9 +269,13 @@ describe('Invariant 2: no generic plaintext secret read API', () => {
246
269
  const unauthorizedImporters: string[] = [];
247
270
 
248
271
  for (const filePath of allFiles) {
249
- const content = readFileSync(filePath, 'utf-8');
272
+ const content = readFileSync(filePath, "utf-8");
250
273
  // Check for imports of getSecureKey via static import, dynamic import(), or require()
251
- if (content.match(/\bgetSecureKey\b/) && (content.match(/from\s+['"].*secure-keys/) || content.match(/(?:import|require)\s*\(\s*['"].*secure-keys/))) {
274
+ if (
275
+ content.match(/\bgetSecureKey\b/) &&
276
+ (content.match(/from\s+['"].*secure-keys/) ||
277
+ content.match(/(?:import|require)\s*\(\s*['"].*secure-keys/))
278
+ ) {
252
279
  const relative = filePath.slice(srcDir.length + 1);
253
280
  if (!ALLOWED_IMPORTERS.has(relative)) {
254
281
  unauthorizedImporters.push(relative);
@@ -264,39 +291,43 @@ describe('Invariant 2: no generic plaintext secret read API', () => {
264
291
  // Invariant 3 — No Plaintext Secret Logging
265
292
  // ---------------------------------------------------------------------------
266
293
 
267
- describe('Invariant 3: secrets never logged in plaintext', () => {
294
+ describe("Invariant 3: secrets never logged in plaintext", () => {
268
295
  for (const tc of logLeakageCases) {
269
- if (tc.component === 'tool_executor') {
296
+ if (tc.component === "tool_executor") {
270
297
  // PR 22 — executor redaction via redactSensitiveFields
271
298
  test(`${tc.label}`, () => {
272
299
  // Simulate a tool input with sensitive fields
273
300
  // Build test values at runtime to avoid pre-commit hook false positives
274
- const testValue = ['ghp_super', 'secret123'].join('');
275
- const testPassword = ['hunt', 'er2'].join('');
276
- const testToken = ['nested_', 'token_value'].join('');
301
+ const testValue = ["ghp_super", "secret123"].join("");
302
+ const testPassword = ["hunt", "er2"].join("");
303
+ const testToken = ["nested_", "token_value"].join("");
277
304
  const input = {
278
- action: 'store',
279
- service: 'github',
280
- field: 'token',
305
+ action: "store",
306
+ service: "github",
307
+ field: "token",
281
308
  value: testValue,
282
309
  password: testPassword,
283
310
  nested: {
284
311
  token: testToken,
285
- safe: 'this is fine',
312
+ safe: "this is fine",
286
313
  },
287
314
  };
288
315
  const redacted = redactSensitiveFields(input);
289
316
 
290
317
  // All sensitive keys must be redacted
291
- expect(redacted.value).toBe('<redacted />');
292
- expect(redacted.password).toBe('<redacted />');
293
- expect((redacted.nested as Record<string, unknown>).token).toBe('<redacted />');
318
+ expect(redacted.value).toBe("<redacted />");
319
+ expect(redacted.password).toBe("<redacted />");
320
+ expect((redacted.nested as Record<string, unknown>).token).toBe(
321
+ "<redacted />",
322
+ );
294
323
  // Non-sensitive keys preserved
295
- expect(redacted.action).toBe('store');
296
- expect(redacted.service).toBe('github');
297
- expect((redacted.nested as Record<string, unknown>).safe).toBe('this is fine');
324
+ expect(redacted.action).toBe("store");
325
+ expect(redacted.service).toBe("github");
326
+ expect((redacted.nested as Record<string, unknown>).safe).toBe(
327
+ "this is fine",
328
+ );
298
329
  });
299
- } else if (tc.component === 'ipc_decode') {
330
+ } else if (tc.component === "ipc_decode") {
300
331
  // PR 24 — IPC decode log hygiene: the TS daemon's IPC parser must
301
332
  // not log raw message content that could contain secrets.
302
333
  // Logging metadata (line length, error type) is acceptable; logging
@@ -304,8 +335,8 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
304
335
  test(`${tc.label}`, () => {
305
336
  const thisDir = dirname(fileURLToPath(import.meta.url));
306
337
  const ipcSrc = readFileSync(
307
- resolve(thisDir, '../daemon/ipc-protocol.ts'),
308
- 'utf-8',
338
+ resolve(thisDir, "../daemon/ipc-protocol.ts"),
339
+ "utf-8",
309
340
  );
310
341
  // Verify log calls never include raw content fields — only safe
311
342
  // metadata like lineLength and errorType are permitted.
@@ -326,8 +357,8 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
326
357
  test(`${tc.label}`, () => {
327
358
  const thisDir = dirname(fileURLToPath(import.meta.url));
328
359
  const prompterSrc = readFileSync(
329
- resolve(thisDir, '../permissions/secret-prompter.ts'),
330
- 'utf-8',
360
+ resolve(thisDir, "../permissions/secret-prompter.ts"),
361
+ "utf-8",
331
362
  );
332
363
 
333
364
  // Extract all log.* call arguments: log.warn({...}, 'msg')
@@ -337,19 +368,29 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
337
368
  let match;
338
369
  while ((match = logCallPattern.exec(prompterSrc)) != null) {
339
370
  // Collect field names from the structured log object
340
- const fields = match[1].split(',').map(f => f.trim().split(':')[0].trim());
371
+ const fields = match[1]
372
+ .split(",")
373
+ .map((f) => f.trim().split(":")[0].trim());
341
374
  loggedFields.push(...fields);
342
375
  }
343
376
 
344
377
  // None of the logged fields should be sensitive credential fields
345
- const sensitiveFields = ['value', 'secret', 'password', 'token', 'api_key', 'credentials'];
378
+ const sensitiveFields = [
379
+ "value",
380
+ "secret",
381
+ "password",
382
+ "token",
383
+ "api_key",
384
+ "credentials",
385
+ ];
346
386
  for (const field of loggedFields) {
347
387
  expect(sensitiveFields).not.toContain(field);
348
388
  }
349
389
 
350
390
  // Additionally verify the resolveSecret method never logs its value parameter
351
391
  // by checking that log calls in resolveSecret only reference requestId
352
- const resolveBlock = prompterSrc.match(/resolveSecret[\s\S]*?^\s{2}\}/m)?.[0] ?? '';
392
+ const resolveBlock =
393
+ prompterSrc.match(/resolveSecret[\s\S]*?^\s{2}\}/m)?.[0] ?? "";
353
394
  expect(resolveBlock).not.toMatch(/log\.\w+\(.*\bvalue\b/);
354
395
  });
355
396
  }
@@ -360,14 +401,14 @@ describe('Invariant 3: secrets never logged in plaintext', () => {
360
401
  // Invariant 4 — Usage-Constrained Credentials (Tool + Domain Policy)
361
402
  // ---------------------------------------------------------------------------
362
403
 
363
- describe('Invariant 4: credentials only used for allowed purpose', () => {
404
+ describe("Invariant 4: credentials only used for allowed purpose", () => {
364
405
  let broker: CredentialBroker;
365
406
 
366
407
  beforeEach(() => {
367
408
  mkdirSync(TEST_DIR, { recursive: true });
368
409
  _setStorePath(STORE_PATH);
369
410
  _resetBackend();
370
- _setMetadataPath(join(TEST_DIR, 'metadata.json'));
411
+ _setMetadataPath(join(TEST_DIR, "metadata.json"));
371
412
  broker = new CredentialBroker();
372
413
  });
373
414
 
@@ -382,15 +423,15 @@ describe('Invariant 4: credentials only used for allowed purpose', () => {
382
423
  // PRs 19-20 — tool + domain policy enforcement in broker
383
424
  test(`${tc.label}`, async () => {
384
425
  // Set up credential with the specified policy
385
- upsertCredentialMetadata(tc.credentialId, 'token', {
426
+ upsertCredentialMetadata(tc.credentialId, "token", {
386
427
  allowedTools: tc.allowedTools,
387
428
  allowedDomains: tc.allowedDomains,
388
429
  });
389
- setSecureKey(`credential:${tc.credentialId}:token`, 'test-secret-value');
430
+ setSecureKey(`credential:${tc.credentialId}:token`, "test-secret-value");
390
431
 
391
432
  const result = await broker.browserFill({
392
433
  service: tc.credentialId,
393
- field: 'token',
434
+ field: "token",
394
435
  toolName: tc.requestingTool,
395
436
  domain: tc.requestDomain,
396
437
  fill: async () => {},
@@ -406,18 +447,18 @@ describe('Invariant 4: credentials only used for allowed purpose', () => {
406
447
  }
407
448
 
408
449
  // PR 20 — domain policy uses registrable-domain matching
409
- test('domain policy allows subdomains of registrable domain', async () => {
410
- upsertCredentialMetadata('github', 'token', {
411
- allowedTools: ['browser_fill_credential'],
412
- allowedDomains: ['github.com'],
450
+ test("domain policy allows subdomains of registrable domain", async () => {
451
+ upsertCredentialMetadata("github", "token", {
452
+ allowedTools: ["browser_fill_credential"],
453
+ allowedDomains: ["github.com"],
413
454
  });
414
- setSecureKey('credential:github:token', 'ghp_secret123');
455
+ setSecureKey("credential:github:token", "ghp_secret123");
415
456
 
416
457
  const result = await broker.browserFill({
417
- service: 'github',
418
- field: 'token',
419
- toolName: 'browser_fill_credential',
420
- domain: 'login.github.com',
458
+ service: "github",
459
+ field: "token",
460
+ toolName: "browser_fill_credential",
461
+ domain: "login.github.com",
421
462
  fill: async () => {},
422
463
  });
423
464
 
@@ -425,19 +466,21 @@ describe('Invariant 4: credentials only used for allowed purpose', () => {
425
466
  });
426
467
 
427
468
  // PR 18 — vault policy fields with strict defaults
428
- test('credential without explicit policy gets strict defaults (deny all)', () => {
469
+ test("credential without explicit policy gets strict defaults (deny all)", () => {
429
470
  // A credential stored without allowed_tools defaults to empty array,
430
471
  // which the broker's isToolAllowed check fails closed on.
431
- upsertCredentialMetadata('test-svc', 'pass', {});
472
+ upsertCredentialMetadata("test-svc", "pass", {});
432
473
 
433
474
  const result = broker.authorize({
434
- service: 'test-svc',
435
- field: 'pass',
436
- toolName: 'browser_fill_credential',
475
+ service: "test-svc",
476
+ field: "pass",
477
+ toolName: "browser_fill_credential",
437
478
  });
438
479
 
439
480
  expect(result.authorized).toBe(false);
440
- expect(!result.authorized && result.reason).toContain('No tools are currently allowed');
481
+ expect(!result.authorized && result.reason).toContain(
482
+ "No tools are currently allowed",
483
+ );
441
484
  });
442
485
  });
443
486
 
@@ -445,21 +488,21 @@ describe('Invariant 4: credentials only used for allowed purpose', () => {
445
488
  // Cross-Cutting — One-Time Send Override
446
489
  // ---------------------------------------------------------------------------
447
490
 
448
- describe('One-time send override', () => {
449
- test('transient_send delivery type is defined in SecretPromptResult', () => {
450
- const delivery: 'store' | 'transient_send' = 'transient_send';
451
- expect(delivery).toBe('transient_send');
491
+ describe("One-time send override", () => {
492
+ test("transient_send delivery type is defined in SecretPromptResult", () => {
493
+ const delivery: "store" | "transient_send" = "transient_send";
494
+ expect(delivery).toBe("transient_send");
452
495
  });
453
496
 
454
- test('allowOneTimeSend defaults to false in config', () => {
497
+ test("allowOneTimeSend defaults to false in config", () => {
455
498
  expect(DEFAULT_CONFIG.secretDetection.allowOneTimeSend).toBe(false);
456
499
  });
457
500
 
458
- test('default secretDetection.action is redact', () => {
459
- expect(DEFAULT_CONFIG.secretDetection.action).toBe('redact');
501
+ test("default secretDetection.action is redact", () => {
502
+ expect(DEFAULT_CONFIG.secretDetection.action).toBe("redact");
460
503
  });
461
504
 
462
- test('default secretDetection.blockIngress is true', () => {
505
+ test("default secretDetection.blockIngress is true", () => {
463
506
  expect(DEFAULT_CONFIG.secretDetection.blockIngress).toBe(true);
464
507
  });
465
508
  });
@@ -472,130 +515,132 @@ import {
472
515
  createSafeLogEntry,
473
516
  sanitizeHeaders,
474
517
  sanitizeUrl,
475
- } from '../tools/network/script-proxy/logging.js';
518
+ } from "../tools/network/script-proxy/logging.js";
476
519
 
477
- describe('Invariant 5: proxy log entries never contain secrets', () => {
478
- test('Authorization headers are redacted in log entries', () => {
520
+ describe("Invariant 5: proxy log entries never contain secrets", () => {
521
+ test("Authorization headers are redacted in log entries", () => {
479
522
  const headers: Record<string, string> = {
480
- 'Authorization': 'Bearer ghp_s3cr3tT0k3n',
481
- 'Content-Type': 'application/json',
482
- 'X-Custom': 'safe-value',
523
+ Authorization: "Bearer ghp_s3cr3tT0k3n",
524
+ "Content-Type": "application/json",
525
+ "X-Custom": "safe-value",
483
526
  };
484
527
 
485
- const sanitized = sanitizeHeaders(headers, ['authorization']);
528
+ const sanitized = sanitizeHeaders(headers, ["authorization"]);
486
529
 
487
- expect(sanitized['Authorization']).toBe('[REDACTED]');
488
- expect(sanitized['Content-Type']).toBe('application/json');
489
- expect(sanitized['X-Custom']).toBe('safe-value');
530
+ expect(sanitized["Authorization"]).toBe("[REDACTED]");
531
+ expect(sanitized["Content-Type"]).toBe("application/json");
532
+ expect(sanitized["X-Custom"]).toBe("safe-value");
490
533
  });
491
534
 
492
- test('header redaction is case-insensitive', () => {
535
+ test("header redaction is case-insensitive", () => {
493
536
  const headers: Record<string, string> = {
494
- 'authorization': 'Bearer secret123',
495
- 'X-Api-Key': 'key-abc-123',
537
+ authorization: "Bearer secret123",
538
+ "X-Api-Key": "key-abc-123",
496
539
  };
497
540
 
498
- const sanitized = sanitizeHeaders(headers, ['Authorization', 'x-api-key']);
541
+ const sanitized = sanitizeHeaders(headers, ["Authorization", "x-api-key"]);
499
542
 
500
- expect(sanitized['authorization']).toBe('[REDACTED]');
501
- expect(sanitized['X-Api-Key']).toBe('[REDACTED]');
543
+ expect(sanitized["authorization"]).toBe("[REDACTED]");
544
+ expect(sanitized["X-Api-Key"]).toBe("[REDACTED]");
502
545
  });
503
546
 
504
- test('API key query params are redacted', () => {
505
- const url = 'https://api.example.com/v1/search?api_key=sk-secret-value&q=hello';
506
- const sanitized = sanitizeUrl(url, ['api_key']);
547
+ test("API key query params are redacted", () => {
548
+ const url =
549
+ "https://api.example.com/v1/search?api_key=sk-secret-value&q=hello";
550
+ const sanitized = sanitizeUrl(url, ["api_key"]);
507
551
 
508
- expect(sanitized).not.toContain('sk-secret-value');
509
- expect(sanitized).toContain('api_key=%5BREDACTED%5D');
510
- expect(sanitized).toContain('q=hello');
552
+ expect(sanitized).not.toContain("sk-secret-value");
553
+ expect(sanitized).toContain("api_key=%5BREDACTED%5D");
554
+ expect(sanitized).toContain("q=hello");
511
555
  });
512
556
 
513
- test('multiple sensitive query params are all redacted', () => {
514
- const url = 'https://api.example.com/path?token=abc123&key=def456&safe=keep';
515
- const sanitized = sanitizeUrl(url, ['token', 'key']);
557
+ test("multiple sensitive query params are all redacted", () => {
558
+ const url =
559
+ "https://api.example.com/path?token=abc123&key=def456&safe=keep";
560
+ const sanitized = sanitizeUrl(url, ["token", "key"]);
516
561
 
517
- expect(sanitized).not.toContain('abc123');
518
- expect(sanitized).not.toContain('def456');
519
- expect(sanitized).toContain('safe=keep');
562
+ expect(sanitized).not.toContain("abc123");
563
+ expect(sanitized).not.toContain("def456");
564
+ expect(sanitized).toContain("safe=keep");
520
565
  });
521
566
 
522
- test('sanitizeUrl handles path-only URLs', () => {
523
- const url = '/v1/search?api_key=secret&q=hello';
524
- const sanitized = sanitizeUrl(url, ['api_key']);
567
+ test("sanitizeUrl handles path-only URLs", () => {
568
+ const url = "/v1/search?api_key=secret&q=hello";
569
+ const sanitized = sanitizeUrl(url, ["api_key"]);
525
570
 
526
- expect(sanitized).not.toContain('secret');
527
- expect(sanitized).toContain('q=hello');
571
+ expect(sanitized).not.toContain("secret");
572
+ expect(sanitized).toContain("q=hello");
528
573
  // Result should still be a path, not an absolute URL
529
574
  expect(sanitized).toMatch(/^\//);
530
575
  });
531
576
 
532
- test('sanitizeUrl returns URL unchanged when no query string', () => {
533
- const url = 'https://api.example.com/v1/resource';
534
- expect(sanitizeUrl(url, ['api_key'])).toBe(url);
577
+ test("sanitizeUrl returns URL unchanged when no query string", () => {
578
+ const url = "https://api.example.com/v1/resource";
579
+ expect(sanitizeUrl(url, ["api_key"])).toBe(url);
535
580
  });
536
581
 
537
- test('credential values from injection templates never appear in sanitized output', () => {
582
+ test("credential values from injection templates never appear in sanitized output", () => {
538
583
  // Simulate a header-injected credential (e.g. "Authorization: Key <secret>")
539
- const secretValue = ['Key ', 'fal_', 'superSecretApiKey'].join('');
584
+ const secretValue = ["Key ", "fal_", "superSecretApiKey"].join("");
540
585
  const req = {
541
- method: 'POST',
542
- url: 'https://api.fal.ai/v1/generate',
586
+ method: "POST",
587
+ url: "https://api.fal.ai/v1/generate",
543
588
  headers: {
544
- 'Authorization': secretValue,
545
- 'Content-Type': 'application/json',
546
- 'Host': 'api.fal.ai',
589
+ Authorization: secretValue,
590
+ "Content-Type": "application/json",
591
+ Host: "api.fal.ai",
547
592
  },
548
593
  };
549
594
 
550
- const entry = createSafeLogEntry(req, ['Authorization']);
595
+ const entry = createSafeLogEntry(req, ["Authorization"]);
551
596
  const serialized = JSON.stringify(entry);
552
597
 
553
- expect(serialized).not.toContain('fal_');
554
- expect(serialized).not.toContain('superSecretApiKey');
555
- expect(entry.headers['Authorization']).toBe('[REDACTED]');
556
- expect(entry.headers['Content-Type']).toBe('application/json');
557
- expect(entry.method).toBe('POST');
598
+ expect(serialized).not.toContain("fal_");
599
+ expect(serialized).not.toContain("superSecretApiKey");
600
+ expect(entry.headers["Authorization"]).toBe("[REDACTED]");
601
+ expect(entry.headers["Content-Type"]).toBe("application/json");
602
+ expect(entry.method).toBe("POST");
558
603
  });
559
604
 
560
- test('credential values from query injection templates never appear in sanitized output', () => {
605
+ test("credential values from query injection templates never appear in sanitized output", () => {
561
606
  // Simulate a query-injected credential (e.g. "?api_key=<secret>")
562
- const secretValue = ['sk-live-', 'abc123', 'xyz789'].join('');
607
+ const secretValue = ["sk-live-", "abc123", "xyz789"].join("");
563
608
  const req = {
564
- method: 'GET',
609
+ method: "GET",
565
610
  url: `https://api.example.com/v1/data?api_key=${secretValue}&format=json`,
566
611
  headers: {
567
- 'Host': 'api.example.com',
612
+ Host: "api.example.com",
568
613
  },
569
614
  };
570
615
 
571
- const entry = createSafeLogEntry(req, ['api_key']);
616
+ const entry = createSafeLogEntry(req, ["api_key"]);
572
617
  const serialized = JSON.stringify(entry);
573
618
 
574
- expect(serialized).not.toContain('sk-live-');
575
- expect(serialized).not.toContain('abc123');
576
- expect(serialized).not.toContain('xyz789');
577
- expect(entry.url).toContain('format=json');
619
+ expect(serialized).not.toContain("sk-live-");
620
+ expect(serialized).not.toContain("abc123");
621
+ expect(serialized).not.toContain("xyz789");
622
+ expect(entry.url).toContain("format=json");
578
623
  });
579
624
 
580
- test('createSafeLogEntry redacts both headers and query params together', () => {
581
- const headerSecret = ['Bearer ', 'ghp_', 'tokenValue'].join('');
582
- const querySecret = ['secret-', 'key-', '42'].join('');
625
+ test("createSafeLogEntry redacts both headers and query params together", () => {
626
+ const headerSecret = ["Bearer ", "ghp_", "tokenValue"].join("");
627
+ const querySecret = ["secret-", "key-", "42"].join("");
583
628
  const req = {
584
- method: 'GET',
629
+ method: "GET",
585
630
  url: `https://api.github.com/repos?access_token=${querySecret}`,
586
631
  headers: {
587
- 'Authorization': headerSecret,
588
- 'Accept': 'application/json',
632
+ Authorization: headerSecret,
633
+ Accept: "application/json",
589
634
  },
590
635
  };
591
636
 
592
- const entry = createSafeLogEntry(req, ['Authorization', 'access_token']);
637
+ const entry = createSafeLogEntry(req, ["Authorization", "access_token"]);
593
638
  const serialized = JSON.stringify(entry);
594
639
 
595
- expect(serialized).not.toContain('ghp_');
596
- expect(serialized).not.toContain('tokenValue');
597
- expect(serialized).not.toContain('secret-');
598
- expect(serialized).not.toContain('key-42');
599
- expect(entry.headers['Accept']).toBe('application/json');
640
+ expect(serialized).not.toContain("ghp_");
641
+ expect(serialized).not.toContain("tokenValue");
642
+ expect(serialized).not.toContain("secret-");
643
+ expect(serialized).not.toContain("key-42");
644
+ expect(entry.headers["Accept"]).toBe("application/json");
600
645
  });
601
646
  });