@vellumai/assistant 0.4.17 → 0.4.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (528) hide show
  1. package/docs/runbook-trusted-contacts.md +5 -3
  2. package/eslint.config.mjs +2 -2
  3. package/package.json +1 -1
  4. package/src/__tests__/access-request-decision.test.ts +128 -120
  5. package/src/__tests__/account-registry.test.ts +121 -110
  6. package/src/__tests__/active-skill-tools.test.ts +200 -172
  7. package/src/__tests__/actor-token-service.test.ts +341 -274
  8. package/src/__tests__/agent-loop-thinking.test.ts +28 -19
  9. package/src/__tests__/agent-loop.test.ts +798 -378
  10. package/src/__tests__/anthropic-provider.test.ts +405 -247
  11. package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
  12. package/src/__tests__/app-bundler.test.ts +112 -79
  13. package/src/__tests__/app-executors.test.ts +205 -178
  14. package/src/__tests__/app-git-history.test.ts +90 -73
  15. package/src/__tests__/app-git-service.test.ts +67 -53
  16. package/src/__tests__/app-open-proxy.test.ts +29 -25
  17. package/src/__tests__/approval-conversation-turn.test.ts +100 -81
  18. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
  19. package/src/__tests__/approval-message-composer.test.ts +119 -119
  20. package/src/__tests__/approval-primitive.test.ts +264 -233
  21. package/src/__tests__/approval-routes-http.test.ts +4 -3
  22. package/src/__tests__/asset-materialize-tool.test.ts +250 -178
  23. package/src/__tests__/asset-search-tool.test.ts +251 -191
  24. package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
  25. package/src/__tests__/assistant-attachments.test.ts +254 -186
  26. package/src/__tests__/assistant-event-hub.test.ts +105 -63
  27. package/src/__tests__/assistant-event.test.ts +66 -58
  28. package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
  29. package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
  30. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
  31. package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
  32. package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
  33. package/src/__tests__/attachments-store.test.ts +240 -183
  34. package/src/__tests__/attachments.test.ts +70 -62
  35. package/src/__tests__/audit-log-rotation.test.ts +50 -35
  36. package/src/__tests__/browser-fill-credential.test.ts +169 -101
  37. package/src/__tests__/browser-manager.test.ts +97 -75
  38. package/src/__tests__/browser-runtime-check.test.ts +16 -15
  39. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
  40. package/src/__tests__/browser-skill-endstate.test.ts +97 -72
  41. package/src/__tests__/bundle-scanner.test.ts +47 -22
  42. package/src/__tests__/bundled-asset.test.ts +74 -47
  43. package/src/__tests__/call-constants.test.ts +19 -19
  44. package/src/__tests__/call-controller.test.ts +0 -1
  45. package/src/__tests__/call-conversation-messages.test.ts +90 -65
  46. package/src/__tests__/call-domain.test.ts +149 -121
  47. package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
  48. package/src/__tests__/call-pointer-messages.test.ts +213 -154
  49. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
  50. package/src/__tests__/call-recovery.test.ts +232 -212
  51. package/src/__tests__/call-routes-http.test.ts +0 -1
  52. package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
  53. package/src/__tests__/call-state-machine.test.ts +62 -51
  54. package/src/__tests__/call-state.test.ts +89 -75
  55. package/src/__tests__/call-store.test.ts +387 -316
  56. package/src/__tests__/callback-handoff-copy.test.ts +84 -82
  57. package/src/__tests__/canonical-guardian-store.test.ts +331 -280
  58. package/src/__tests__/channel-approval-routes.test.ts +1643 -1115
  59. package/src/__tests__/channel-approval.test.ts +139 -137
  60. package/src/__tests__/channel-approvals.test.ts +7 -2
  61. package/src/__tests__/channel-delivery-store.test.ts +232 -194
  62. package/src/__tests__/channel-guardian.test.ts +5 -3
  63. package/src/__tests__/channel-invite-transport.test.ts +107 -92
  64. package/src/__tests__/channel-policy.test.ts +42 -38
  65. package/src/__tests__/channel-readiness-service.test.ts +119 -102
  66. package/src/__tests__/channel-reply-delivery.test.ts +147 -118
  67. package/src/__tests__/channel-retry-sweep.test.ts +153 -110
  68. package/src/__tests__/checker.test.ts +3309 -1850
  69. package/src/__tests__/clarification-resolver.test.ts +91 -79
  70. package/src/__tests__/classifier.test.ts +64 -54
  71. package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
  72. package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
  73. package/src/__tests__/clawhub.test.ts +92 -82
  74. package/src/__tests__/cli.test.ts +30 -30
  75. package/src/__tests__/clipboard.test.ts +53 -46
  76. package/src/__tests__/commit-guarantee.test.ts +59 -52
  77. package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
  78. package/src/__tests__/compaction.benchmark.test.ts +33 -31
  79. package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
  80. package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
  81. package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
  82. package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
  83. package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
  84. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
  85. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
  86. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
  87. package/src/__tests__/computer-use-tools.test.ts +121 -98
  88. package/src/__tests__/config-schema.test.ts +443 -347
  89. package/src/__tests__/config-watcher.test.ts +96 -81
  90. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
  91. package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
  92. package/src/__tests__/conflict-policy.test.ts +151 -80
  93. package/src/__tests__/conflict-store.test.ts +203 -157
  94. package/src/__tests__/connection-policy.test.ts +89 -59
  95. package/src/__tests__/contacts-tools.test.ts +247 -178
  96. package/src/__tests__/context-memory-e2e.test.ts +306 -214
  97. package/src/__tests__/context-token-estimator.test.ts +114 -74
  98. package/src/__tests__/context-window-manager.test.ts +269 -167
  99. package/src/__tests__/contradiction-checker.test.ts +161 -135
  100. package/src/__tests__/conversation-attention-store.test.ts +350 -290
  101. package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
  102. package/src/__tests__/conversation-pairing.test.ts +220 -113
  103. package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
  104. package/src/__tests__/conversation-store.test.ts +390 -235
  105. package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
  106. package/src/__tests__/credential-broker-server-use.test.ts +283 -243
  107. package/src/__tests__/credential-broker.test.ts +128 -74
  108. package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
  109. package/src/__tests__/credential-metadata-store.test.ts +360 -311
  110. package/src/__tests__/credential-policy-validate.test.ts +81 -65
  111. package/src/__tests__/credential-resolve.test.ts +212 -145
  112. package/src/__tests__/credential-security-e2e.test.ts +144 -103
  113. package/src/__tests__/credential-security-invariants.test.ts +253 -208
  114. package/src/__tests__/credential-selection.test.ts +254 -146
  115. package/src/__tests__/credential-vault-unit.test.ts +531 -341
  116. package/src/__tests__/credential-vault.test.ts +761 -484
  117. package/src/__tests__/daemon-assistant-events.test.ts +91 -66
  118. package/src/__tests__/daemon-lifecycle.test.ts +258 -190
  119. package/src/__tests__/daemon-server-session-init.test.ts +2 -1
  120. package/src/__tests__/date-context.test.ts +314 -249
  121. package/src/__tests__/db-migration-rollback.test.ts +259 -130
  122. package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
  123. package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
  124. package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
  125. package/src/__tests__/dictation-mode-detection.test.ts +77 -55
  126. package/src/__tests__/dictation-profile-store.test.ts +70 -56
  127. package/src/__tests__/dictation-text-processing.test.ts +53 -35
  128. package/src/__tests__/diff.test.ts +102 -98
  129. package/src/__tests__/domain-normalize.test.ts +54 -54
  130. package/src/__tests__/domain-policy.test.ts +71 -55
  131. package/src/__tests__/dynamic-page-surface.test.ts +31 -33
  132. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
  133. package/src/__tests__/edit-engine.test.ts +56 -56
  134. package/src/__tests__/elevenlabs-client.test.ts +117 -91
  135. package/src/__tests__/elevenlabs-config.test.ts +32 -31
  136. package/src/__tests__/email-classifier.test.ts +15 -12
  137. package/src/__tests__/email-cli.test.ts +121 -108
  138. package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
  139. package/src/__tests__/encrypted-store.test.ts +180 -154
  140. package/src/__tests__/entity-extractor.test.ts +108 -87
  141. package/src/__tests__/entity-search.test.ts +664 -258
  142. package/src/__tests__/ephemeral-permissions.test.ts +224 -188
  143. package/src/__tests__/event-bus.test.ts +81 -77
  144. package/src/__tests__/extract-email.test.ts +29 -20
  145. package/src/__tests__/file-edit-tool.test.ts +62 -44
  146. package/src/__tests__/file-ops-service.test.ts +131 -114
  147. package/src/__tests__/file-read-tool.test.ts +48 -31
  148. package/src/__tests__/file-write-tool.test.ts +43 -37
  149. package/src/__tests__/filesystem-tools.test.ts +238 -209
  150. package/src/__tests__/followup-tools.test.ts +237 -162
  151. package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
  152. package/src/__tests__/frontmatter.test.ts +96 -81
  153. package/src/__tests__/fuzzy-match-property.test.ts +75 -81
  154. package/src/__tests__/fuzzy-match.test.ts +71 -65
  155. package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
  156. package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
  157. package/src/__tests__/gateway-only-guard.test.ts +0 -1
  158. package/src/__tests__/gemini-image-service.test.ts +113 -100
  159. package/src/__tests__/gemini-provider.test.ts +297 -220
  160. package/src/__tests__/get-weather.test.ts +188 -114
  161. package/src/__tests__/gmail-integration.test.ts +13 -5
  162. package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
  163. package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
  164. package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
  165. package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
  166. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
  167. package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
  168. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
  169. package/src/__tests__/guardian-action-store.test.ts +158 -109
  170. package/src/__tests__/guardian-action-sweep.test.ts +114 -100
  171. package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
  172. package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
  173. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
  174. package/src/__tests__/guardian-dispatch.test.ts +316 -256
  175. package/src/__tests__/guardian-grant-minting.test.ts +247 -178
  176. package/src/__tests__/guardian-outbound-http.test.ts +5 -3
  177. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
  178. package/src/__tests__/guardian-question-copy.test.ts +17 -17
  179. package/src/__tests__/guardian-question-mode.test.ts +134 -100
  180. package/src/__tests__/guardian-routing-invariants.test.ts +0 -1
  181. package/src/__tests__/guardian-routing-state.test.ts +0 -1
  182. package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
  183. package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
  184. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
  185. package/src/__tests__/handle-user-message-secret-resume.test.ts +7 -2
  186. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
  187. package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
  188. package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
  189. package/src/__tests__/handlers-slack-config.test.ts +63 -54
  190. package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
  191. package/src/__tests__/handlers-telegram-config.test.ts +662 -329
  192. package/src/__tests__/handlers-twitter-config.test.ts +525 -298
  193. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +5 -2
  194. package/src/__tests__/headless-browser-interactions.test.ts +444 -280
  195. package/src/__tests__/headless-browser-navigate.test.ts +116 -79
  196. package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
  197. package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
  198. package/src/__tests__/heartbeat-service.test.ts +76 -58
  199. package/src/__tests__/history-repair-observability.test.ts +14 -14
  200. package/src/__tests__/history-repair.test.ts +171 -167
  201. package/src/__tests__/home-base-bootstrap.test.ts +30 -27
  202. package/src/__tests__/hooks-blocking.test.ts +86 -37
  203. package/src/__tests__/hooks-cli.test.ts +104 -68
  204. package/src/__tests__/hooks-config.test.ts +81 -43
  205. package/src/__tests__/hooks-discovery.test.ts +106 -96
  206. package/src/__tests__/hooks-integration.test.ts +78 -72
  207. package/src/__tests__/hooks-manager.test.ts +99 -61
  208. package/src/__tests__/hooks-runner.test.ts +94 -71
  209. package/src/__tests__/hooks-settings.test.ts +69 -64
  210. package/src/__tests__/hooks-templates.test.ts +85 -54
  211. package/src/__tests__/hooks-ts-runner.test.ts +82 -45
  212. package/src/__tests__/hooks-watch.test.ts +32 -22
  213. package/src/__tests__/host-file-edit-tool.test.ts +190 -148
  214. package/src/__tests__/host-file-read-tool.test.ts +86 -63
  215. package/src/__tests__/host-file-write-tool.test.ts +98 -64
  216. package/src/__tests__/host-shell-tool.test.ts +342 -233
  217. package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
  218. package/src/__tests__/ingress-member-store.test.ts +163 -159
  219. package/src/__tests__/ingress-reconcile.test.ts +13 -6
  220. package/src/__tests__/ingress-routes-http.test.ts +441 -356
  221. package/src/__tests__/ingress-url-consistency.test.ts +125 -64
  222. package/src/__tests__/integration-status.test.ts +93 -73
  223. package/src/__tests__/intent-routing.test.ts +148 -118
  224. package/src/__tests__/invite-redemption-service.test.ts +163 -121
  225. package/src/__tests__/ipc-blob-store.test.ts +104 -91
  226. package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
  227. package/src/__tests__/ipc-contract.test.ts +24 -23
  228. package/src/__tests__/ipc-protocol.test.ts +52 -46
  229. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
  230. package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
  231. package/src/__tests__/ipc-validate.test.ts +240 -179
  232. package/src/__tests__/key-migration.test.ts +123 -90
  233. package/src/__tests__/keychain.test.ts +150 -123
  234. package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
  235. package/src/__tests__/llm-usage-store.test.ts +112 -87
  236. package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
  237. package/src/__tests__/managed-store.test.ts +411 -360
  238. package/src/__tests__/mcp-cli.test.ts +190 -124
  239. package/src/__tests__/mcp-health-check.test.ts +26 -21
  240. package/src/__tests__/media-generate-image.test.ts +122 -99
  241. package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
  242. package/src/__tests__/media-visibility-policy.test.ts +86 -38
  243. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
  244. package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
  245. package/src/__tests__/memory-query-builder.test.ts +32 -33
  246. package/src/__tests__/memory-recall-quality.test.ts +761 -407
  247. package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
  248. package/src/__tests__/memory-regressions.test.ts +3725 -2642
  249. package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
  250. package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
  251. package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
  252. package/src/__tests__/messaging-send-tool.test.ts +36 -29
  253. package/src/__tests__/migration-cli-flows.test.ts +69 -53
  254. package/src/__tests__/migration-ordering.test.ts +103 -86
  255. package/src/__tests__/mime-builder.test.ts +55 -32
  256. package/src/__tests__/mock-signup-server.test.ts +384 -246
  257. package/src/__tests__/model-intents.test.ts +61 -37
  258. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
  259. package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
  260. package/src/__tests__/non-member-access-request.test.ts +3 -2
  261. package/src/__tests__/notification-broadcaster.test.ts +99 -81
  262. package/src/__tests__/notification-decision-fallback.test.ts +223 -178
  263. package/src/__tests__/notification-decision-strategy.test.ts +375 -337
  264. package/src/__tests__/notification-deep-link.test.ts +67 -61
  265. package/src/__tests__/notification-guardian-path.test.ts +248 -206
  266. package/src/__tests__/notification-routing-intent.test.ts +166 -93
  267. package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
  268. package/src/__tests__/notification-thread-candidates.test.ts +64 -61
  269. package/src/__tests__/oauth-callback-registry.test.ts +40 -30
  270. package/src/__tests__/oauth-connect-handler.test.ts +109 -89
  271. package/src/__tests__/oauth-scope-policy.test.ts +63 -55
  272. package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
  273. package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
  274. package/src/__tests__/onboarding-template-contract.test.ts +93 -94
  275. package/src/__tests__/openai-provider.test.ts +366 -274
  276. package/src/__tests__/pairing-concurrent.test.ts +18 -12
  277. package/src/__tests__/pairing-routes.test.ts +45 -41
  278. package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
  279. package/src/__tests__/parser.test.ts +316 -226
  280. package/src/__tests__/path-classifier.test.ts +24 -25
  281. package/src/__tests__/path-policy.test.ts +187 -147
  282. package/src/__tests__/phone.test.ts +36 -36
  283. package/src/__tests__/platform-move-helper.test.ts +48 -40
  284. package/src/__tests__/platform-socket-path.test.ts +23 -24
  285. package/src/__tests__/platform-workspace-migration.test.ts +464 -414
  286. package/src/__tests__/platform.test.ts +61 -53
  287. package/src/__tests__/playbook-execution.test.ts +397 -265
  288. package/src/__tests__/playbook-tools.test.ts +267 -196
  289. package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
  290. package/src/__tests__/pricing.test.ts +316 -136
  291. package/src/__tests__/profile-compiler.test.ts +206 -188
  292. package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
  293. package/src/__tests__/provider-error-scenarios.test.ts +212 -158
  294. package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
  295. package/src/__tests__/provider-registry-ollama.test.ts +13 -9
  296. package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
  297. package/src/__tests__/proxy-approval-callback.test.ts +180 -119
  298. package/src/__tests__/public-ingress-urls.test.ts +112 -94
  299. package/src/__tests__/qdrant-manager.test.ts +147 -98
  300. package/src/__tests__/ratelimit.test.ts +152 -82
  301. package/src/__tests__/recording-handler.test.ts +273 -151
  302. package/src/__tests__/recording-intent-fallback.test.ts +94 -75
  303. package/src/__tests__/recording-intent-handler.test.ts +9 -2
  304. package/src/__tests__/recording-intent.test.ts +578 -379
  305. package/src/__tests__/recording-state-machine.test.ts +530 -316
  306. package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
  307. package/src/__tests__/recurrence-engine.test.ts +81 -41
  308. package/src/__tests__/recurrence-types.test.ts +63 -44
  309. package/src/__tests__/relay-server.test.ts +2131 -1602
  310. package/src/__tests__/reminder-store.test.ts +158 -80
  311. package/src/__tests__/reminder.test.ts +113 -109
  312. package/src/__tests__/remote-skill-policy.test.ts +96 -72
  313. package/src/__tests__/request-file-tool.test.ts +74 -67
  314. package/src/__tests__/response-tier.test.ts +131 -74
  315. package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
  316. package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
  317. package/src/__tests__/runtime-events-sse.test.ts +0 -1
  318. package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
  319. package/src/__tests__/sandbox-host-parity.test.ts +377 -301
  320. package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
  321. package/src/__tests__/schedule-store.test.ts +268 -205
  322. package/src/__tests__/schedule-tools.test.ts +702 -524
  323. package/src/__tests__/scheduler-recurrence.test.ts +240 -130
  324. package/src/__tests__/scoped-approval-grants.test.ts +258 -168
  325. package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
  326. package/src/__tests__/script-proxy-certs.test.ts +38 -35
  327. package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
  328. package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
  329. package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
  330. package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
  331. package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
  332. package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
  333. package/src/__tests__/script-proxy-policy.test.ts +308 -153
  334. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
  335. package/src/__tests__/script-proxy-router.test.ts +111 -77
  336. package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
  337. package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
  338. package/src/__tests__/secret-allowlist.test.ts +105 -90
  339. package/src/__tests__/secret-ingress-handler.test.ts +41 -30
  340. package/src/__tests__/secret-onetime-send.test.ts +67 -50
  341. package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
  342. package/src/__tests__/secret-response-routing.test.ts +50 -41
  343. package/src/__tests__/secret-scanner-executor.test.ts +152 -111
  344. package/src/__tests__/secret-scanner.test.ts +495 -413
  345. package/src/__tests__/secure-keys.test.ts +132 -121
  346. package/src/__tests__/send-endpoint-busy.test.ts +8 -3
  347. package/src/__tests__/send-notification-tool.test.ts +43 -42
  348. package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
  349. package/src/__tests__/sequence-store.test.ts +335 -167
  350. package/src/__tests__/server-history-render.test.ts +341 -202
  351. package/src/__tests__/session-abort-tool-results.test.ts +133 -70
  352. package/src/__tests__/session-confirmation-signals.test.ts +252 -160
  353. package/src/__tests__/session-conflict-gate.test.ts +775 -585
  354. package/src/__tests__/session-error.test.ts +222 -191
  355. package/src/__tests__/session-evictor.test.ts +79 -62
  356. package/src/__tests__/session-init.benchmark.test.ts +170 -108
  357. package/src/__tests__/session-load-history-repair.test.ts +273 -139
  358. package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
  359. package/src/__tests__/session-pre-run-repair.test.ts +106 -59
  360. package/src/__tests__/session-profile-injection.test.ts +198 -130
  361. package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
  362. package/src/__tests__/session-queue.test.ts +624 -321
  363. package/src/__tests__/session-runtime-assembly.test.ts +425 -329
  364. package/src/__tests__/session-runtime-workspace.test.ts +69 -61
  365. package/src/__tests__/session-skill-tools.test.ts +973 -678
  366. package/src/__tests__/session-slash-known.test.ts +185 -133
  367. package/src/__tests__/session-slash-queue.test.ts +147 -81
  368. package/src/__tests__/session-slash-unknown.test.ts +135 -90
  369. package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
  370. package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
  371. package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
  372. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
  373. package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
  374. package/src/__tests__/session-undo.test.ts +43 -30
  375. package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
  376. package/src/__tests__/session-workspace-injection.test.ts +245 -117
  377. package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
  378. package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
  379. package/src/__tests__/shell-credential-ref.test.ts +126 -90
  380. package/src/__tests__/shell-identity.test.ts +134 -111
  381. package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
  382. package/src/__tests__/shell-parser-property.test.ts +435 -288
  383. package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
  384. package/src/__tests__/size-guard.test.ts +42 -44
  385. package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
  386. package/src/__tests__/skill-feature-flags.test.ts +75 -47
  387. package/src/__tests__/skill-include-graph.test.ts +143 -148
  388. package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
  389. package/src/__tests__/skill-load-tool.test.ts +371 -199
  390. package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
  391. package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
  392. package/src/__tests__/skill-script-runner-host.test.ts +460 -250
  393. package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
  394. package/src/__tests__/skill-script-runner.test.ts +115 -74
  395. package/src/__tests__/skill-tool-factory.test.ts +140 -96
  396. package/src/__tests__/skill-tool-manifest.test.ts +306 -210
  397. package/src/__tests__/skill-version-hash.test.ts +70 -56
  398. package/src/__tests__/skills.test.ts +0 -1
  399. package/src/__tests__/slack-channel-config.test.ts +127 -84
  400. package/src/__tests__/slack-skill.test.ts +60 -47
  401. package/src/__tests__/slash-commands-catalog.test.ts +37 -31
  402. package/src/__tests__/slash-commands-parser.test.ts +71 -64
  403. package/src/__tests__/slash-commands-resolver.test.ts +143 -107
  404. package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
  405. package/src/__tests__/sms-messaging-provider.test.ts +4 -0
  406. package/src/__tests__/speaker-identification.test.ts +28 -25
  407. package/src/__tests__/starter-bundle.test.ts +27 -23
  408. package/src/__tests__/starter-task-flow.test.ts +67 -52
  409. package/src/__tests__/subagent-manager-notify.test.ts +154 -108
  410. package/src/__tests__/subagent-tools.test.ts +311 -270
  411. package/src/__tests__/subagent-types.test.ts +40 -40
  412. package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
  413. package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
  414. package/src/__tests__/swarm-orchestrator.test.ts +135 -101
  415. package/src/__tests__/swarm-plan-validator.test.ts +125 -73
  416. package/src/__tests__/swarm-recursion.test.ts +58 -46
  417. package/src/__tests__/swarm-router-planner.test.ts +99 -74
  418. package/src/__tests__/swarm-session-integration.test.ts +148 -91
  419. package/src/__tests__/swarm-tool.test.ts +65 -45
  420. package/src/__tests__/swarm-worker-backend.test.ts +59 -45
  421. package/src/__tests__/swarm-worker-runner.test.ts +133 -118
  422. package/src/__tests__/system-prompt.test.ts +311 -256
  423. package/src/__tests__/task-compiler.test.ts +176 -120
  424. package/src/__tests__/task-management-tools.test.ts +561 -456
  425. package/src/__tests__/task-memory-cleanup.test.ts +627 -362
  426. package/src/__tests__/task-runner.test.ts +117 -94
  427. package/src/__tests__/task-scheduler.test.ts +113 -84
  428. package/src/__tests__/task-tools.test.ts +349 -264
  429. package/src/__tests__/terminal-sandbox.test.ts +138 -108
  430. package/src/__tests__/terminal-tools.test.ts +350 -305
  431. package/src/__tests__/thread-seed-composer.test.ts +307 -180
  432. package/src/__tests__/tool-approval-handler.test.ts +238 -137
  433. package/src/__tests__/tool-audit-listener.test.ts +69 -69
  434. package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
  435. package/src/__tests__/tool-execution-abort-cleanup.test.ts +155 -146
  436. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
  437. package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
  438. package/src/__tests__/tool-executor-redaction.test.ts +112 -109
  439. package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
  440. package/src/__tests__/tool-executor.test.ts +1274 -674
  441. package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
  442. package/src/__tests__/tool-metrics-listener.test.ts +97 -85
  443. package/src/__tests__/tool-notification-listener.test.ts +42 -25
  444. package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
  445. package/src/__tests__/tool-policy.test.ts +44 -25
  446. package/src/__tests__/tool-profiling-listener.test.ts +99 -93
  447. package/src/__tests__/tool-result-truncation.test.ts +5 -4
  448. package/src/__tests__/tool-trace-listener.test.ts +131 -111
  449. package/src/__tests__/top-level-renderer.test.ts +62 -58
  450. package/src/__tests__/top-level-scanner.test.ts +68 -64
  451. package/src/__tests__/trace-emitter.test.ts +56 -56
  452. package/src/__tests__/trust-context-guards.test.ts +65 -65
  453. package/src/__tests__/trust-store.test.ts +1239 -806
  454. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  455. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
  456. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +3 -2
  457. package/src/__tests__/trusted-contact-multichannel.test.ts +3 -2
  458. package/src/__tests__/trusted-contact-verification.test.ts +251 -231
  459. package/src/__tests__/turn-commit.test.ts +259 -200
  460. package/src/__tests__/twilio-provider.test.ts +140 -126
  461. package/src/__tests__/twilio-rest.test.ts +22 -18
  462. package/src/__tests__/twilio-routes-elevenlabs.test.ts +0 -1
  463. package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
  464. package/src/__tests__/twilio-routes.test.ts +0 -1
  465. package/src/__tests__/twitter-auth-handler.test.ts +184 -139
  466. package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
  467. package/src/__tests__/twitter-cli-routing.test.ts +146 -99
  468. package/src/__tests__/twitter-oauth-client.test.ts +82 -65
  469. package/src/__tests__/update-bulletin-format.test.ts +69 -66
  470. package/src/__tests__/update-bulletin-state.test.ts +66 -60
  471. package/src/__tests__/update-bulletin.test.ts +150 -114
  472. package/src/__tests__/update-template-contract.test.ts +15 -10
  473. package/src/__tests__/url-safety.test.ts +288 -265
  474. package/src/__tests__/user-reference.test.ts +32 -32
  475. package/src/__tests__/view-image-tool.test.ts +118 -96
  476. package/src/__tests__/voice-invite-redemption.test.ts +111 -106
  477. package/src/__tests__/voice-quality.test.ts +117 -102
  478. package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
  479. package/src/__tests__/voice-session-bridge.test.ts +351 -216
  480. package/src/__tests__/weather-skill-regression.test.ts +170 -120
  481. package/src/__tests__/web-fetch.test.ts +664 -526
  482. package/src/__tests__/web-search.test.ts +379 -213
  483. package/src/__tests__/work-item-output.test.ts +90 -53
  484. package/src/__tests__/workspace-git-service.test.ts +437 -356
  485. package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
  486. package/src/__tests__/workspace-lifecycle.test.ts +98 -64
  487. package/src/__tests__/workspace-policy.test.ts +139 -71
  488. package/src/cli/mcp.ts +81 -28
  489. package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
  490. package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
  491. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +25 -10
  492. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
  493. package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +6 -11
  494. package/src/config/bundled-skills/messaging/SKILL.md +4 -3
  495. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
  496. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
  497. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -2
  498. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
  499. package/src/config/bundled-skills/sms-setup/SKILL.md +8 -16
  500. package/src/config/bundled-skills/telegram-setup/SKILL.md +3 -3
  501. package/src/config/bundled-skills/trusted-contacts/SKILL.md +13 -25
  502. package/src/config/bundled-skills/twilio-setup/SKILL.md +13 -23
  503. package/src/config/bundled-tool-registry.ts +2 -0
  504. package/src/config/env.ts +3 -4
  505. package/src/config/system-prompt.ts +32 -0
  506. package/src/mcp/client.ts +2 -7
  507. package/src/memory/db-connection.ts +16 -10
  508. package/src/messaging/providers/gmail/adapter.ts +10 -3
  509. package/src/messaging/providers/gmail/client.ts +280 -72
  510. package/src/runtime/auth/__tests__/context.test.ts +75 -65
  511. package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
  512. package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
  513. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
  514. package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
  515. package/src/runtime/auth/__tests__/policy.test.ts +9 -9
  516. package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
  517. package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
  518. package/src/runtime/auth/__tests__/subject.test.ts +54 -54
  519. package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
  520. package/src/runtime/auth/scopes.ts +3 -0
  521. package/src/runtime/auth/token-service.ts +4 -1
  522. package/src/runtime/auth/types.ts +2 -1
  523. package/src/runtime/http-server.ts +2 -1
  524. package/src/security/secure-keys.ts +120 -54
  525. package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
  526. package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
  527. package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
  528. package/src/tools/terminal/safe-env.ts +7 -0
