@vellumai/assistant 0.4.17 → 0.4.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (515) hide show
  1. package/eslint.config.mjs +2 -2
  2. package/package.json +1 -1
  3. package/src/__tests__/access-request-decision.test.ts +128 -120
  4. package/src/__tests__/account-registry.test.ts +121 -110
  5. package/src/__tests__/active-skill-tools.test.ts +200 -172
  6. package/src/__tests__/actor-token-service.test.ts +341 -274
  7. package/src/__tests__/agent-loop-thinking.test.ts +28 -19
  8. package/src/__tests__/agent-loop.test.ts +798 -378
  9. package/src/__tests__/anthropic-provider.test.ts +405 -247
  10. package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
  11. package/src/__tests__/app-bundler.test.ts +112 -79
  12. package/src/__tests__/app-executors.test.ts +205 -178
  13. package/src/__tests__/app-git-history.test.ts +90 -73
  14. package/src/__tests__/app-git-service.test.ts +67 -53
  15. package/src/__tests__/app-open-proxy.test.ts +29 -25
  16. package/src/__tests__/approval-conversation-turn.test.ts +100 -81
  17. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
  18. package/src/__tests__/approval-message-composer.test.ts +119 -119
  19. package/src/__tests__/approval-primitive.test.ts +264 -233
  20. package/src/__tests__/approval-routes-http.test.ts +4 -3
  21. package/src/__tests__/asset-materialize-tool.test.ts +250 -178
  22. package/src/__tests__/asset-search-tool.test.ts +251 -191
  23. package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
  24. package/src/__tests__/assistant-attachments.test.ts +254 -186
  25. package/src/__tests__/assistant-event-hub.test.ts +105 -63
  26. package/src/__tests__/assistant-event.test.ts +66 -58
  27. package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
  28. package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
  29. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
  30. package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
  31. package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
  32. package/src/__tests__/attachments-store.test.ts +240 -183
  33. package/src/__tests__/attachments.test.ts +70 -62
  34. package/src/__tests__/audit-log-rotation.test.ts +50 -35
  35. package/src/__tests__/browser-fill-credential.test.ts +169 -101
  36. package/src/__tests__/browser-manager.test.ts +97 -75
  37. package/src/__tests__/browser-runtime-check.test.ts +16 -15
  38. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
  39. package/src/__tests__/browser-skill-endstate.test.ts +97 -72
  40. package/src/__tests__/bundle-scanner.test.ts +47 -22
  41. package/src/__tests__/bundled-asset.test.ts +74 -47
  42. package/src/__tests__/call-constants.test.ts +19 -19
  43. package/src/__tests__/call-controller.test.ts +0 -1
  44. package/src/__tests__/call-conversation-messages.test.ts +90 -65
  45. package/src/__tests__/call-domain.test.ts +149 -121
  46. package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
  47. package/src/__tests__/call-pointer-messages.test.ts +213 -154
  48. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
  49. package/src/__tests__/call-recovery.test.ts +232 -212
  50. package/src/__tests__/call-routes-http.test.ts +0 -1
  51. package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
  52. package/src/__tests__/call-state-machine.test.ts +62 -51
  53. package/src/__tests__/call-state.test.ts +89 -75
  54. package/src/__tests__/call-store.test.ts +387 -316
  55. package/src/__tests__/callback-handoff-copy.test.ts +84 -82
  56. package/src/__tests__/canonical-guardian-store.test.ts +331 -280
  57. package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
  58. package/src/__tests__/channel-approval.test.ts +139 -137
  59. package/src/__tests__/channel-approvals.test.ts +0 -1
  60. package/src/__tests__/channel-delivery-store.test.ts +232 -194
  61. package/src/__tests__/channel-guardian.test.ts +5 -3
  62. package/src/__tests__/channel-invite-transport.test.ts +107 -92
  63. package/src/__tests__/channel-policy.test.ts +42 -38
  64. package/src/__tests__/channel-readiness-service.test.ts +119 -102
  65. package/src/__tests__/channel-reply-delivery.test.ts +147 -118
  66. package/src/__tests__/channel-retry-sweep.test.ts +153 -110
  67. package/src/__tests__/checker.test.ts +3309 -1850
  68. package/src/__tests__/clarification-resolver.test.ts +91 -79
  69. package/src/__tests__/classifier.test.ts +64 -54
  70. package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
  71. package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
  72. package/src/__tests__/clawhub.test.ts +92 -82
  73. package/src/__tests__/cli.test.ts +30 -30
  74. package/src/__tests__/clipboard.test.ts +53 -46
  75. package/src/__tests__/commit-guarantee.test.ts +59 -52
  76. package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
  77. package/src/__tests__/compaction.benchmark.test.ts +33 -31
  78. package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
  79. package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
  80. package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
  81. package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
  82. package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
  83. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
  84. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
  85. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
  86. package/src/__tests__/computer-use-tools.test.ts +121 -98
  87. package/src/__tests__/config-schema.test.ts +443 -347
  88. package/src/__tests__/config-watcher.test.ts +96 -81
  89. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
  90. package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
  91. package/src/__tests__/conflict-policy.test.ts +151 -80
  92. package/src/__tests__/conflict-store.test.ts +203 -157
  93. package/src/__tests__/connection-policy.test.ts +89 -59
  94. package/src/__tests__/contacts-tools.test.ts +247 -178
  95. package/src/__tests__/context-memory-e2e.test.ts +306 -214
  96. package/src/__tests__/context-token-estimator.test.ts +114 -74
  97. package/src/__tests__/context-window-manager.test.ts +269 -167
  98. package/src/__tests__/contradiction-checker.test.ts +161 -135
  99. package/src/__tests__/conversation-attention-store.test.ts +350 -290
  100. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  101. package/src/__tests__/conversation-pairing.test.ts +220 -113
  102. package/src/__tests__/conversation-store.test.ts +390 -235
  103. package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
  104. package/src/__tests__/credential-broker-server-use.test.ts +283 -243
  105. package/src/__tests__/credential-broker.test.ts +128 -74
  106. package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
  107. package/src/__tests__/credential-metadata-store.test.ts +360 -311
  108. package/src/__tests__/credential-policy-validate.test.ts +81 -65
  109. package/src/__tests__/credential-resolve.test.ts +212 -145
  110. package/src/__tests__/credential-security-e2e.test.ts +144 -103
  111. package/src/__tests__/credential-security-invariants.test.ts +253 -208
  112. package/src/__tests__/credential-selection.test.ts +254 -146
  113. package/src/__tests__/credential-vault-unit.test.ts +531 -341
  114. package/src/__tests__/credential-vault.test.ts +761 -484
  115. package/src/__tests__/daemon-assistant-events.test.ts +91 -66
  116. package/src/__tests__/daemon-lifecycle.test.ts +258 -190
  117. package/src/__tests__/daemon-server-session-init.test.ts +0 -1
  118. package/src/__tests__/date-context.test.ts +314 -249
  119. package/src/__tests__/db-migration-rollback.test.ts +259 -130
  120. package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
  121. package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
  122. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
  123. package/src/__tests__/dictation-mode-detection.test.ts +77 -55
  124. package/src/__tests__/dictation-profile-store.test.ts +70 -56
  125. package/src/__tests__/dictation-text-processing.test.ts +53 -35
  126. package/src/__tests__/diff.test.ts +102 -98
  127. package/src/__tests__/domain-normalize.test.ts +54 -54
  128. package/src/__tests__/domain-policy.test.ts +71 -55
  129. package/src/__tests__/dynamic-page-surface.test.ts +31 -33
  130. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
  131. package/src/__tests__/edit-engine.test.ts +56 -56
  132. package/src/__tests__/elevenlabs-client.test.ts +117 -91
  133. package/src/__tests__/elevenlabs-config.test.ts +32 -31
  134. package/src/__tests__/email-classifier.test.ts +15 -12
  135. package/src/__tests__/email-cli.test.ts +121 -108
  136. package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
  137. package/src/__tests__/encrypted-store.test.ts +180 -154
  138. package/src/__tests__/entity-extractor.test.ts +108 -87
  139. package/src/__tests__/entity-search.test.ts +664 -258
  140. package/src/__tests__/ephemeral-permissions.test.ts +224 -188
  141. package/src/__tests__/event-bus.test.ts +81 -77
  142. package/src/__tests__/extract-email.test.ts +29 -20
  143. package/src/__tests__/file-edit-tool.test.ts +62 -44
  144. package/src/__tests__/file-ops-service.test.ts +131 -114
  145. package/src/__tests__/file-read-tool.test.ts +48 -31
  146. package/src/__tests__/file-write-tool.test.ts +43 -37
  147. package/src/__tests__/filesystem-tools.test.ts +238 -209
  148. package/src/__tests__/followup-tools.test.ts +237 -162
  149. package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
  150. package/src/__tests__/frontmatter.test.ts +96 -81
  151. package/src/__tests__/fuzzy-match-property.test.ts +75 -81
  152. package/src/__tests__/fuzzy-match.test.ts +71 -65
  153. package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
  154. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  155. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  156. package/src/__tests__/gemini-image-service.test.ts +113 -100
  157. package/src/__tests__/gemini-provider.test.ts +297 -220
  158. package/src/__tests__/get-weather.test.ts +188 -114
  159. package/src/__tests__/gmail-integration.test.ts +0 -1
  160. package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
  161. package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
  162. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
  163. package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
  164. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
  165. package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
  166. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
  167. package/src/__tests__/guardian-action-store.test.ts +158 -109
  168. package/src/__tests__/guardian-action-sweep.test.ts +114 -100
  169. package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
  170. package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
  171. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
  172. package/src/__tests__/guardian-dispatch.test.ts +316 -256
  173. package/src/__tests__/guardian-grant-minting.test.ts +247 -178
  174. package/src/__tests__/guardian-outbound-http.test.ts +5 -3
  175. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
  176. package/src/__tests__/guardian-question-copy.test.ts +17 -17
  177. package/src/__tests__/guardian-question-mode.test.ts +134 -100
  178. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  179. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  180. package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
  181. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  182. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
  183. package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -1
  184. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
  185. package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
  186. package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
  187. package/src/__tests__/handlers-slack-config.test.ts +63 -54
  188. package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
  189. package/src/__tests__/handlers-telegram-config.test.ts +662 -329
  190. package/src/__tests__/handlers-twitter-config.test.ts +525 -298
  191. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -2
  192. package/src/__tests__/headless-browser-interactions.test.ts +444 -280
  193. package/src/__tests__/headless-browser-navigate.test.ts +116 -79
  194. package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
  195. package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
  196. package/src/__tests__/heartbeat-service.test.ts +76 -58
  197. package/src/__tests__/history-repair-observability.test.ts +14 -14
  198. package/src/__tests__/history-repair.test.ts +171 -167
  199. package/src/__tests__/home-base-bootstrap.test.ts +30 -27
  200. package/src/__tests__/hooks-blocking.test.ts +86 -37
  201. package/src/__tests__/hooks-cli.test.ts +104 -68
  202. package/src/__tests__/hooks-config.test.ts +81 -43
  203. package/src/__tests__/hooks-discovery.test.ts +106 -96
  204. package/src/__tests__/hooks-integration.test.ts +78 -72
  205. package/src/__tests__/hooks-manager.test.ts +99 -61
  206. package/src/__tests__/hooks-runner.test.ts +94 -71
  207. package/src/__tests__/hooks-settings.test.ts +69 -64
  208. package/src/__tests__/hooks-templates.test.ts +85 -54
  209. package/src/__tests__/hooks-ts-runner.test.ts +82 -45
  210. package/src/__tests__/hooks-watch.test.ts +32 -22
  211. package/src/__tests__/host-file-edit-tool.test.ts +190 -148
  212. package/src/__tests__/host-file-read-tool.test.ts +86 -63
  213. package/src/__tests__/host-file-write-tool.test.ts +98 -64
  214. package/src/__tests__/host-shell-tool.test.ts +342 -233
  215. package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
  216. package/src/__tests__/ingress-member-store.test.ts +163 -159
  217. package/src/__tests__/ingress-reconcile.test.ts +0 -1
  218. package/src/__tests__/ingress-routes-http.test.ts +441 -356
  219. package/src/__tests__/ingress-url-consistency.test.ts +125 -64
  220. package/src/__tests__/integration-status.test.ts +93 -73
  221. package/src/__tests__/intent-routing.test.ts +148 -118
  222. package/src/__tests__/invite-redemption-service.test.ts +163 -121
  223. package/src/__tests__/ipc-blob-store.test.ts +104 -91
  224. package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
  225. package/src/__tests__/ipc-contract.test.ts +24 -23
  226. package/src/__tests__/ipc-protocol.test.ts +52 -46
  227. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
  228. package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
  229. package/src/__tests__/ipc-validate.test.ts +240 -179
  230. package/src/__tests__/key-migration.test.ts +123 -90
  231. package/src/__tests__/keychain.test.ts +150 -123
  232. package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
  233. package/src/__tests__/llm-usage-store.test.ts +112 -87
  234. package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
  235. package/src/__tests__/managed-store.test.ts +411 -360
  236. package/src/__tests__/mcp-cli.test.ts +189 -123
  237. package/src/__tests__/mcp-health-check.test.ts +26 -21
  238. package/src/__tests__/media-generate-image.test.ts +122 -99
  239. package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
  240. package/src/__tests__/media-visibility-policy.test.ts +86 -38
  241. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
  242. package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
  243. package/src/__tests__/memory-query-builder.test.ts +32 -33
  244. package/src/__tests__/memory-recall-quality.test.ts +761 -407
  245. package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
  246. package/src/__tests__/memory-regressions.test.ts +3725 -2642
  247. package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
  248. package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
  249. package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
  250. package/src/__tests__/messaging-send-tool.test.ts +36 -29
  251. package/src/__tests__/migration-cli-flows.test.ts +69 -53
  252. package/src/__tests__/migration-ordering.test.ts +103 -86
  253. package/src/__tests__/mime-builder.test.ts +55 -32
  254. package/src/__tests__/mock-signup-server.test.ts +384 -246
  255. package/src/__tests__/model-intents.test.ts +61 -37
  256. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
  257. package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
  258. package/src/__tests__/non-member-access-request.test.ts +3 -2
  259. package/src/__tests__/notification-broadcaster.test.ts +99 -81
  260. package/src/__tests__/notification-decision-fallback.test.ts +223 -178
  261. package/src/__tests__/notification-decision-strategy.test.ts +375 -337
  262. package/src/__tests__/notification-deep-link.test.ts +67 -61
  263. package/src/__tests__/notification-guardian-path.test.ts +248 -206
  264. package/src/__tests__/notification-routing-intent.test.ts +166 -93
  265. package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
  266. package/src/__tests__/notification-thread-candidates.test.ts +64 -61
  267. package/src/__tests__/oauth-callback-registry.test.ts +40 -30
  268. package/src/__tests__/oauth-connect-handler.test.ts +109 -89
  269. package/src/__tests__/oauth-scope-policy.test.ts +63 -55
  270. package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
  271. package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
  272. package/src/__tests__/onboarding-template-contract.test.ts +93 -94
  273. package/src/__tests__/openai-provider.test.ts +366 -274
  274. package/src/__tests__/pairing-concurrent.test.ts +18 -12
  275. package/src/__tests__/pairing-routes.test.ts +45 -41
  276. package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
  277. package/src/__tests__/parser.test.ts +316 -226
  278. package/src/__tests__/path-classifier.test.ts +24 -25
  279. package/src/__tests__/path-policy.test.ts +187 -147
  280. package/src/__tests__/phone.test.ts +36 -36
  281. package/src/__tests__/platform-move-helper.test.ts +48 -40
  282. package/src/__tests__/platform-socket-path.test.ts +23 -24
  283. package/src/__tests__/platform-workspace-migration.test.ts +464 -414
  284. package/src/__tests__/platform.test.ts +61 -53
  285. package/src/__tests__/playbook-execution.test.ts +397 -265
  286. package/src/__tests__/playbook-tools.test.ts +267 -196
  287. package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
  288. package/src/__tests__/pricing.test.ts +316 -136
  289. package/src/__tests__/profile-compiler.test.ts +206 -188
  290. package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
  291. package/src/__tests__/provider-error-scenarios.test.ts +212 -158
  292. package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
  293. package/src/__tests__/provider-registry-ollama.test.ts +13 -9
  294. package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
  295. package/src/__tests__/proxy-approval-callback.test.ts +180 -119
  296. package/src/__tests__/public-ingress-urls.test.ts +112 -94
  297. package/src/__tests__/qdrant-manager.test.ts +147 -98
  298. package/src/__tests__/ratelimit.test.ts +152 -82
  299. package/src/__tests__/recording-handler.test.ts +273 -151
  300. package/src/__tests__/recording-intent-fallback.test.ts +94 -75
  301. package/src/__tests__/recording-intent-handler.test.ts +0 -1
  302. package/src/__tests__/recording-intent.test.ts +578 -379
  303. package/src/__tests__/recording-state-machine.test.ts +530 -316
  304. package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
  305. package/src/__tests__/recurrence-engine.test.ts +81 -41
  306. package/src/__tests__/recurrence-types.test.ts +63 -44
  307. package/src/__tests__/relay-server.test.ts +2131 -1602
  308. package/src/__tests__/reminder-store.test.ts +158 -80
  309. package/src/__tests__/reminder.test.ts +113 -109
  310. package/src/__tests__/remote-skill-policy.test.ts +96 -72
  311. package/src/__tests__/request-file-tool.test.ts +74 -67
  312. package/src/__tests__/response-tier.test.ts +131 -74
  313. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  314. package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
  315. package/src/__tests__/runtime-events-sse.test.ts +0 -1
  316. package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
  317. package/src/__tests__/sandbox-host-parity.test.ts +377 -301
  318. package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
  319. package/src/__tests__/schedule-store.test.ts +268 -205
  320. package/src/__tests__/schedule-tools.test.ts +702 -524
  321. package/src/__tests__/scheduler-recurrence.test.ts +240 -130
  322. package/src/__tests__/scoped-approval-grants.test.ts +258 -168
  323. package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
  324. package/src/__tests__/script-proxy-certs.test.ts +38 -35
  325. package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
  326. package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
  327. package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
  328. package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
  329. package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
  330. package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
  331. package/src/__tests__/script-proxy-policy.test.ts +308 -153
  332. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
  333. package/src/__tests__/script-proxy-router.test.ts +111 -77
  334. package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
  335. package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
  336. package/src/__tests__/secret-allowlist.test.ts +105 -90
  337. package/src/__tests__/secret-ingress-handler.test.ts +41 -30
  338. package/src/__tests__/secret-onetime-send.test.ts +67 -50
  339. package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
  340. package/src/__tests__/secret-response-routing.test.ts +50 -41
  341. package/src/__tests__/secret-scanner-executor.test.ts +152 -111
  342. package/src/__tests__/secret-scanner.test.ts +495 -413
  343. package/src/__tests__/secure-keys.test.ts +132 -121
  344. package/src/__tests__/send-endpoint-busy.test.ts +0 -1
  345. package/src/__tests__/send-notification-tool.test.ts +43 -42
  346. package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
  347. package/src/__tests__/sequence-store.test.ts +335 -167
  348. package/src/__tests__/server-history-render.test.ts +341 -202
  349. package/src/__tests__/session-abort-tool-results.test.ts +133 -70
  350. package/src/__tests__/session-confirmation-signals.test.ts +252 -160
  351. package/src/__tests__/session-conflict-gate.test.ts +775 -585
  352. package/src/__tests__/session-error.test.ts +222 -191
  353. package/src/__tests__/session-evictor.test.ts +79 -62
  354. package/src/__tests__/session-init.benchmark.test.ts +170 -108
  355. package/src/__tests__/session-load-history-repair.test.ts +273 -139
  356. package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
  357. package/src/__tests__/session-pre-run-repair.test.ts +106 -59
  358. package/src/__tests__/session-profile-injection.test.ts +198 -130
  359. package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
  360. package/src/__tests__/session-queue.test.ts +624 -321
  361. package/src/__tests__/session-runtime-assembly.test.ts +425 -329
  362. package/src/__tests__/session-runtime-workspace.test.ts +69 -61
  363. package/src/__tests__/session-skill-tools.test.ts +973 -678
  364. package/src/__tests__/session-slash-known.test.ts +185 -133
  365. package/src/__tests__/session-slash-queue.test.ts +147 -81
  366. package/src/__tests__/session-slash-unknown.test.ts +135 -90
  367. package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
  368. package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
  369. package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
  370. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
  371. package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
  372. package/src/__tests__/session-undo.test.ts +43 -30
  373. package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
  374. package/src/__tests__/session-workspace-injection.test.ts +245 -117
  375. package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
  376. package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
  377. package/src/__tests__/shell-credential-ref.test.ts +126 -90
  378. package/src/__tests__/shell-identity.test.ts +134 -111
  379. package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
  380. package/src/__tests__/shell-parser-property.test.ts +435 -288
  381. package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
  382. package/src/__tests__/size-guard.test.ts +42 -44
  383. package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
  384. package/src/__tests__/skill-feature-flags.test.ts +75 -47
  385. package/src/__tests__/skill-include-graph.test.ts +143 -148
  386. package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
  387. package/src/__tests__/skill-load-tool.test.ts +371 -199
  388. package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
  389. package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
  390. package/src/__tests__/skill-script-runner-host.test.ts +460 -250
  391. package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
  392. package/src/__tests__/skill-script-runner.test.ts +115 -74
  393. package/src/__tests__/skill-tool-factory.test.ts +140 -96
  394. package/src/__tests__/skill-tool-manifest.test.ts +306 -210
  395. package/src/__tests__/skill-version-hash.test.ts +70 -56
  396. package/src/__tests__/skills.test.ts +0 -1
  397. package/src/__tests__/slack-channel-config.test.ts +127 -84
  398. package/src/__tests__/slack-skill.test.ts +60 -47
  399. package/src/__tests__/slash-commands-catalog.test.ts +37 -31
  400. package/src/__tests__/slash-commands-parser.test.ts +71 -64
  401. package/src/__tests__/slash-commands-resolver.test.ts +143 -107
  402. package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
  403. package/src/__tests__/speaker-identification.test.ts +28 -25
  404. package/src/__tests__/starter-bundle.test.ts +27 -23
  405. package/src/__tests__/starter-task-flow.test.ts +67 -52
  406. package/src/__tests__/subagent-manager-notify.test.ts +154 -108
  407. package/src/__tests__/subagent-tools.test.ts +311 -270
  408. package/src/__tests__/subagent-types.test.ts +40 -40
  409. package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
  410. package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
  411. package/src/__tests__/swarm-orchestrator.test.ts +135 -101
  412. package/src/__tests__/swarm-plan-validator.test.ts +125 -73
  413. package/src/__tests__/swarm-recursion.test.ts +58 -46
  414. package/src/__tests__/swarm-router-planner.test.ts +99 -74
  415. package/src/__tests__/swarm-session-integration.test.ts +148 -91
  416. package/src/__tests__/swarm-tool.test.ts +65 -45
  417. package/src/__tests__/swarm-worker-backend.test.ts +59 -45
  418. package/src/__tests__/swarm-worker-runner.test.ts +133 -118
  419. package/src/__tests__/system-prompt.test.ts +290 -256
  420. package/src/__tests__/task-compiler.test.ts +176 -120
  421. package/src/__tests__/task-management-tools.test.ts +561 -456
  422. package/src/__tests__/task-memory-cleanup.test.ts +627 -362
  423. package/src/__tests__/task-runner.test.ts +117 -94
  424. package/src/__tests__/task-scheduler.test.ts +113 -84
  425. package/src/__tests__/task-tools.test.ts +349 -264
  426. package/src/__tests__/terminal-sandbox.test.ts +138 -108
  427. package/src/__tests__/terminal-tools.test.ts +350 -305
  428. package/src/__tests__/thread-seed-composer.test.ts +307 -180
  429. package/src/__tests__/tool-approval-handler.test.ts +238 -137
  430. package/src/__tests__/tool-audit-listener.test.ts +69 -69
  431. package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
  432. package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
  433. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
  434. package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
  435. package/src/__tests__/tool-executor-redaction.test.ts +112 -109
  436. package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
  437. package/src/__tests__/tool-executor.test.ts +1274 -674
  438. package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
  439. package/src/__tests__/tool-metrics-listener.test.ts +97 -85
  440. package/src/__tests__/tool-notification-listener.test.ts +42 -25
  441. package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
  442. package/src/__tests__/tool-policy.test.ts +44 -25
  443. package/src/__tests__/tool-profiling-listener.test.ts +99 -93
  444. package/src/__tests__/tool-result-truncation.test.ts +5 -4
  445. package/src/__tests__/tool-trace-listener.test.ts +131 -111
  446. package/src/__tests__/top-level-renderer.test.ts +62 -58
  447. package/src/__tests__/top-level-scanner.test.ts +68 -64
  448. package/src/__tests__/trace-emitter.test.ts +56 -56
  449. package/src/__tests__/trust-context-guards.test.ts +65 -65
  450. package/src/__tests__/trust-store.test.ts +1239 -806
  451. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  452. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  453. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  454. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  455. package/src/__tests__/trusted-contact-verification.test.ts +251 -231
  456. package/src/__tests__/turn-commit.test.ts +259 -200
  457. package/src/__tests__/twilio-provider.test.ts +140 -126
  458. package/src/__tests__/twilio-rest.test.ts +22 -18
  459. package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
  460. package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
  461. package/src/__tests__/twilio-routes.test.ts +0 -1
  462. package/src/__tests__/twitter-auth-handler.test.ts +184 -139
  463. package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
  464. package/src/__tests__/twitter-cli-routing.test.ts +146 -99
  465. package/src/__tests__/twitter-oauth-client.test.ts +82 -65
  466. package/src/__tests__/update-bulletin-format.test.ts +69 -66
  467. package/src/__tests__/update-bulletin-state.test.ts +66 -60
  468. package/src/__tests__/update-bulletin.test.ts +150 -114
  469. package/src/__tests__/update-template-contract.test.ts +15 -10
  470. package/src/__tests__/url-safety.test.ts +288 -265
  471. package/src/__tests__/user-reference.test.ts +32 -32
  472. package/src/__tests__/view-image-tool.test.ts +118 -96
  473. package/src/__tests__/voice-invite-redemption.test.ts +111 -106
  474. package/src/__tests__/voice-quality.test.ts +117 -102
  475. package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
  476. package/src/__tests__/voice-session-bridge.test.ts +351 -216
  477. package/src/__tests__/weather-skill-regression.test.ts +170 -120
  478. package/src/__tests__/web-fetch.test.ts +664 -526
  479. package/src/__tests__/web-search.test.ts +379 -213
  480. package/src/__tests__/work-item-output.test.ts +90 -53
  481. package/src/__tests__/workspace-git-service.test.ts +437 -356
  482. package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
  483. package/src/__tests__/workspace-lifecycle.test.ts +98 -64
  484. package/src/__tests__/workspace-policy.test.ts +139 -71
  485. package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
  486. package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
  487. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
  488. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
  489. package/src/config/bundled-skills/messaging/SKILL.md +4 -3
  490. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
  491. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
  492. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
  493. package/src/config/bundled-tool-registry.ts +2 -0
  494. package/src/config/env.ts +3 -4
  495. package/src/memory/db-connection.ts +16 -10
  496. package/src/messaging/providers/gmail/adapter.ts +10 -3
  497. package/src/messaging/providers/gmail/client.ts +280 -72
  498. package/src/runtime/auth/__tests__/context.test.ts +75 -65
  499. package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
  500. package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
  501. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
  502. package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
  503. package/src/runtime/auth/__tests__/policy.test.ts +9 -9
  504. package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
  505. package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
  506. package/src/runtime/auth/__tests__/subject.test.ts +54 -54
  507. package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
  508. package/src/runtime/auth/scopes.ts +3 -0
  509. package/src/runtime/auth/token-service.ts +4 -1
  510. package/src/runtime/auth/types.ts +2 -1
  511. package/src/runtime/http-server.ts +2 -1
  512. package/src/security/secure-keys.ts +103 -53
  513. package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
  514. package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
  515. package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