@@ -1,28 +1,46 @@
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
- import { _resetEnrichmentService, getEnrichmentService } from '../workspace/commit-message-enrichment-service.js';
9
- import type { CommitContext, CommitMessageProvider, CommitMessageResult } from '../workspace/commit-message-provider.js';
14
+ import {
15
+ _resetEnrichmentService,
16
+ getEnrichmentService,
17
+ } from "../workspace/commit-message-enrichment-service.js";
18
+ import type {
19
+ CommitContext,
20
+ CommitMessageProvider,
21
+ CommitMessageResult,
22
+ } from "../workspace/commit-message-provider.js";
10
23
  import {
11
24
  _resetGitServiceRegistry,
12
25
  WorkspaceGitService,
13
- } from '../workspace/git-service.js';
26
+ } from "../workspace/git-service.js";
14
27
  import {
15
28
  _resetHeartbeatState,
16
29
  WorkspaceHeartbeatService,
17
- } from '../workspace/heartbeat-service.js';
30
+ } from "../workspace/heartbeat-service.js";
18
31
 
19
- describe('WorkspaceHeartbeatService', () => {
32
+ describe("WorkspaceHeartbeatService", () => {
20
33
  let testDir: string;
21
34
  let service: WorkspaceGitService;
22
35
  let services: Map<string, WorkspaceGitService>;
23
36
 
24
37
  beforeEach(async () => {
25
- testDir = join(tmpdir(), `vellum-heartbeat-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
38
+ testDir = join(
39
+ tmpdir(),
40
+ `vellum-heartbeat-test-${Date.now()}-${Math.random()
41
+ .toString(36)
42
+ .slice(2)}`,
43
+ );
26
44
  mkdirSync(testDir, { recursive: true });
27
45
  _resetGitServiceRegistry();
28
46
  _resetHeartbeatState();
@@ -36,7 +54,11 @@ describe('WorkspaceHeartbeatService', () => {
36
54
 
37
55
  afterEach(async () => {
38
56
  // Shut down any in-flight enrichment work before removing the test directory
39
- try { await getEnrichmentService().shutdown(); } catch { /* ignore */ }
57
+ try {
58
+ await getEnrichmentService().shutdown();
59
+ } catch {
60
+ /* ignore */
61
+ }
40
62
  _resetEnrichmentService();
41
63
  if (existsSync(testDir)) {
42
64
  rmSync(testDir, { recursive: true, force: true });
@@ -44,12 +66,16 @@ describe('WorkspaceHeartbeatService', () => {
44
66
  });
45
67
 
46
68
  afterAll(async () => {
47
- try { await getEnrichmentService().shutdown(); } catch { /* ignore */ }
69
+ try {
70
+ await getEnrichmentService().shutdown();
71
+ } catch {
72
+ /* ignore */
73
+ }
48
74
  _resetEnrichmentService();
49
75
  });
50
76
 
51
- describe('heartbeat check with age threshold', () => {
52
- test('does not commit when workspace is clean', async () => {
77
+ describe("heartbeat check with age threshold", () => {
78
+ test("does not commit when workspace is clean", async () => {
53
79
  const heartbeat = new WorkspaceHeartbeatService({
54
80
  ageThresholdMs: 0, // Immediate
55
81
  fileThreshold: 1,
@@ -63,8 +89,8 @@ describe('WorkspaceHeartbeatService', () => {
63
89
  expect(result.skipped).toBe(1);
64
90
  });
65
91
 
66
- test('does not commit when changes are below age and file thresholds', async () => {
67
- writeFileSync(join(testDir, 'file.txt'), 'content');
92
+ test("does not commit when changes are below age and file thresholds", async () => {
93
+ writeFileSync(join(testDir, "file.txt"), "content");
68
94
 
69
95
  const heartbeat = new WorkspaceHeartbeatService({
70
96
  ageThresholdMs: 10 * 60 * 1000, // 10 minutes
@@ -79,8 +105,8 @@ describe('WorkspaceHeartbeatService', () => {
79
105
  expect(result.skipped).toBe(1);
80
106
  });
81
107
 
82
- test('commits when changes exceed age threshold', async () => {
83
- writeFileSync(join(testDir, 'file.txt'), 'content');
108
+ test("commits when changes exceed age threshold", async () => {
109
+ writeFileSync(join(testDir, "file.txt"), "content");
84
110
 
85
111
  let currentTime = 1000000;
86
112
  const heartbeat = new WorkspaceHeartbeatService({
@@ -101,16 +127,16 @@ describe('WorkspaceHeartbeatService', () => {
101
127
  expect(secondResult.committed).toBe(1);
102
128
 
103
129
  // Verify commit message
104
- const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
130
+ const commitMsg = execFileSync("git", ["log", "-1", "--pretty=%B"], {
105
131
  cwd: testDir,
106
- encoding: 'utf-8',
132
+ encoding: "utf-8",
107
133
  });
108
- expect(commitMsg).toContain('auto-commit');
109
- expect(commitMsg).toContain('heartbeat');
110
- expect(commitMsg).toContain('safety net');
134
+ expect(commitMsg).toContain("auto-commit");
135
+ expect(commitMsg).toContain("heartbeat");
136
+ expect(commitMsg).toContain("safety net");
111
137
  });
112
138
 
113
- test('commits when file count exceeds threshold', async () => {
139
+ test("commits when file count exceeds threshold", async () => {
114
140
  // Create enough files to exceed the threshold
115
141
  for (let i = 0; i < 25; i++) {
116
142
  writeFileSync(join(testDir, `file${i}.txt`), `content ${i}`);
@@ -127,16 +153,16 @@ describe('WorkspaceHeartbeatService', () => {
127
153
  expect(result.committed).toBe(1);
128
154
 
129
155
  // Verify commit message mentions file count
130
- const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
156
+ const commitMsg = execFileSync("git", ["log", "-1", "--pretty=%B"], {
131
157
  cwd: testDir,
132
- encoding: 'utf-8',
158
+ encoding: "utf-8",
133
159
  });
134
- expect(commitMsg).toContain('25 files changed');
160
+ expect(commitMsg).toContain("25 files changed");
135
161
  });
136
162
  });
137
163
 
138
- describe('normal operation does not create spurious commits', () => {
139
- test('clean workspace produces no heartbeat commits', async () => {
164
+ describe("normal operation does not create spurious commits", () => {
165
+ test("clean workspace produces no heartbeat commits", async () => {
140
166
  const heartbeat = new WorkspaceHeartbeatService({
141
167
  ageThresholdMs: 0,
142
168
  fileThreshold: 1,
@@ -150,16 +176,16 @@ describe('WorkspaceHeartbeatService', () => {
150
176
  }
151
177
 
152
178
  // Only the initial commit should exist
153
- const commitCount = execFileSync('git', ['rev-list', '--count', 'HEAD'], {
179
+ const commitCount = execFileSync("git", ["rev-list", "--count", "HEAD"], {
154
180
  cwd: testDir,
155
- encoding: 'utf-8',
181
+ encoding: "utf-8",
156
182
  }).trim();
157
183
  expect(parseInt(commitCount, 10)).toBe(1);
158
184
  });
159
185
 
160
- test('changes below both thresholds do not trigger heartbeat commit', async () => {
186
+ test("changes below both thresholds do not trigger heartbeat commit", async () => {
161
187
  // Create a small number of files (below file threshold)
162
- writeFileSync(join(testDir, 'small-change.txt'), 'content');
188
+ writeFileSync(join(testDir, "small-change.txt"), "content");
163
189
 
164
190
  const heartbeat = new WorkspaceHeartbeatService({
165
191
  ageThresholdMs: 10 * 60 * 1000, // 10 minutes
@@ -172,8 +198,8 @@ describe('WorkspaceHeartbeatService', () => {
172
198
  expect(result.skipped).toBe(1);
173
199
  });
174
200
 
175
- test('does not double-commit after turn-boundary commit clears changes', async () => {
176
- writeFileSync(join(testDir, 'file.txt'), 'content');
201
+ test("does not double-commit after turn-boundary commit clears changes", async () => {
202
+ writeFileSync(join(testDir, "file.txt"), "content");
177
203
 
178
204
  let currentTime = 1000000;
179
205
  const heartbeat = new WorkspaceHeartbeatService({
@@ -187,7 +213,7 @@ describe('WorkspaceHeartbeatService', () => {
187
213
  await heartbeat.check();
188
214
 
189
215
  // Simulate a turn-boundary commit that clears the changes
190
- await service.commitChanges('Turn-boundary commit');
216
+ await service.commitChanges("Turn-boundary commit");
191
217
 
192
218
  // Advance time past the threshold
193
219
  currentTime += 6 * 60 * 1000;
@@ -199,9 +225,9 @@ describe('WorkspaceHeartbeatService', () => {
199
225
  });
200
226
  });
201
227
 
202
- describe('shutdown commits', () => {
203
- test('commits pending changes on shutdown', async () => {
204
- writeFileSync(join(testDir, 'unsaved.txt'), 'uncommitted content');
228
+ describe("shutdown commits", () => {
229
+ test("commits pending changes on shutdown", async () => {
230
+ writeFileSync(join(testDir, "unsaved.txt"), "uncommitted content");
205
231
 
206
232
  const heartbeat = new WorkspaceHeartbeatService({
207
233
  getServices: () => services,
@@ -213,16 +239,16 @@ describe('WorkspaceHeartbeatService', () => {
213
239
  expect(result.committed).toBe(1);
214
240
 
215
241
  // Verify commit message
216
- const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
242
+ const commitMsg = execFileSync("git", ["log", "-1", "--pretty=%B"], {
217
243
  cwd: testDir,
218
- encoding: 'utf-8',
244
+ encoding: "utf-8",
219
245
  });
220
- expect(commitMsg).toContain('auto-commit');
221
- expect(commitMsg).toContain('shutdown');
222
- expect(commitMsg).toContain('safety net');
246
+ expect(commitMsg).toContain("auto-commit");
247
+ expect(commitMsg).toContain("shutdown");
248
+ expect(commitMsg).toContain("safety net");
223
249
  });
224
250
 
225
- test('does not commit on shutdown when workspace is clean', async () => {
251
+ test("does not commit on shutdown when workspace is clean", async () => {
226
252
  const heartbeat = new WorkspaceHeartbeatService({
227
253
  getServices: () => services,
228
254
  });
@@ -234,15 +260,20 @@ describe('WorkspaceHeartbeatService', () => {
234
260
  expect(result.skipped).toBe(1);
235
261
  });
236
262
 
237
- test('shutdown commits multiple workspaces', async () => {
238
- const testDir2 = join(tmpdir(), `vellum-heartbeat-test2-${Date.now()}-${Math.random().toString(36).slice(2)}`);
263
+ test("shutdown commits multiple workspaces", async () => {
264
+ const testDir2 = join(
265
+ tmpdir(),
266
+ `vellum-heartbeat-test2-${Date.now()}-${Math.random()
267
+ .toString(36)
268
+ .slice(2)}`,
269
+ );
239
270
  mkdirSync(testDir2, { recursive: true });
240
271
  const service2 = new WorkspaceGitService(testDir2);
241
272
  await service2.ensureInitialized();
242
273
  services.set(testDir2, service2);
243
274
 
244
- writeFileSync(join(testDir, 'file1.txt'), 'content1');
245
- writeFileSync(join(testDir2, 'file2.txt'), 'content2');
275
+ writeFileSync(join(testDir, "file1.txt"), "content1");
276
+ writeFileSync(join(testDir2, "file2.txt"), "content2");
246
277
 
247
278
  const heartbeat = new WorkspaceHeartbeatService({
248
279
  getServices: () => services,
@@ -258,9 +289,12 @@ describe('WorkspaceHeartbeatService', () => {
258
289
  });
259
290
  });
260
291
 
261
- describe('uninitialized workspaces', () => {
262
- test('skips uninitialized workspaces', async () => {
263
- const uninitDir = join(tmpdir(), `vellum-uninit-${Date.now()}-${Math.random().toString(36).slice(2)}`);
292
+ describe("uninitialized workspaces", () => {
293
+ test("skips uninitialized workspaces", async () => {
294
+ const uninitDir = join(
295
+ tmpdir(),
296
+ `vellum-uninit-${Date.now()}-${Math.random().toString(36).slice(2)}`,
297
+ );
264
298
  mkdirSync(uninitDir, { recursive: true });
265
299
 
266
300
  const uninitService = new WorkspaceGitService(uninitDir);
@@ -268,7 +302,7 @@ describe('WorkspaceHeartbeatService', () => {
268
302
  mixedServices.set(uninitDir, uninitService);
269
303
  mixedServices.set(testDir, service);
270
304
 
271
- writeFileSync(join(testDir, 'file.txt'), 'content');
305
+ writeFileSync(join(testDir, "file.txt"), "content");
272
306
 
273
307
  const heartbeat = new WorkspaceHeartbeatService({
274
308
  ageThresholdMs: 0,
@@ -289,8 +323,8 @@ describe('WorkspaceHeartbeatService', () => {
289
323
  });
290
324
  });
291
325
 
292
- describe('threshold behavior', () => {
293
- test('resets dirty tracking after successful commit', async () => {
326
+ describe("threshold behavior", () => {
327
+ test("resets dirty tracking after successful commit", async () => {
294
328
  let currentTime = 1000000;
295
329
  const heartbeat = new WorkspaceHeartbeatService({
296
330
  ageThresholdMs: 5 * 60 * 1000,
@@ -300,7 +334,7 @@ describe('WorkspaceHeartbeatService', () => {
300
334
  });
301
335
 
302
336
  // Create changes and register dirty state
303
- writeFileSync(join(testDir, 'file1.txt'), 'content');
337
+ writeFileSync(join(testDir, "file1.txt"), "content");
304
338
  await heartbeat.check(); // Records first-seen time
305
339
 
306
340
  // Advance past threshold and commit
@@ -309,7 +343,7 @@ describe('WorkspaceHeartbeatService', () => {
309
343
  expect(firstResult.committed).toBe(1);
310
344
 
311
345
  // Create new changes after the commit
312
- writeFileSync(join(testDir, 'file2.txt'), 'more content');
346
+ writeFileSync(join(testDir, "file2.txt"), "more content");
313
347
 
314
348
  // Check again immediately -- should not commit (dirty timer was reset)
315
349
  const secondResult = await heartbeat.check();
@@ -321,8 +355,8 @@ describe('WorkspaceHeartbeatService', () => {
321
355
  expect(thirdResult.committed).toBe(1);
322
356
  });
323
357
 
324
- test('commit message includes trigger metadata', async () => {
325
- writeFileSync(join(testDir, 'file.txt'), 'content');
358
+ test("commit message includes trigger metadata", async () => {
359
+ writeFileSync(join(testDir, "file.txt"), "content");
326
360
 
327
361
  const heartbeat = new WorkspaceHeartbeatService({
328
362
  ageThresholdMs: 0,
@@ -334,16 +368,16 @@ describe('WorkspaceHeartbeatService', () => {
334
368
  await heartbeat.check();
335
369
  await heartbeat.check();
336
370
 
337
- const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
371
+ const commitMsg = execFileSync("git", ["log", "-1", "--pretty=%B"], {
338
372
  cwd: testDir,
339
- encoding: 'utf-8',
373
+ encoding: "utf-8",
340
374
  });
341
375
  expect(commitMsg).toContain('trigger: "heartbeat"');
342
376
  });
343
377
  });
344
378
 
345
- describe('start and stop', () => {
346
- test('start and stop are idempotent', async () => {
379
+ describe("start and stop", () => {
380
+ test("start and stop are idempotent", async () => {
347
381
  const heartbeat = new WorkspaceHeartbeatService({
348
382
  intervalMs: 60000,
349
383
  getServices: () => services,
@@ -357,9 +391,9 @@ describe('WorkspaceHeartbeatService', () => {
357
391
  });
358
392
  });
359
393
 
360
- describe('custom commit message provider', () => {
361
- test('heartbeat commit uses custom provider message', async () => {
362
- writeFileSync(join(testDir, 'file.txt'), 'content');
394
+ describe("custom commit message provider", () => {
395
+ test("heartbeat commit uses custom provider message", async () => {
396
+ writeFileSync(join(testDir, "file.txt"), "content");
363
397
 
364
398
  const customProvider: CommitMessageProvider = {
365
399
  buildImmediateMessage(ctx: CommitContext): CommitMessageResult {
@@ -387,17 +421,17 @@ describe('WorkspaceHeartbeatService', () => {
387
421
  const result = await heartbeat.check();
388
422
  expect(result.committed).toBe(1);
389
423
 
390
- const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
424
+ const commitMsg = execFileSync("git", ["log", "-1", "--pretty=%B"], {
391
425
  cwd: testDir,
392
- encoding: 'utf-8',
426
+ encoding: "utf-8",
393
427
  });
394
- expect(commitMsg).toContain('CUSTOM-HEARTBEAT:');
395
- expect(commitMsg).toContain('via heartbeat');
396
- expect(commitMsg).toContain('customProvider: true');
428
+ expect(commitMsg).toContain("CUSTOM-HEARTBEAT:");
429
+ expect(commitMsg).toContain("via heartbeat");
430
+ expect(commitMsg).toContain("customProvider: true");
397
431
  });
398
432
 
399
- test('shutdown commit uses custom provider message', async () => {
400
- writeFileSync(join(testDir, 'unsaved.txt'), 'uncommitted content');
433
+ test("shutdown commit uses custom provider message", async () => {
434
+ writeFileSync(join(testDir, "unsaved.txt"), "uncommitted content");
401
435
 
402
436
  const customProvider: CommitMessageProvider = {
403
437
  buildImmediateMessage(ctx: CommitContext): CommitMessageResult {
@@ -416,23 +450,23 @@ describe('WorkspaceHeartbeatService', () => {
416
450
  const result = await heartbeat.commitAllPending();
417
451
  expect(result.committed).toBe(1);
418
452
 
419
- const commitMsg = execFileSync('git', ['log', '-1', '--pretty=%B'], {
453
+ const commitMsg = execFileSync("git", ["log", "-1", "--pretty=%B"], {
420
454
  cwd: testDir,
421
- encoding: 'utf-8',
455
+ encoding: "utf-8",
422
456
  });
423
- expect(commitMsg).toContain('CUSTOM-SHUTDOWN: saving');
424
- expect(commitMsg).toContain('shutdownProvider: true');
457
+ expect(commitMsg).toContain("CUSTOM-SHUTDOWN: saving");
458
+ expect(commitMsg).toContain("shutdownProvider: true");
425
459
  });
426
460
 
427
- test('custom provider receives correct context fields for heartbeat trigger', async () => {
428
- writeFileSync(join(testDir, 'a.txt'), 'a');
429
- writeFileSync(join(testDir, 'b.txt'), 'b');
461
+ test("custom provider receives correct context fields for heartbeat trigger", async () => {
462
+ writeFileSync(join(testDir, "a.txt"), "a");
463
+ writeFileSync(join(testDir, "b.txt"), "b");
430
464
 
431
465
  let capturedCtx: CommitContext | null = null;
432
466
  const customProvider: CommitMessageProvider = {
433
467
  buildImmediateMessage(ctx: CommitContext): CommitMessageResult {
434
468
  capturedCtx = ctx;
435
- return { message: 'capture-context' };
469
+ return { message: "capture-context" };
436
470
  },
437
471
  };
438
472
 
@@ -452,22 +486,22 @@ describe('WorkspaceHeartbeatService', () => {
452
486
  await heartbeat.check();
453
487
 
454
488
  expect(capturedCtx).not.toBeNull();
455
- expect(capturedCtx!.trigger).toBe('heartbeat');
489
+ expect(capturedCtx!.trigger).toBe("heartbeat");
456
490
  expect(capturedCtx!.workspaceDir).toBe(testDir);
457
- expect(capturedCtx!.changedFiles).toContain('a.txt');
458
- expect(capturedCtx!.changedFiles).toContain('b.txt');
491
+ expect(capturedCtx!.changedFiles).toContain("a.txt");
492
+ expect(capturedCtx!.changedFiles).toContain("b.txt");
459
493
  expect(capturedCtx!.timestampMs).toBe(currentTime);
460
494
  expect(capturedCtx!.reason).toBeDefined();
461
495
  });
462
496
 
463
- test('custom provider receives correct context fields for shutdown trigger', async () => {
464
- writeFileSync(join(testDir, 'shutdown-file.txt'), 'data');
497
+ test("custom provider receives correct context fields for shutdown trigger", async () => {
498
+ writeFileSync(join(testDir, "shutdown-file.txt"), "data");
465
499
 
466
500
  let capturedCtx: CommitContext | null = null;
467
501
  const customProvider: CommitMessageProvider = {
468
502
  buildImmediateMessage(ctx: CommitContext): CommitMessageResult {
469
503
  capturedCtx = ctx;
470
- return { message: 'capture-shutdown-context' };
504
+ return { message: "capture-shutdown-context" };
471
505
  },
472
506
  };
473
507
 
@@ -479,9 +513,9 @@ describe('WorkspaceHeartbeatService', () => {
479
513
  await heartbeat.commitAllPending();
480
514
 
481
515
  expect(capturedCtx).not.toBeNull();
482
- expect(capturedCtx!.trigger).toBe('shutdown');
516
+ expect(capturedCtx!.trigger).toBe("shutdown");
483
517
  expect(capturedCtx!.workspaceDir).toBe(testDir);
484
- expect(capturedCtx!.changedFiles).toContain('shutdown-file.txt');
518
+ expect(capturedCtx!.changedFiles).toContain("shutdown-file.txt");
485
519
  });
486
520
  });
487
521
  });