@@ -1,24 +1,38 @@
1
- import { execFileSync } from 'node:child_process';
2
- import { existsSync,mkdirSync, rmSync, writeFileSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
-
6
- import { afterAll, afterEach, beforeEach, describe, expect, test } from 'bun:test';
1
+ import { execFileSync } from "node:child_process";
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import {
6
+ afterAll,
7
+ afterEach,
8
+ beforeEach,
9
+ describe,
10
+ expect,
11
+ test,
12
+ } from "bun:test";
7
13
 
8
14
  import {
9
15
  _resetEnrichmentService,
10
16
  CommitEnrichmentService,
11
17
  getEnrichmentService,
12
- } from '../workspace/commit-message-enrichment-service.js';
13
- import type { CommitContext } from '../workspace/commit-message-provider.js';
14
- import { _resetGitServiceRegistry,WorkspaceGitService } from '../workspace/git-service.js';
18
+ } from "../workspace/commit-message-enrichment-service.js";
19
+ import type { CommitContext } from "../workspace/commit-message-provider.js";
20
+ import {
21
+ _resetGitServiceRegistry,
22
+ WorkspaceGitService,
23
+ } from "../workspace/git-service.js";
15
24
 
16
- describe('CommitEnrichmentService', () => {
25
+ describe("CommitEnrichmentService", () => {
17
26
  let testDir: string;
18
27
  let gitService: WorkspaceGitService;
19
28
 
20
29
  beforeEach(async () => {
21
- testDir = join(tmpdir(), `vellum-enrichment-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
30
+ testDir = join(
31
+ tmpdir(),
32
+ `vellum-enrichment-test-${Date.now()}-${Math.random()
33
+ .toString(36)
34
+ .slice(2)}`,
35
+ );
22
36
  mkdirSync(testDir, { recursive: true });
23
37
  _resetGitServiceRegistry();
24
38
  _resetEnrichmentService();
@@ -28,7 +42,11 @@ describe('CommitEnrichmentService', () => {
28
42
  });
29
43
 
30
44
  afterEach(async () => {
31
- try { await getEnrichmentService().shutdown(); } catch { /* ignore */ }
45
+ try {
46
+ await getEnrichmentService().shutdown();
47
+ } catch {
48
+ /* ignore */
49
+ }
32
50
  _resetEnrichmentService();
33
51
  if (existsSync(testDir)) {
34
52
  rmSync(testDir, { recursive: true, force: true });
@@ -36,39 +54,48 @@ describe('CommitEnrichmentService', () => {
36
54
  });
37
55
 
38
56
  afterAll(async () => {
39
- try { await getEnrichmentService().shutdown(); } catch { /* ignore */ }
57
+ try {
58
+ await getEnrichmentService().shutdown();
59
+ } catch {
60
+ /* ignore */
61
+ }
40
62
  _resetEnrichmentService();
41
63
  });
42
64
 
43
65
  function makeContext(overrides?: Partial<CommitContext>): CommitContext {
44
66
  return {
45
67
  workspaceDir: testDir,
46
- trigger: 'turn',
47
- sessionId: 'sess_test',
68
+ trigger: "turn",
69
+ sessionId: "sess_test",
48
70
  turnNumber: 1,
49
- changedFiles: ['file.txt'],
71
+ changedFiles: ["file.txt"],
50
72
  timestampMs: Date.now(),
51
73
  ...overrides,
52
74
  };
53
75
  }
54
76
 
55
77
  async function createCommit(): Promise<string> {
56
- writeFileSync(join(testDir, `file-${Date.now()}.txt`), 'content');
57
- await gitService.commitChanges('test commit');
78
+ writeFileSync(join(testDir, `file-${Date.now()}.txt`), "content");
79
+ await gitService.commitChanges("test commit");
58
80
  return await gitService.getHeadHash();
59
81
  }
60
82
 
61
- async function waitForDrain(service: CommitEnrichmentService, timeoutMs = 5000): Promise<void> {
83
+ async function waitForDrain(
84
+ service: CommitEnrichmentService,
85
+ timeoutMs = 5000,
86
+ ): Promise<void> {
62
87
  const started = Date.now();
63
88
  while (service._getQueueSize() > 0 || service._getActiveWorkers() > 0) {
64
89
  if (Date.now() - started > timeoutMs) {
65
- throw new Error(`Timed out waiting for enrichment queue to drain after ${timeoutMs}ms`);
90
+ throw new Error(
91
+ `Timed out waiting for enrichment queue to drain after ${timeoutMs}ms`,
92
+ );
66
93
  }
67
- await new Promise(resolve => setTimeout(resolve, 50));
94
+ await new Promise((resolve) => setTimeout(resolve, 50));
68
95
  }
69
96
  }
70
97
 
71
- test('enqueue and execute writes git note on success', async () => {
98
+ test("enqueue and execute writes git note on success", async () => {
72
99
  const commitHash = await createCommit();
73
100
  const service = new CommitEnrichmentService({
74
101
  maxQueueSize: 10,
@@ -88,21 +115,25 @@ describe('CommitEnrichmentService', () => {
88
115
  await service.shutdown();
89
116
 
90
117
  // Verify git note was written
91
- const noteContent = execFileSync('git', ['notes', '--ref=vellum', 'show', commitHash], {
92
- cwd: testDir,
93
- encoding: 'utf-8',
94
- });
118
+ const noteContent = execFileSync(
119
+ "git",
120
+ ["notes", "--ref=vellum", "show", commitHash],
121
+ {
122
+ cwd: testDir,
123
+ encoding: "utf-8",
124
+ },
125
+ );
95
126
 
96
127
  const note = JSON.parse(noteContent);
97
128
  expect(note.enriched).toBe(true);
98
- expect(note.trigger).toBe('turn');
99
- expect(note.sessionId).toBe('sess_test');
129
+ expect(note.trigger).toBe("turn");
130
+ expect(note.sessionId).toBe("sess_test");
100
131
  expect(note.turnNumber).toBe(1);
101
132
  expect(note.filesChanged).toBe(1);
102
133
  expect(service._getSucceededCount()).toBe(1);
103
134
  });
104
135
 
105
- test('queue overflow drops oldest job', async () => {
136
+ test("queue overflow drops oldest job", async () => {
106
137
  const service = new CommitEnrichmentService({
107
138
  maxQueueSize: 2,
108
139
  maxConcurrency: 1,
@@ -116,9 +147,24 @@ describe('CommitEnrichmentService', () => {
116
147
 
117
148
  // Enqueue 3 jobs — hash1 starts immediately (active worker),
118
149
  // hash2 goes to queue (size=1), hash3 goes to queue (size=2), no overflow drop.
119
- service.enqueue({ workspaceDir: testDir, commitHash: hash1, context: makeContext(), gitService });
120
- service.enqueue({ workspaceDir: testDir, commitHash: hash2, context: makeContext(), gitService });
121
- service.enqueue({ workspaceDir: testDir, commitHash: hash3, context: makeContext(), gitService });
150
+ service.enqueue({
151
+ workspaceDir: testDir,
152
+ commitHash: hash1,
153
+ context: makeContext(),
154
+ gitService,
155
+ });
156
+ service.enqueue({
157
+ workspaceDir: testDir,
158
+ commitHash: hash2,
159
+ context: makeContext(),
160
+ gitService,
161
+ });
162
+ service.enqueue({
163
+ workspaceDir: testDir,
164
+ commitHash: hash3,
165
+ context: makeContext(),
166
+ gitService,
167
+ });
122
168
 
123
169
  // No overflow drops — queue size 2 can hold 2 pending while 1 is active
124
170
  expect(service._getDroppedCount()).toBe(0);
@@ -130,7 +176,7 @@ describe('CommitEnrichmentService', () => {
130
176
  expect(service._getSucceededCount()).toBe(1);
131
177
  });
132
178
 
133
- test('queue overflow actually drops when truly full', async () => {
179
+ test("queue overflow actually drops when truly full", async () => {
134
180
  // Create a service where the worker is slow
135
181
  const service = new CommitEnrichmentService({
136
182
  maxQueueSize: 1,
@@ -146,16 +192,31 @@ describe('CommitEnrichmentService', () => {
146
192
  // hash1 starts processing immediately (active worker = 1, queue empty)
147
193
  // hash2 goes to queue (queue size = 1)
148
194
  // hash3 tries to go to queue but it's full → drops hash2, adds hash3
149
- service.enqueue({ workspaceDir: testDir, commitHash: hash1, context: makeContext(), gitService });
150
- service.enqueue({ workspaceDir: testDir, commitHash: hash2, context: makeContext(), gitService });
151
- service.enqueue({ workspaceDir: testDir, commitHash: hash3, context: makeContext(), gitService });
195
+ service.enqueue({
196
+ workspaceDir: testDir,
197
+ commitHash: hash1,
198
+ context: makeContext(),
199
+ gitService,
200
+ });
201
+ service.enqueue({
202
+ workspaceDir: testDir,
203
+ commitHash: hash2,
204
+ context: makeContext(),
205
+ gitService,
206
+ });
207
+ service.enqueue({
208
+ workspaceDir: testDir,
209
+ commitHash: hash3,
210
+ context: makeContext(),
211
+ gitService,
212
+ });
152
213
 
153
214
  expect(service._getDroppedCount()).toBe(1);
154
215
 
155
216
  await service.shutdown();
156
217
  });
157
218
 
158
- test('fire-and-forget enqueue does not block caller', async () => {
219
+ test("fire-and-forget enqueue does not block caller", async () => {
159
220
  const commitHash = await createCommit();
160
221
  const service = new CommitEnrichmentService({
161
222
  maxQueueSize: 10,
@@ -179,7 +240,7 @@ describe('CommitEnrichmentService', () => {
179
240
  await service.shutdown();
180
241
  });
181
242
 
182
- test('graceful shutdown drains in-flight and discards pending', async () => {
243
+ test("graceful shutdown drains in-flight and discards pending", async () => {
183
244
  const hash1 = await createCommit();
184
245
  const hash2 = await createCommit();
185
246
 
@@ -190,8 +251,18 @@ describe('CommitEnrichmentService', () => {
190
251
  maxRetries: 0,
191
252
  });
192
253
 
193
- service.enqueue({ workspaceDir: testDir, commitHash: hash1, context: makeContext(), gitService });
194
- service.enqueue({ workspaceDir: testDir, commitHash: hash2, context: makeContext(), gitService });
254
+ service.enqueue({
255
+ workspaceDir: testDir,
256
+ commitHash: hash1,
257
+ context: makeContext(),
258
+ gitService,
259
+ });
260
+ service.enqueue({
261
+ workspaceDir: testDir,
262
+ commitHash: hash2,
263
+ context: makeContext(),
264
+ gitService,
265
+ });
195
266
 
196
267
  // Shutdown should complete without hanging
197
268
  await service.shutdown();
@@ -203,7 +274,7 @@ describe('CommitEnrichmentService', () => {
203
274
  expect(service._getQueueSize()).toBe(0);
204
275
  });
205
276
 
206
- test('shutdown discards all pending jobs and counts them as dropped', async () => {
277
+ test("shutdown discards all pending jobs and counts them as dropped", async () => {
207
278
  // Use maxConcurrency 1 so only one job starts processing; the rest stay pending.
208
279
  const hashes: string[] = [];
209
280
  for (let i = 0; i < 5; i++) {
@@ -218,7 +289,12 @@ describe('CommitEnrichmentService', () => {
218
289
  });
219
290
 
220
291
  for (const hash of hashes) {
221
- service.enqueue({ workspaceDir: testDir, commitHash: hash, context: makeContext(), gitService });
292
+ service.enqueue({
293
+ workspaceDir: testDir,
294
+ commitHash: hash,
295
+ context: makeContext(),
296
+ gitService,
297
+ });
222
298
  }
223
299
 
224
300
  // First job is in-flight, remaining 4 are pending
@@ -229,7 +305,7 @@ describe('CommitEnrichmentService', () => {
229
305
  expect(service._getDroppedCount()).toBe(4);
230
306
  });
231
307
 
232
- test('shutdown does not cause concurrency spike', async () => {
308
+ test("shutdown does not cause concurrency spike", async () => {
233
309
  const hashes: string[] = [];
234
310
  for (let i = 0; i < 3; i++) {
235
311
  hashes.push(await createCommit());
@@ -243,7 +319,12 @@ describe('CommitEnrichmentService', () => {
243
319
  });
244
320
 
245
321
  for (const hash of hashes) {
246
- service.enqueue({ workspaceDir: testDir, commitHash: hash, context: makeContext(), gitService });
322
+ service.enqueue({
323
+ workspaceDir: testDir,
324
+ commitHash: hash,
325
+ context: makeContext(),
326
+ gitService,
327
+ });
247
328
  }
248
329
 
249
330
  await service.shutdown();
@@ -252,7 +333,7 @@ describe('CommitEnrichmentService', () => {
252
333
  expect(service._getActiveWorkers()).toBe(0);
253
334
  });
254
335
 
255
- test('discards jobs enqueued after shutdown', async () => {
336
+ test("discards jobs enqueued after shutdown", async () => {
256
337
  const commitHash = await createCommit();
257
338
  const service = new CommitEnrichmentService({
258
339
  maxQueueSize: 10,
@@ -275,7 +356,7 @@ describe('CommitEnrichmentService', () => {
275
356
  expect(service._getSucceededCount()).toBe(0);
276
357
  });
277
358
 
278
- test('multiple successful enrichments write separate git notes', async () => {
359
+ test("multiple successful enrichments write separate git notes", async () => {
279
360
  const hash1 = await createCommit();
280
361
  const hash2 = await createCommit();
281
362
 
@@ -304,19 +385,25 @@ describe('CommitEnrichmentService', () => {
304
385
  await service.shutdown();
305
386
 
306
387
  // Both notes should exist
307
- const note1 = JSON.parse(execFileSync('git', ['notes', '--ref=vellum', 'show', hash1], {
308
- cwd: testDir, encoding: 'utf-8',
309
- }));
310
- const note2 = JSON.parse(execFileSync('git', ['notes', '--ref=vellum', 'show', hash2], {
311
- cwd: testDir, encoding: 'utf-8',
312
- }));
388
+ const note1 = JSON.parse(
389
+ execFileSync("git", ["notes", "--ref=vellum", "show", hash1], {
390
+ cwd: testDir,
391
+ encoding: "utf-8",
392
+ }),
393
+ );
394
+ const note2 = JSON.parse(
395
+ execFileSync("git", ["notes", "--ref=vellum", "show", hash2], {
396
+ cwd: testDir,
397
+ encoding: "utf-8",
398
+ }),
399
+ );
313
400
 
314
401
  expect(note1.turnNumber).toBe(1);
315
402
  expect(note2.turnNumber).toBe(2);
316
403
  expect(service._getSucceededCount()).toBe(2);
317
404
  });
318
405
 
319
- test('job timeout triggers retry with backoff then fails after max retries', async () => {
406
+ test("job timeout triggers retry with backoff then fails after max retries", async () => {
320
407
  const service = new CommitEnrichmentService({
321
408
  maxQueueSize: 10,
322
409
  maxConcurrency: 1,
@@ -349,7 +436,7 @@ describe('CommitEnrichmentService', () => {
349
436
  expect(service._getSucceededCount()).toBe(0);
350
437
  }, 15000); // Allow up to 15s for backoff delays
351
438
 
352
- test('queue overflow drop behavior is deterministic', async () => {
439
+ test("queue overflow drop behavior is deterministic", async () => {
353
440
  // With maxQueueSize=2 and maxConcurrency=1:
354
441
  // - Job A starts processing immediately (in-flight)
355
442
  // - Job B enters queue (size=1)
@@ -369,17 +456,42 @@ describe('CommitEnrichmentService', () => {
369
456
  const hashD = await createCommit();
370
457
  const hashE = await createCommit();
371
458
 
372
- service.enqueue({ workspaceDir: testDir, commitHash: hashA, context: makeContext({ turnNumber: 1 }), gitService });
373
- service.enqueue({ workspaceDir: testDir, commitHash: hashB, context: makeContext({ turnNumber: 2 }), gitService });
374
- service.enqueue({ workspaceDir: testDir, commitHash: hashC, context: makeContext({ turnNumber: 3 }), gitService });
459
+ service.enqueue({
460
+ workspaceDir: testDir,
461
+ commitHash: hashA,
462
+ context: makeContext({ turnNumber: 1 }),
463
+ gitService,
464
+ });
465
+ service.enqueue({
466
+ workspaceDir: testDir,
467
+ commitHash: hashB,
468
+ context: makeContext({ turnNumber: 2 }),
469
+ gitService,
470
+ });
471
+ service.enqueue({
472
+ workspaceDir: testDir,
473
+ commitHash: hashC,
474
+ context: makeContext({ turnNumber: 3 }),
475
+ gitService,
476
+ });
375
477
  // No drops yet: A is in-flight, B and C in queue (size=2)
376
478
  expect(service._getDroppedCount()).toBe(0);
377
479
 
378
- service.enqueue({ workspaceDir: testDir, commitHash: hashD, context: makeContext({ turnNumber: 4 }), gitService });
480
+ service.enqueue({
481
+ workspaceDir: testDir,
482
+ commitHash: hashD,
483
+ context: makeContext({ turnNumber: 4 }),
484
+ gitService,
485
+ });
379
486
  // Queue was full (2), so oldest (B) was dropped
380
487
  expect(service._getDroppedCount()).toBe(1);
381
488
 
382
- service.enqueue({ workspaceDir: testDir, commitHash: hashE, context: makeContext({ turnNumber: 5 }), gitService });
489
+ service.enqueue({
490
+ workspaceDir: testDir,
491
+ commitHash: hashE,
492
+ context: makeContext({ turnNumber: 5 }),
493
+ gitService,
494
+ });
383
495
  // Queue was full again (2), so oldest (C) was dropped
384
496
  expect(service._getDroppedCount()).toBe(2);
385
497
 
@@ -394,7 +506,7 @@ describe('CommitEnrichmentService', () => {
394
506
  expect(service._getDroppedCount()).toBe(4);
395
507
  });
396
508
 
397
- test('timed-out enrichment work is cancelled via AbortSignal', async () => {
509
+ test("timed-out enrichment work is cancelled via AbortSignal", async () => {
398
510
  // Track whether the slow enrichment work actually ran to completion
399
511
  let enrichmentCompleted = false;
400
512
  const commitHash = await createCommit();
@@ -411,17 +523,25 @@ describe('CommitEnrichmentService', () => {
411
523
  // child process on abort. This mock replicates that behavior by rejecting
412
524
  // when the signal fires.
413
525
  const originalWriteNote = gitService.writeNote.bind(gitService);
414
- gitService.writeNote = async (_hash: string, _note: string, signal?: AbortSignal) => {
526
+ gitService.writeNote = async (
527
+ _hash: string,
528
+ _note: string,
529
+ signal?: AbortSignal,
530
+ ) => {
415
531
  // Simulate slow work that is cancellable via AbortSignal
416
532
  await new Promise<void>((resolve, reject) => {
417
533
  const timer = setTimeout(() => {
418
534
  enrichmentCompleted = true;
419
535
  resolve();
420
536
  }, 2000);
421
- signal?.addEventListener('abort', () => {
422
- clearTimeout(timer);
423
- reject(new Error('aborted'));
424
- }, { once: true });
537
+ signal?.addEventListener(
538
+ "abort",
539
+ () => {
540
+ clearTimeout(timer);
541
+ reject(new Error("aborted"));
542
+ },
543
+ { once: true },
544
+ );
425
545
  });
426
546
  };
427
547
 
@@ -438,7 +558,7 @@ describe('CommitEnrichmentService', () => {
438
558
  // Allow any zombie work to settle — if abort didn't work, the 2s timer
439
559
  // would still be running and would set enrichmentCompleted=true. Wait
440
560
  // longer than the 2000ms mock delay to reliably catch the regression.
441
- await new Promise(resolve => setTimeout(resolve, 2500));
561
+ await new Promise((resolve) => setTimeout(resolve, 2500));
442
562
 
443
563
  // The job should have timed out and been counted as failed
444
564
  expect(service._getFailedCount()).toBe(1);
@@ -450,7 +570,7 @@ describe('CommitEnrichmentService', () => {
450
570
  gitService.writeNote = originalWriteNote;
451
571
  });
452
572
 
453
- test('shutdown does not hang on timed-out jobs', async () => {
573
+ test("shutdown does not hang on timed-out jobs", async () => {
454
574
  const commitHash = await createCommit();
455
575
 
456
576
  const service = new CommitEnrichmentService({
@@ -463,13 +583,21 @@ describe('CommitEnrichmentService', () => {
463
583
  // Make writeNote artificially slow so the job will always time out.
464
584
  // The mock respects the abort signal so the subprocess is killed on timeout.
465
585
  const originalWriteNote = gitService.writeNote.bind(gitService);
466
- gitService.writeNote = async (_hash: string, _note: string, signal?: AbortSignal) => {
586
+ gitService.writeNote = async (
587
+ _hash: string,
588
+ _note: string,
589
+ signal?: AbortSignal,
590
+ ) => {
467
591
  await new Promise<void>((resolve, reject) => {
468
592
  const timer = setTimeout(resolve, 5000);
469
- signal?.addEventListener('abort', () => {
470
- clearTimeout(timer);
471
- reject(new Error('aborted'));
472
- }, { once: true });
593
+ signal?.addEventListener(
594
+ "abort",
595
+ () => {
596
+ clearTimeout(timer);
597
+ reject(new Error("aborted"));
598
+ },
599
+ { once: true },
600
+ );
473
601
  });
474
602
  };
475
603
 
@@ -492,7 +620,7 @@ describe('CommitEnrichmentService', () => {
492
620
  gitService.writeNote = originalWriteNote;
493
621
  }, 10000);
494
622
 
495
- test('abort signal is triggered on non-timeout errors before retry', async () => {
623
+ test("abort signal is triggered on non-timeout errors before retry", async () => {
496
624
  const commitHash = await createCommit();
497
625
 
498
626
  const service = new CommitEnrichmentService({
@@ -508,7 +636,7 @@ describe('CommitEnrichmentService', () => {
508
636
  // Set up a listener on the abort controller's signal to track abortion.
509
637
  // We access the signal indirectly by throwing, which triggers the catch
510
638
  // block in executeJob where controller.abort() is called.
511
- throw new Error('Simulated writeNote failure');
639
+ throw new Error("Simulated writeNote failure");
512
640
  };
513
641
 
514
642
  service.enqueue({
@@ -528,7 +656,7 @@ describe('CommitEnrichmentService', () => {
528
656
  gitService.writeNote = originalWriteNote;
529
657
  });
530
658
 
531
- test('enqueue is fire-and-forget and never throws even when called rapidly', async () => {
659
+ test("enqueue is fire-and-forget and never throws even when called rapidly", async () => {
532
660
  const service = new CommitEnrichmentService({
533
661
  maxQueueSize: 3,
534
662
  maxConcurrency: 1,
@@ -8,33 +8,33 @@
8
8
  * - summary call count within expected range
9
9
  * - severe pressure overriding cooldown
10
10
  */
11
- import { describe, expect, mock, test } from 'bun:test';
11
+ import { describe, expect, mock, test } from "bun:test";
12
12
 
13
- import { DEFAULT_CONFIG } from '../config/defaults.js';
14
- import { estimatePromptTokens } from '../context/token-estimator.js';
15
- import { ContextWindowManager } from '../context/window-manager.js';
16
- import type { Message, Provider } from '../providers/types.js';
13
+ import { DEFAULT_CONFIG } from "../config/defaults.js";
14
+ import { estimatePromptTokens } from "../context/token-estimator.js";
15
+ import { ContextWindowManager } from "../context/window-manager.js";
16
+ import type { Message, Provider } from "../providers/types.js";
17
17
 
18
- mock.module('../util/logger.js', () => ({
18
+ mock.module("../util/logger.js", () => ({
19
19
  getLogger: () =>
20
20
  new Proxy({} as Record<string, unknown>, { get: () => () => {} }),
21
21
  }));
22
22
 
23
23
  function makeSummaryProvider(counter: { calls: number }): Provider {
24
24
  return {
25
- name: 'mock',
25
+ name: "mock",
26
26
  async sendMessage() {
27
27
  counter.calls += 1;
28
28
  return {
29
29
  content: [
30
30
  {
31
- type: 'text',
31
+ type: "text",
32
32
  text: `## Goals\n- Preserve state\n## Constraints\n- Keep PRs small\n## Decisions\n- Call ${counter.calls}`,
33
33
  },
34
34
  ],
35
- model: 'mock-model',
35
+ model: "mock-model",
36
36
  usage: { inputTokens: 420, outputTokens: 85 },
37
- stopReason: 'end_turn',
37
+ stopReason: "end_turn",
38
38
  };
39
39
  },
40
40
  };
@@ -44,19 +44,19 @@ function makeLongMessages(turns: number): Message[] {
44
44
  const rows: Message[] = [];
45
45
  for (let i = 0; i < turns; i++) {
46
46
  rows.push({
47
- role: 'user',
47
+ role: "user",
48
48
  content: [
49
49
  {
50
- type: 'text',
50
+ type: "text",
51
51
  text: `[U${i}] User message with enough content to estimate tokens. Topic ${i % 9}.`,
52
52
  },
53
53
  ],
54
54
  });
55
55
  rows.push({
56
- role: 'assistant',
56
+ role: "assistant",
57
57
  content: [
58
58
  {
59
- type: 'text',
59
+ type: "text",
60
60
  text: `[A${i}] Assistant response with relevant content. Result ${i % 7}.`,
61
61
  },
62
62
  ],
@@ -76,19 +76,21 @@ function makeConfig() {
76
76
  };
77
77
  }
78
78
 
79
- describe('Compaction benchmark', () => {
80
- test('compaction with mock provider completes under 500ms', async () => {
79
+ describe("Compaction benchmark", () => {
80
+ test("compaction with mock provider completes under 500ms", async () => {
81
81
  const counter = { calls: 0 };
82
82
  const provider = makeSummaryProvider(counter);
83
83
  const config = makeConfig();
84
- const manager = new ContextWindowManager(provider, 'system prompt', config);
84
+ const manager = new ContextWindowManager(provider, "system prompt", config);
85
85
 
86
86
  // 90 turns = 180 messages, well above 60% of 6000 = 3600 threshold
87
87
  const messages = makeLongMessages(90);
88
- const before = estimatePromptTokens(messages, 'system prompt', {
89
- providerName: 'mock',
88
+ const before = estimatePromptTokens(messages, "system prompt", {
89
+ providerName: "mock",
90
90
  });
91
- expect(before).toBeGreaterThan(config.maxInputTokens * config.compactThreshold);
91
+ expect(before).toBeGreaterThan(
92
+ config.maxInputTokens * config.compactThreshold,
93
+ );
92
94
 
93
95
  const start = performance.now();
94
96
  const result = await manager.maybeCompact(messages);
@@ -98,11 +100,11 @@ describe('Compaction benchmark', () => {
98
100
  expect(elapsed).toBeLessThan(500);
99
101
  });
100
102
 
101
- test('below-threshold check returns in under 50ms (no-op)', async () => {
103
+ test("below-threshold check returns in under 50ms (no-op)", async () => {
102
104
  const counter = { calls: 0 };
103
105
  const provider = makeSummaryProvider(counter);
104
106
  const config = makeConfig();
105
- const manager = new ContextWindowManager(provider, 'system prompt', config);
107
+ const manager = new ContextWindowManager(provider, "system prompt", config);
106
108
 
107
109
  // 3 turns = 6 messages, well below threshold
108
110
  const messages = makeLongMessages(3);
@@ -112,16 +114,16 @@ describe('Compaction benchmark', () => {
112
114
  const elapsed = performance.now() - start;
113
115
 
114
116
  expect(result.compacted).toBe(false);
115
- expect(result.reason).toBe('below compaction threshold');
117
+ expect(result.reason).toBe("below compaction threshold");
116
118
  expect(elapsed).toBeLessThan(50);
117
119
  expect(counter.calls).toBe(0);
118
120
  });
119
121
 
120
- test('token reduction ratio exceeds 30% after compaction', async () => {
122
+ test("token reduction ratio exceeds 30% after compaction", async () => {
121
123
  const counter = { calls: 0 };
122
124
  const provider = makeSummaryProvider(counter);
123
125
  const config = makeConfig();
124
- const manager = new ContextWindowManager(provider, 'system prompt', config);
126
+ const manager = new ContextWindowManager(provider, "system prompt", config);
125
127
 
126
128
  const messages = makeLongMessages(90);
127
129
  const result = await manager.maybeCompact(messages);
@@ -133,11 +135,11 @@ describe('Compaction benchmark', () => {
133
135
  expect(reductionRatio).toBeGreaterThan(0.3);
134
136
  });
135
137
 
136
- test('summary calls fall within 2-6 range', async () => {
138
+ test("summary calls fall within 2-6 range", async () => {
137
139
  const counter = { calls: 0 };
138
140
  const provider = makeSummaryProvider(counter);
139
141
  const config = makeConfig();
140
- const manager = new ContextWindowManager(provider, 'system prompt', config);
142
+ const manager = new ContextWindowManager(provider, "system prompt", config);
141
143
 
142
144
  const messages = makeLongMessages(90);
143
145
  const result = await manager.maybeCompact(messages);
@@ -148,7 +150,7 @@ describe('Compaction benchmark', () => {
148
150
  expect(result.summaryCalls).toBe(counter.calls);
149
151
  });
150
152
 
151
- test('severe pressure triggers compaction even during cooldown', async () => {
153
+ test("severe pressure triggers compaction even during cooldown", async () => {
152
154
  const counter = { calls: 0 };
153
155
  const provider = makeSummaryProvider(counter);
154
156
  // Use a tighter maxInputTokens so 90 turns exceeds the 95% severe threshold
@@ -157,11 +159,11 @@ describe('Compaction benchmark', () => {
157
159
  maxInputTokens: 4000,
158
160
  targetInputTokens: 2000,
159
161
  };
160
- const manager = new ContextWindowManager(provider, 'system prompt', config);
162
+ const manager = new ContextWindowManager(provider, "system prompt", config);
161
163
 
162
164
  const messages = makeLongMessages(90);
163
- const estimated = estimatePromptTokens(messages, 'system prompt', {
164
- providerName: 'mock',
165
+ const estimated = estimatePromptTokens(messages, "system prompt", {
166
+ providerName: "mock",
165
167
  });
166
168
  expect(estimated).toBeGreaterThan(config.maxInputTokens * 0.95);
167
169