@vellumai/assistant 0.4.16 → 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 (536) hide show
  1. package/Dockerfile +6 -6
  2. package/README.md +1 -2
  3. package/eslint.config.mjs +2 -2
  4. package/package.json +1 -1
  5. package/src/__tests__/access-request-decision.test.ts +128 -120
  6. package/src/__tests__/account-registry.test.ts +121 -110
  7. package/src/__tests__/active-skill-tools.test.ts +200 -172
  8. package/src/__tests__/actor-token-service.test.ts +341 -274
  9. package/src/__tests__/agent-loop-thinking.test.ts +28 -19
  10. package/src/__tests__/agent-loop.test.ts +798 -378
  11. package/src/__tests__/anthropic-provider.test.ts +405 -247
  12. package/src/__tests__/app-builder-tool-scripts.test.ts +97 -97
  13. package/src/__tests__/app-bundler.test.ts +112 -79
  14. package/src/__tests__/app-executors.test.ts +205 -178
  15. package/src/__tests__/app-git-history.test.ts +90 -73
  16. package/src/__tests__/app-git-service.test.ts +67 -53
  17. package/src/__tests__/app-open-proxy.test.ts +29 -25
  18. package/src/__tests__/approval-conversation-turn.test.ts +100 -81
  19. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +45 -17
  20. package/src/__tests__/approval-message-composer.test.ts +119 -119
  21. package/src/__tests__/approval-primitive.test.ts +264 -233
  22. package/src/__tests__/approval-routes-http.test.ts +4 -3
  23. package/src/__tests__/asset-materialize-tool.test.ts +250 -178
  24. package/src/__tests__/asset-search-tool.test.ts +251 -191
  25. package/src/__tests__/assistant-attachment-directive.test.ts +187 -142
  26. package/src/__tests__/assistant-attachments.test.ts +254 -186
  27. package/src/__tests__/assistant-event-hub.test.ts +105 -63
  28. package/src/__tests__/assistant-event.test.ts +66 -58
  29. package/src/__tests__/assistant-events-sse-hardening.test.ts +113 -73
  30. package/src/__tests__/assistant-feature-flag-guard.test.ts +78 -52
  31. package/src/__tests__/assistant-feature-flag-guardrails.test.ts +48 -45
  32. package/src/__tests__/assistant-feature-flags-integration.test.ts +118 -77
  33. package/src/__tests__/assistant-id-boundary-guard.test.ts +158 -104
  34. package/src/__tests__/attachments-store.test.ts +240 -183
  35. package/src/__tests__/attachments.test.ts +70 -62
  36. package/src/__tests__/audit-log-rotation.test.ts +50 -35
  37. package/src/__tests__/browser-fill-credential.test.ts +169 -101
  38. package/src/__tests__/browser-manager.test.ts +97 -75
  39. package/src/__tests__/browser-runtime-check.test.ts +16 -15
  40. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +12 -10
  41. package/src/__tests__/browser-skill-endstate.test.ts +97 -72
  42. package/src/__tests__/bundle-scanner.test.ts +47 -22
  43. package/src/__tests__/bundled-asset.test.ts +74 -47
  44. package/src/__tests__/call-constants.test.ts +19 -19
  45. package/src/__tests__/call-controller.test.ts +1073 -751
  46. package/src/__tests__/call-conversation-messages.test.ts +90 -65
  47. package/src/__tests__/call-domain.test.ts +149 -121
  48. package/src/__tests__/call-pointer-message-composer.test.ts +113 -83
  49. package/src/__tests__/call-pointer-messages.test.ts +213 -154
  50. package/src/__tests__/call-pointer-no-hardcoded-copy.guard.test.ts +9 -10
  51. package/src/__tests__/call-recovery.test.ts +232 -212
  52. package/src/__tests__/call-routes-http.test.ts +328 -279
  53. package/src/__tests__/call-start-guardian-guard.test.ts +32 -30
  54. package/src/__tests__/call-state-machine.test.ts +62 -51
  55. package/src/__tests__/call-state.test.ts +89 -75
  56. package/src/__tests__/call-store.test.ts +387 -316
  57. package/src/__tests__/callback-handoff-copy.test.ts +84 -82
  58. package/src/__tests__/canonical-guardian-store.test.ts +331 -280
  59. package/src/__tests__/channel-approval-routes.test.ts +1643 -1126
  60. package/src/__tests__/channel-approval.test.ts +139 -137
  61. package/src/__tests__/channel-approvals.test.ts +226 -182
  62. package/src/__tests__/channel-delivery-store.test.ts +232 -194
  63. package/src/__tests__/channel-guardian.test.ts +6 -3
  64. package/src/__tests__/channel-invite-transport.test.ts +107 -92
  65. package/src/__tests__/channel-policy.test.ts +42 -38
  66. package/src/__tests__/channel-readiness-service.test.ts +119 -102
  67. package/src/__tests__/channel-reply-delivery.test.ts +147 -118
  68. package/src/__tests__/channel-retry-sweep.test.ts +153 -110
  69. package/src/__tests__/checker.test.ts +3309 -1850
  70. package/src/__tests__/clarification-resolver.test.ts +91 -79
  71. package/src/__tests__/classifier.test.ts +64 -54
  72. package/src/__tests__/claude-code-skill-regression.test.ts +42 -37
  73. package/src/__tests__/claude-code-tool-profiles.test.ts +31 -29
  74. package/src/__tests__/clawhub.test.ts +92 -82
  75. package/src/__tests__/cli.test.ts +30 -30
  76. package/src/__tests__/clipboard.test.ts +53 -46
  77. package/src/__tests__/commit-guarantee.test.ts +59 -52
  78. package/src/__tests__/commit-message-enrichment-service.test.ts +203 -75
  79. package/src/__tests__/compaction.benchmark.test.ts +33 -31
  80. package/src/__tests__/computer-use-session-compaction.test.ts +60 -50
  81. package/src/__tests__/computer-use-session-lifecycle.test.ts +145 -117
  82. package/src/__tests__/computer-use-session-working-dir.test.ts +62 -48
  83. package/src/__tests__/computer-use-skill-baseline.test.ts +22 -19
  84. package/src/__tests__/computer-use-skill-endstate.test.ts +45 -31
  85. package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +121 -88
  86. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +65 -42
  87. package/src/__tests__/computer-use-skill-proxy-bridge.test.ts +33 -18
  88. package/src/__tests__/computer-use-tools.test.ts +121 -98
  89. package/src/__tests__/config-schema.test.ts +443 -347
  90. package/src/__tests__/config-watcher.test.ts +96 -81
  91. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +148 -133
  92. package/src/__tests__/conflict-intent-tokenization.test.ts +96 -78
  93. package/src/__tests__/conflict-policy.test.ts +151 -80
  94. package/src/__tests__/conflict-store.test.ts +203 -157
  95. package/src/__tests__/connection-policy.test.ts +89 -59
  96. package/src/__tests__/contacts-tools.test.ts +247 -178
  97. package/src/__tests__/context-memory-e2e.test.ts +306 -214
  98. package/src/__tests__/context-token-estimator.test.ts +114 -74
  99. package/src/__tests__/context-window-manager.test.ts +269 -167
  100. package/src/__tests__/contradiction-checker.test.ts +161 -135
  101. package/src/__tests__/conversation-attention-store.test.ts +350 -290
  102. package/src/__tests__/conversation-attention-telegram.test.ts +156 -114
  103. package/src/__tests__/conversation-pairing.test.ts +220 -113
  104. package/src/__tests__/conversation-routes-guardian-reply.test.ts +164 -104
  105. package/src/__tests__/conversation-routes.test.ts +71 -41
  106. package/src/__tests__/conversation-store.test.ts +390 -235
  107. package/src/__tests__/credential-broker-browser-fill.test.ts +325 -250
  108. package/src/__tests__/credential-broker-server-use.test.ts +283 -243
  109. package/src/__tests__/credential-broker.test.ts +128 -74
  110. package/src/__tests__/credential-host-pattern-match.test.ts +64 -44
  111. package/src/__tests__/credential-metadata-store.test.ts +360 -311
  112. package/src/__tests__/credential-policy-validate.test.ts +81 -65
  113. package/src/__tests__/credential-resolve.test.ts +212 -145
  114. package/src/__tests__/credential-security-e2e.test.ts +144 -103
  115. package/src/__tests__/credential-security-invariants.test.ts +253 -208
  116. package/src/__tests__/credential-selection.test.ts +254 -146
  117. package/src/__tests__/credential-vault-unit.test.ts +531 -341
  118. package/src/__tests__/credential-vault.test.ts +761 -484
  119. package/src/__tests__/daemon-assistant-events.test.ts +91 -66
  120. package/src/__tests__/daemon-lifecycle.test.ts +258 -190
  121. package/src/__tests__/daemon-server-session-init.test.ts +257 -191
  122. package/src/__tests__/date-context.test.ts +314 -249
  123. package/src/__tests__/db-migration-rollback.test.ts +259 -130
  124. package/src/__tests__/db-schedule-syntax-migration.test.ts +78 -41
  125. package/src/__tests__/delete-managed-skill-tool.test.ts +77 -53
  126. package/src/__tests__/deterministic-verification-control-plane.test.ts +183 -135
  127. package/src/__tests__/dictation-mode-detection.test.ts +77 -55
  128. package/src/__tests__/dictation-profile-store.test.ts +70 -56
  129. package/src/__tests__/dictation-text-processing.test.ts +53 -35
  130. package/src/__tests__/diff.test.ts +102 -98
  131. package/src/__tests__/domain-normalize.test.ts +54 -54
  132. package/src/__tests__/domain-policy.test.ts +71 -55
  133. package/src/__tests__/dynamic-page-surface.test.ts +31 -33
  134. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +69 -69
  135. package/src/__tests__/edit-engine.test.ts +56 -56
  136. package/src/__tests__/elevenlabs-client.test.ts +117 -91
  137. package/src/__tests__/elevenlabs-config.test.ts +32 -31
  138. package/src/__tests__/email-classifier.test.ts +15 -12
  139. package/src/__tests__/email-cli.test.ts +121 -108
  140. package/src/__tests__/emit-signal-routing-intent.test.ts +76 -69
  141. package/src/__tests__/encrypted-store.test.ts +180 -154
  142. package/src/__tests__/entity-extractor.test.ts +108 -87
  143. package/src/__tests__/entity-search.test.ts +664 -258
  144. package/src/__tests__/ephemeral-permissions.test.ts +224 -188
  145. package/src/__tests__/event-bus.test.ts +81 -77
  146. package/src/__tests__/extract-email.test.ts +51 -0
  147. package/src/__tests__/file-edit-tool.test.ts +62 -44
  148. package/src/__tests__/file-ops-service.test.ts +131 -114
  149. package/src/__tests__/file-read-tool.test.ts +48 -31
  150. package/src/__tests__/file-write-tool.test.ts +43 -37
  151. package/src/__tests__/filesystem-tools.test.ts +238 -209
  152. package/src/__tests__/followup-tools.test.ts +237 -162
  153. package/src/__tests__/forbidden-legacy-symbols.test.ts +19 -20
  154. package/src/__tests__/frontmatter.test.ts +96 -81
  155. package/src/__tests__/fuzzy-match-property.test.ts +75 -81
  156. package/src/__tests__/fuzzy-match.test.ts +71 -65
  157. package/src/__tests__/gateway-client-managed-outbound.test.ts +76 -57
  158. package/src/__tests__/gateway-only-enforcement.test.ts +467 -369
  159. package/src/__tests__/gateway-only-guard.test.ts +54 -56
  160. package/src/__tests__/gemini-image-service.test.ts +113 -100
  161. package/src/__tests__/gemini-provider.test.ts +297 -220
  162. package/src/__tests__/get-weather.test.ts +188 -114
  163. package/src/__tests__/gmail-integration.test.ts +47 -46
  164. package/src/__tests__/guardian-action-conversation-turn.test.ts +226 -171
  165. package/src/__tests__/guardian-action-copy-generator.test.ts +111 -93
  166. package/src/__tests__/guardian-action-followup-executor.test.ts +215 -151
  167. package/src/__tests__/guardian-action-followup-store.test.ts +199 -167
  168. package/src/__tests__/guardian-action-grant-mint-consume.test.ts +297 -250
  169. package/src/__tests__/guardian-action-late-reply.test.ts +462 -316
  170. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +23 -18
  171. package/src/__tests__/guardian-action-store.test.ts +158 -109
  172. package/src/__tests__/guardian-action-sweep.test.ts +114 -100
  173. package/src/__tests__/guardian-actions-endpoint.test.ts +440 -256
  174. package/src/__tests__/guardian-control-plane-policy.test.ts +497 -331
  175. package/src/__tests__/guardian-decision-primitive-canonical.test.ts +217 -215
  176. package/src/__tests__/guardian-dispatch.test.ts +316 -256
  177. package/src/__tests__/guardian-grant-minting.test.ts +247 -178
  178. package/src/__tests__/guardian-outbound-http.test.ts +337 -209
  179. package/src/__tests__/guardian-principal-id-roundtrip.test.ts +99 -96
  180. package/src/__tests__/guardian-question-copy.test.ts +17 -17
  181. package/src/__tests__/guardian-question-mode.test.ts +134 -100
  182. package/src/__tests__/guardian-routing-invariants.test.ts +679 -613
  183. package/src/__tests__/guardian-routing-state.test.ts +256 -209
  184. package/src/__tests__/guardian-verification-intent-routing.test.ts +94 -88
  185. package/src/__tests__/guardian-verification-voice-binding.test.ts +47 -41
  186. package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +0 -1
  187. package/src/__tests__/handle-user-message-secret-resume.test.ts +43 -21
  188. package/src/__tests__/handlers-add-trust-rule-metadata.test.ts +92 -76
  189. package/src/__tests__/handlers-cu-observation-blob.test.ts +103 -70
  190. package/src/__tests__/handlers-ipc-blob-probe.test.ts +77 -51
  191. package/src/__tests__/handlers-slack-config.test.ts +63 -54
  192. package/src/__tests__/handlers-task-submit-slash.test.ts +18 -18
  193. package/src/__tests__/handlers-telegram-config.test.ts +662 -329
  194. package/src/__tests__/handlers-twitter-config.test.ts +525 -298
  195. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +270 -195
  196. package/src/__tests__/headless-browser-interactions.test.ts +444 -280
  197. package/src/__tests__/headless-browser-navigate.test.ts +116 -79
  198. package/src/__tests__/headless-browser-read-tools.test.ts +123 -86
  199. package/src/__tests__/headless-browser-snapshot.test.ts +71 -56
  200. package/src/__tests__/heartbeat-service.test.ts +76 -58
  201. package/src/__tests__/history-repair-observability.test.ts +14 -14
  202. package/src/__tests__/history-repair.test.ts +171 -167
  203. package/src/__tests__/home-base-bootstrap.test.ts +30 -27
  204. package/src/__tests__/hooks-blocking.test.ts +86 -37
  205. package/src/__tests__/hooks-cli.test.ts +104 -68
  206. package/src/__tests__/hooks-config.test.ts +81 -43
  207. package/src/__tests__/hooks-discovery.test.ts +106 -96
  208. package/src/__tests__/hooks-integration.test.ts +78 -72
  209. package/src/__tests__/hooks-manager.test.ts +99 -61
  210. package/src/__tests__/hooks-runner.test.ts +94 -71
  211. package/src/__tests__/hooks-settings.test.ts +69 -64
  212. package/src/__tests__/hooks-templates.test.ts +85 -54
  213. package/src/__tests__/hooks-ts-runner.test.ts +82 -45
  214. package/src/__tests__/hooks-watch.test.ts +32 -22
  215. package/src/__tests__/host-file-edit-tool.test.ts +190 -148
  216. package/src/__tests__/host-file-read-tool.test.ts +86 -63
  217. package/src/__tests__/host-file-write-tool.test.ts +98 -64
  218. package/src/__tests__/host-shell-tool.test.ts +342 -233
  219. package/src/__tests__/inbound-invite-redemption.test.ts +194 -152
  220. package/src/__tests__/ingress-member-store.test.ts +163 -159
  221. package/src/__tests__/ingress-reconcile.test.ts +183 -142
  222. package/src/__tests__/ingress-routes-http.test.ts +441 -356
  223. package/src/__tests__/ingress-url-consistency.test.ts +125 -64
  224. package/src/__tests__/integration-status.test.ts +93 -73
  225. package/src/__tests__/intent-routing.test.ts +148 -118
  226. package/src/__tests__/invite-redemption-service.test.ts +163 -121
  227. package/src/__tests__/ipc-blob-store.test.ts +104 -91
  228. package/src/__tests__/ipc-contract-inventory.test.ts +27 -15
  229. package/src/__tests__/ipc-contract.test.ts +24 -23
  230. package/src/__tests__/ipc-protocol.test.ts +52 -46
  231. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +61 -50
  232. package/src/__tests__/ipc-snapshot.test.ts +1135 -1056
  233. package/src/__tests__/ipc-validate.test.ts +240 -179
  234. package/src/__tests__/key-migration.test.ts +123 -90
  235. package/src/__tests__/keychain.test.ts +150 -123
  236. package/src/__tests__/lifecycle-docs-guard.test.ts +65 -64
  237. package/src/__tests__/llm-usage-store.test.ts +112 -87
  238. package/src/__tests__/managed-skill-lifecycle.test.ts +147 -108
  239. package/src/__tests__/managed-store.test.ts +411 -360
  240. package/src/__tests__/mcp-cli.test.ts +189 -123
  241. package/src/__tests__/mcp-health-check.test.ts +26 -21
  242. package/src/__tests__/media-generate-image.test.ts +122 -99
  243. package/src/__tests__/media-reuse-story.e2e.test.ts +282 -214
  244. package/src/__tests__/media-visibility-policy.test.ts +86 -38
  245. package/src/__tests__/memory-context-benchmark.benchmark.test.ts +146 -100
  246. package/src/__tests__/memory-lifecycle-e2e.test.ts +385 -297
  247. package/src/__tests__/memory-query-builder.test.ts +32 -33
  248. package/src/__tests__/memory-recall-quality.test.ts +761 -407
  249. package/src/__tests__/memory-regressions.experimental.test.ts +443 -380
  250. package/src/__tests__/memory-regressions.test.ts +3725 -2642
  251. package/src/__tests__/memory-retrieval-budget.test.ts +7 -8
  252. package/src/__tests__/memory-retrieval.benchmark.test.ts +144 -109
  253. package/src/__tests__/memory-upsert-concurrency.test.ts +292 -201
  254. package/src/__tests__/messaging-send-tool.test.ts +36 -29
  255. package/src/__tests__/migration-cli-flows.test.ts +69 -53
  256. package/src/__tests__/migration-ordering.test.ts +103 -86
  257. package/src/__tests__/mime-builder.test.ts +55 -32
  258. package/src/__tests__/mock-signup-server.test.ts +384 -246
  259. package/src/__tests__/model-intents.test.ts +61 -37
  260. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +9 -12
  261. package/src/__tests__/no-is-trusted-guard.test.ts +24 -21
  262. package/src/__tests__/non-member-access-request.test.ts +294 -249
  263. package/src/__tests__/notification-broadcaster.test.ts +99 -81
  264. package/src/__tests__/notification-decision-fallback.test.ts +223 -178
  265. package/src/__tests__/notification-decision-strategy.test.ts +375 -337
  266. package/src/__tests__/notification-deep-link.test.ts +67 -61
  267. package/src/__tests__/notification-guardian-path.test.ts +248 -206
  268. package/src/__tests__/notification-routing-intent.test.ts +166 -93
  269. package/src/__tests__/notification-telegram-adapter.test.ts +60 -46
  270. package/src/__tests__/notification-thread-candidate-validation.test.ts +78 -75
  271. package/src/__tests__/notification-thread-candidates.test.ts +64 -61
  272. package/src/__tests__/oauth-callback-registry.test.ts +40 -30
  273. package/src/__tests__/oauth-connect-handler.test.ts +109 -89
  274. package/src/__tests__/oauth-scope-policy.test.ts +63 -55
  275. package/src/__tests__/oauth2-gateway-transport.test.ts +252 -174
  276. package/src/__tests__/onboarding-starter-tasks.test.ts +93 -89
  277. package/src/__tests__/onboarding-template-contract.test.ts +93 -94
  278. package/src/__tests__/openai-provider.test.ts +366 -274
  279. package/src/__tests__/pairing-concurrent.test.ts +18 -12
  280. package/src/__tests__/pairing-routes.test.ts +45 -41
  281. package/src/__tests__/parallel-tool.benchmark.test.ts +108 -58
  282. package/src/__tests__/parser.test.ts +316 -226
  283. package/src/__tests__/path-classifier.test.ts +24 -25
  284. package/src/__tests__/path-policy.test.ts +187 -147
  285. package/src/__tests__/phone.test.ts +36 -36
  286. package/src/__tests__/platform-move-helper.test.ts +48 -40
  287. package/src/__tests__/platform-socket-path.test.ts +23 -24
  288. package/src/__tests__/platform-workspace-migration.test.ts +464 -414
  289. package/src/__tests__/platform.test.ts +61 -53
  290. package/src/__tests__/playbook-execution.test.ts +397 -265
  291. package/src/__tests__/playbook-tools.test.ts +267 -196
  292. package/src/__tests__/prebuilt-home-base-seed.test.ts +30 -27
  293. package/src/__tests__/pricing.test.ts +316 -136
  294. package/src/__tests__/profile-compiler.test.ts +206 -188
  295. package/src/__tests__/provider-commit-message-generator.test.ts +114 -106
  296. package/src/__tests__/provider-error-scenarios.test.ts +212 -158
  297. package/src/__tests__/provider-fail-open-selection.test.ts +51 -44
  298. package/src/__tests__/provider-registry-ollama.test.ts +13 -9
  299. package/src/__tests__/provider-streaming.benchmark.test.ts +232 -183
  300. package/src/__tests__/proxy-approval-callback.test.ts +180 -119
  301. package/src/__tests__/public-ingress-urls.test.ts +112 -94
  302. package/src/__tests__/qdrant-manager.test.ts +147 -98
  303. package/src/__tests__/ratelimit.test.ts +152 -82
  304. package/src/__tests__/recording-handler.test.ts +273 -151
  305. package/src/__tests__/recording-intent-fallback.test.ts +94 -75
  306. package/src/__tests__/recording-intent-handler.test.ts +422 -292
  307. package/src/__tests__/recording-intent.test.ts +578 -379
  308. package/src/__tests__/recording-state-machine.test.ts +530 -316
  309. package/src/__tests__/recurrence-engine-rruleset.test.ts +150 -92
  310. package/src/__tests__/recurrence-engine.test.ts +81 -41
  311. package/src/__tests__/recurrence-types.test.ts +63 -44
  312. package/src/__tests__/relay-server.test.ts +2131 -1602
  313. package/src/__tests__/reminder-store.test.ts +158 -80
  314. package/src/__tests__/reminder.test.ts +113 -109
  315. package/src/__tests__/remote-skill-policy.test.ts +96 -72
  316. package/src/__tests__/request-file-tool.test.ts +74 -67
  317. package/src/__tests__/response-tier.test.ts +131 -74
  318. package/src/__tests__/runtime-attachment-metadata.test.ts +107 -70
  319. package/src/__tests__/runtime-events-sse-parity.test.ts +167 -145
  320. package/src/__tests__/runtime-events-sse.test.ts +67 -51
  321. package/src/__tests__/sandbox-diagnostics.test.ts +66 -56
  322. package/src/__tests__/sandbox-host-parity.test.ts +377 -301
  323. package/src/__tests__/scaffold-managed-skill-tool.test.ts +213 -161
  324. package/src/__tests__/schedule-store.test.ts +268 -205
  325. package/src/__tests__/schedule-tools.test.ts +702 -524
  326. package/src/__tests__/scheduler-recurrence.test.ts +240 -130
  327. package/src/__tests__/scoped-approval-grants.test.ts +258 -168
  328. package/src/__tests__/scoped-grant-security-matrix.test.ts +160 -146
  329. package/src/__tests__/script-proxy-certs.test.ts +38 -35
  330. package/src/__tests__/script-proxy-connect-tunnel.test.ts +71 -46
  331. package/src/__tests__/script-proxy-decision-trace.test.ts +161 -84
  332. package/src/__tests__/script-proxy-http-forwarder.test.ts +146 -129
  333. package/src/__tests__/script-proxy-injection-runtime.test.ts +139 -113
  334. package/src/__tests__/script-proxy-mitm-handler.test.ts +226 -142
  335. package/src/__tests__/script-proxy-policy-runtime.test.ts +126 -86
  336. package/src/__tests__/script-proxy-policy.test.ts +308 -153
  337. package/src/__tests__/script-proxy-rewrite-specificity.test.ts +74 -62
  338. package/src/__tests__/script-proxy-router.test.ts +111 -77
  339. package/src/__tests__/script-proxy-session-manager.test.ts +156 -113
  340. package/src/__tests__/script-proxy-session-runtime.test.ts +28 -24
  341. package/src/__tests__/secret-allowlist.test.ts +105 -90
  342. package/src/__tests__/secret-ingress-handler.test.ts +41 -30
  343. package/src/__tests__/secret-onetime-send.test.ts +67 -50
  344. package/src/__tests__/secret-prompt-log-hygiene.test.ts +35 -31
  345. package/src/__tests__/secret-response-routing.test.ts +50 -41
  346. package/src/__tests__/secret-scanner-executor.test.ts +152 -111
  347. package/src/__tests__/secret-scanner.test.ts +495 -413
  348. package/src/__tests__/secure-keys.test.ts +132 -121
  349. package/src/__tests__/send-endpoint-busy.test.ts +313 -232
  350. package/src/__tests__/send-notification-tool.test.ts +43 -42
  351. package/src/__tests__/sensitive-output-placeholders.test.ts +72 -64
  352. package/src/__tests__/sequence-store.test.ts +335 -167
  353. package/src/__tests__/server-history-render.test.ts +341 -202
  354. package/src/__tests__/session-abort-tool-results.test.ts +133 -70
  355. package/src/__tests__/session-approval-overrides.test.ts +93 -91
  356. package/src/__tests__/session-confirmation-signals.test.ts +252 -160
  357. package/src/__tests__/session-conflict-gate.test.ts +775 -585
  358. package/src/__tests__/session-error.test.ts +222 -191
  359. package/src/__tests__/session-evictor.test.ts +79 -62
  360. package/src/__tests__/session-init.benchmark.test.ts +170 -108
  361. package/src/__tests__/session-load-history-repair.test.ts +273 -139
  362. package/src/__tests__/session-messaging-secret-redirect.test.ts +130 -90
  363. package/src/__tests__/session-pre-run-repair.test.ts +106 -59
  364. package/src/__tests__/session-profile-injection.test.ts +198 -130
  365. package/src/__tests__/session-provider-retry-repair.test.ts +223 -141
  366. package/src/__tests__/session-queue.test.ts +624 -321
  367. package/src/__tests__/session-runtime-assembly.test.ts +425 -329
  368. package/src/__tests__/session-runtime-workspace.test.ts +69 -61
  369. package/src/__tests__/session-skill-tools.test.ts +973 -678
  370. package/src/__tests__/session-slash-known.test.ts +185 -133
  371. package/src/__tests__/session-slash-queue.test.ts +147 -81
  372. package/src/__tests__/session-slash-unknown.test.ts +135 -90
  373. package/src/__tests__/session-surfaces-task-progress.test.ts +122 -87
  374. package/src/__tests__/session-tool-setup-app-refresh.test.ts +338 -177
  375. package/src/__tests__/session-tool-setup-memory-scope.test.ts +63 -40
  376. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +60 -37
  377. package/src/__tests__/session-tool-setup-tools-disabled.test.ts +28 -26
  378. package/src/__tests__/session-undo.test.ts +43 -30
  379. package/src/__tests__/session-workspace-cache-state.test.ts +108 -67
  380. package/src/__tests__/session-workspace-injection.test.ts +245 -117
  381. package/src/__tests__/session-workspace-tool-tracking.test.ts +260 -93
  382. package/src/__tests__/shared-filesystem-errors.test.ts +47 -47
  383. package/src/__tests__/shell-credential-ref.test.ts +126 -90
  384. package/src/__tests__/shell-identity.test.ts +134 -111
  385. package/src/__tests__/shell-parser-fuzz.test.ts +263 -179
  386. package/src/__tests__/shell-parser-property.test.ts +435 -288
  387. package/src/__tests__/shell-tool-proxy-mode.test.ts +142 -70
  388. package/src/__tests__/size-guard.test.ts +42 -44
  389. package/src/__tests__/skill-feature-flags-integration.test.ts +79 -52
  390. package/src/__tests__/skill-feature-flags.test.ts +75 -47
  391. package/src/__tests__/skill-include-graph.test.ts +143 -148
  392. package/src/__tests__/skill-load-feature-flag.test.ts +94 -59
  393. package/src/__tests__/skill-load-tool.test.ts +371 -199
  394. package/src/__tests__/skill-projection-feature-flag.test.ts +131 -88
  395. package/src/__tests__/skill-projection.benchmark.test.ts +93 -65
  396. package/src/__tests__/skill-script-runner-host.test.ts +460 -250
  397. package/src/__tests__/skill-script-runner-sandbox.test.ts +168 -108
  398. package/src/__tests__/skill-script-runner.test.ts +115 -74
  399. package/src/__tests__/skill-tool-factory.test.ts +140 -96
  400. package/src/__tests__/skill-tool-manifest.test.ts +306 -210
  401. package/src/__tests__/skill-version-hash.test.ts +70 -56
  402. package/src/__tests__/skills.test.ts +0 -1
  403. package/src/__tests__/slack-channel-config.test.ts +127 -84
  404. package/src/__tests__/slack-skill.test.ts +60 -47
  405. package/src/__tests__/slash-commands-catalog.test.ts +37 -31
  406. package/src/__tests__/slash-commands-parser.test.ts +71 -64
  407. package/src/__tests__/slash-commands-resolver.test.ts +143 -107
  408. package/src/__tests__/slash-commands-rewrite.test.ts +22 -22
  409. package/src/__tests__/sms-messaging-provider.test.ts +74 -47
  410. package/src/__tests__/speaker-identification.test.ts +28 -25
  411. package/src/__tests__/starter-bundle.test.ts +27 -23
  412. package/src/__tests__/starter-task-flow.test.ts +67 -52
  413. package/src/__tests__/subagent-manager-notify.test.ts +154 -108
  414. package/src/__tests__/subagent-tools.test.ts +311 -270
  415. package/src/__tests__/subagent-types.test.ts +40 -40
  416. package/src/__tests__/surface-mutex-cleanup.test.ts +42 -30
  417. package/src/__tests__/swarm-dag-pathological.test.ts +122 -111
  418. package/src/__tests__/swarm-orchestrator.test.ts +135 -101
  419. package/src/__tests__/swarm-plan-validator.test.ts +125 -73
  420. package/src/__tests__/swarm-recursion.test.ts +58 -46
  421. package/src/__tests__/swarm-router-planner.test.ts +99 -74
  422. package/src/__tests__/swarm-session-integration.test.ts +148 -91
  423. package/src/__tests__/swarm-tool.test.ts +65 -45
  424. package/src/__tests__/swarm-worker-backend.test.ts +59 -45
  425. package/src/__tests__/swarm-worker-runner.test.ts +133 -118
  426. package/src/__tests__/system-prompt.test.ts +290 -256
  427. package/src/__tests__/task-compiler.test.ts +176 -120
  428. package/src/__tests__/task-management-tools.test.ts +561 -456
  429. package/src/__tests__/task-memory-cleanup.test.ts +627 -362
  430. package/src/__tests__/task-runner.test.ts +117 -94
  431. package/src/__tests__/task-scheduler.test.ts +113 -84
  432. package/src/__tests__/task-tools.test.ts +349 -264
  433. package/src/__tests__/terminal-sandbox.test.ts +138 -108
  434. package/src/__tests__/terminal-tools.test.ts +350 -305
  435. package/src/__tests__/thread-seed-composer.test.ts +307 -180
  436. package/src/__tests__/tool-approval-handler.test.ts +238 -137
  437. package/src/__tests__/tool-audit-listener.test.ts +69 -69
  438. package/src/__tests__/tool-domain-event-publisher.test.ts +142 -132
  439. package/src/__tests__/tool-execution-abort-cleanup.test.ts +153 -146
  440. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +136 -105
  441. package/src/__tests__/tool-executor-lifecycle-events.test.ts +355 -239
  442. package/src/__tests__/tool-executor-redaction.test.ts +112 -109
  443. package/src/__tests__/tool-executor-shell-integration.test.ts +130 -79
  444. package/src/__tests__/tool-executor.test.ts +1274 -674
  445. package/src/__tests__/tool-grant-request-escalation.test.ts +401 -283
  446. package/src/__tests__/tool-metrics-listener.test.ts +97 -85
  447. package/src/__tests__/tool-notification-listener.test.ts +42 -25
  448. package/src/__tests__/tool-permission-simulate-handler.test.ts +137 -113
  449. package/src/__tests__/tool-policy.test.ts +44 -25
  450. package/src/__tests__/tool-profiling-listener.test.ts +99 -93
  451. package/src/__tests__/tool-result-truncation.test.ts +5 -4
  452. package/src/__tests__/tool-trace-listener.test.ts +131 -111
  453. package/src/__tests__/top-level-renderer.test.ts +62 -58
  454. package/src/__tests__/top-level-scanner.test.ts +68 -64
  455. package/src/__tests__/trace-emitter.test.ts +56 -56
  456. package/src/__tests__/trust-context-guards.test.ts +65 -65
  457. package/src/__tests__/trust-store.test.ts +1239 -806
  458. package/src/__tests__/trusted-contact-approval-notifier.test.ts +339 -275
  459. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +484 -373
  460. package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +264 -241
  461. package/src/__tests__/trusted-contact-multichannel.test.ts +182 -142
  462. package/src/__tests__/trusted-contact-verification.test.ts +251 -231
  463. package/src/__tests__/turn-commit.test.ts +259 -200
  464. package/src/__tests__/twilio-config.test.ts +49 -41
  465. package/src/__tests__/twilio-provider.test.ts +140 -126
  466. package/src/__tests__/twilio-rest.test.ts +22 -18
  467. package/src/__tests__/twilio-routes-elevenlabs.test.ts +188 -162
  468. package/src/__tests__/twilio-routes-twiml.test.ts +55 -55
  469. package/src/__tests__/twilio-routes.test.ts +389 -281
  470. package/src/__tests__/twitter-auth-handler.test.ts +184 -139
  471. package/src/__tests__/twitter-cli-error-shaping.test.ts +88 -73
  472. package/src/__tests__/twitter-cli-routing.test.ts +146 -99
  473. package/src/__tests__/twitter-oauth-client.test.ts +82 -65
  474. package/src/__tests__/update-bulletin-format.test.ts +69 -66
  475. package/src/__tests__/update-bulletin-state.test.ts +66 -60
  476. package/src/__tests__/update-bulletin.test.ts +150 -114
  477. package/src/__tests__/update-template-contract.test.ts +15 -10
  478. package/src/__tests__/url-safety.test.ts +288 -265
  479. package/src/__tests__/user-reference.test.ts +32 -32
  480. package/src/__tests__/view-image-tool.test.ts +118 -96
  481. package/src/__tests__/voice-invite-redemption.test.ts +111 -106
  482. package/src/__tests__/voice-quality.test.ts +117 -102
  483. package/src/__tests__/voice-scoped-grant-consumer.test.ts +204 -146
  484. package/src/__tests__/voice-session-bridge.test.ts +351 -216
  485. package/src/__tests__/weather-skill-regression.test.ts +170 -120
  486. package/src/__tests__/web-fetch.test.ts +664 -526
  487. package/src/__tests__/web-search.test.ts +379 -213
  488. package/src/__tests__/work-item-output.test.ts +90 -53
  489. package/src/__tests__/workspace-git-service.test.ts +437 -356
  490. package/src/__tests__/workspace-heartbeat-service.test.ts +125 -91
  491. package/src/__tests__/workspace-lifecycle.test.ts +98 -64
  492. package/src/__tests__/workspace-policy.test.ts +139 -71
  493. package/src/commands/__tests__/cc-command-registry.test.ts +142 -134
  494. package/src/config/__tests__/feature-flag-registry-guard.test.ts +48 -39
  495. package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +44 -4
  496. package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +0 -1
  497. package/src/config/bundled-skills/messaging/SKILL.md +9 -7
  498. package/src/config/bundled-skills/messaging/tools/gmail-outreach-scan.ts +15 -5
  499. package/src/config/bundled-skills/messaging/tools/gmail-sender-digest.ts +16 -5
  500. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +11 -7
  501. package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +34 -32
  502. package/src/config/bundled-tool-registry.ts +2 -0
  503. package/src/config/env.ts +38 -29
  504. package/src/daemon/handlers/skills.ts +18 -10
  505. package/src/daemon/ipc-contract/messages.ts +1 -0
  506. package/src/daemon/ipc-contract/surfaces.ts +7 -1
  507. package/src/daemon/session-agent-loop-handlers.ts +5 -0
  508. package/src/daemon/session-agent-loop.ts +1 -1
  509. package/src/daemon/session-process.ts +1 -1
  510. package/src/daemon/session-surfaces.ts +42 -2
  511. package/src/memory/db-connection.ts +16 -10
  512. package/src/messaging/providers/gmail/adapter.ts +10 -3
  513. package/src/messaging/providers/gmail/client.ts +280 -72
  514. package/src/runtime/auth/__tests__/context.test.ts +75 -65
  515. package/src/runtime/auth/__tests__/credential-service.test.ts +137 -114
  516. package/src/runtime/auth/__tests__/guard-tests.test.ts +84 -90
  517. package/src/runtime/auth/__tests__/ipc-auth-context.test.ts +40 -40
  518. package/src/runtime/auth/__tests__/middleware.test.ts +80 -74
  519. package/src/runtime/auth/__tests__/policy.test.ts +9 -9
  520. package/src/runtime/auth/__tests__/route-policy.test.ts +76 -65
  521. package/src/runtime/auth/__tests__/scopes.test.ts +68 -60
  522. package/src/runtime/auth/__tests__/subject.test.ts +54 -54
  523. package/src/runtime/auth/__tests__/token-service.test.ts +115 -108
  524. package/src/runtime/auth/scopes.ts +3 -0
  525. package/src/runtime/auth/token-service.ts +78 -48
  526. package/src/runtime/auth/types.ts +2 -1
  527. package/src/runtime/http-server.ts +2 -1
  528. package/src/security/secure-keys.ts +103 -53
  529. package/src/sequence/reply-matcher.ts +10 -6
  530. package/src/skills/frontmatter.ts +9 -6
  531. package/src/tools/browser/__tests__/auth-cache.test.ts +69 -63
  532. package/src/tools/browser/__tests__/auth-detector.test.ts +218 -157
  533. package/src/tools/browser/__tests__/jit-auth.test.ts +83 -99
  534. package/src/tools/ui-surface/definitions.ts +2 -1
  535. package/src/util/platform.ts +0 -12
  536. package/docs/architecture/http-token-refresh.md +0 -274
@@ -1,10 +1,14 @@
1
- import { mkdirSync, realpathSync,rmSync, writeFileSync } from 'node:fs';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
-
5
- import { afterEach,beforeEach, describe, expect, test } from 'bun:test';
6
-
7
- import { type DirectiveRequest,parseDirectives, resolveHostDirective, resolveSandboxDirective } from '../daemon/assistant-attachments.js';
1
+ import { mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
5
+
6
+ import {
7
+ type DirectiveRequest,
8
+ parseDirectives,
9
+ resolveHostDirective,
10
+ resolveSandboxDirective,
11
+ } from "../daemon/assistant-attachments.js";
8
12
 
9
13
  // Use realpath to avoid macOS /tmp → /private/tmp symlink mismatches
10
14
  const RAW_TEST_DIR = join(tmpdir(), `vellum-sandbox-test-${Date.now()}`);
@@ -15,40 +19,41 @@ const TEST_DIR = realpathSync(RAW_TEST_DIR);
15
19
  // parseDirectives
16
20
  // ---------------------------------------------------------------------------
17
21
 
18
- describe('parseDirectives', () => {
19
- test('parses a single sandbox directive with all attributes', () => {
20
- const text = 'Here is the report:\n<vellum-attachment source="sandbox" path="output/report.pdf" filename="report.pdf" mime_type="application/pdf" />';
22
+ describe("parseDirectives", () => {
23
+ test("parses a single sandbox directive with all attributes", () => {
24
+ const text =
25
+ 'Here is the report:\n<vellum-attachment source="sandbox" path="output/report.pdf" filename="report.pdf" mime_type="application/pdf" />';
21
26
  const result = parseDirectives(text);
22
27
 
23
28
  expect(result.directiveRequests).toHaveLength(1);
24
29
  expect(result.directiveRequests[0]).toEqual({
25
- source: 'sandbox',
26
- path: 'output/report.pdf',
27
- filename: 'report.pdf',
28
- mimeType: 'application/pdf',
30
+ source: "sandbox",
31
+ path: "output/report.pdf",
32
+ filename: "report.pdf",
33
+ mimeType: "application/pdf",
29
34
  });
30
- expect(result.cleanText).toBe('Here is the report:');
35
+ expect(result.cleanText).toBe("Here is the report:");
31
36
  expect(result.parseWarnings).toHaveLength(0);
32
37
  });
33
38
 
34
- test('defaults source to sandbox when omitted', () => {
39
+ test("defaults source to sandbox when omitted", () => {
35
40
  const text = '<vellum-attachment path="chart.png" />';
36
41
  const result = parseDirectives(text);
37
42
 
38
43
  expect(result.directiveRequests).toHaveLength(1);
39
- expect(result.directiveRequests[0].source).toBe('sandbox');
44
+ expect(result.directiveRequests[0].source).toBe("sandbox");
40
45
  });
41
46
 
42
- test('parses host source', () => {
47
+ test("parses host source", () => {
43
48
  const text = '<vellum-attachment source="host" path="/Users/me/doc.pdf" />';
44
49
  const result = parseDirectives(text);
45
50
 
46
51
  expect(result.directiveRequests).toHaveLength(1);
47
- expect(result.directiveRequests[0].source).toBe('host');
48
- expect(result.directiveRequests[0].path).toBe('/Users/me/doc.pdf');
52
+ expect(result.directiveRequests[0].source).toBe("host");
53
+ expect(result.directiveRequests[0].path).toBe("/Users/me/doc.pdf");
49
54
  });
50
55
 
51
- test('leaves optional filename and mime_type undefined when absent', () => {
56
+ test("leaves optional filename and mime_type undefined when absent", () => {
52
57
  const text = '<vellum-attachment path="file.txt" />';
53
58
  const result = parseDirectives(text);
54
59
 
@@ -56,23 +61,23 @@ describe('parseDirectives', () => {
56
61
  expect(result.directiveRequests[0].mimeType).toBeUndefined();
57
62
  });
58
63
 
59
- test('parses multiple directives preserving order', () => {
64
+ test("parses multiple directives preserving order", () => {
60
65
  const text = [
61
- 'Results:',
66
+ "Results:",
62
67
  '<vellum-attachment path="a.png" />',
63
- 'And also:',
68
+ "And also:",
64
69
  '<vellum-attachment path="b.pdf" />',
65
- ].join('\n');
70
+ ].join("\n");
66
71
 
67
72
  const result = parseDirectives(text);
68
73
 
69
74
  expect(result.directiveRequests).toHaveLength(2);
70
- expect(result.directiveRequests[0].path).toBe('a.png');
71
- expect(result.directiveRequests[1].path).toBe('b.pdf');
72
- expect(result.cleanText).toBe('Results:\n\nAnd also:');
75
+ expect(result.directiveRequests[0].path).toBe("a.png");
76
+ expect(result.directiveRequests[1].path).toBe("b.pdf");
77
+ expect(result.cleanText).toBe("Results:\n\nAnd also:");
73
78
  });
74
79
 
75
- test('rejects directive without path attribute', () => {
80
+ test("rejects directive without path attribute", () => {
76
81
  const text = '<vellum-attachment source="sandbox" />';
77
82
  const result = parseDirectives(text);
78
83
 
@@ -80,36 +85,36 @@ describe('parseDirectives', () => {
80
85
  expect(result.parseWarnings).toHaveLength(1);
81
86
  expect(result.parseWarnings[0]).toContain('missing required "path"');
82
87
  // Malformed tag preserved in text
83
- expect(result.cleanText).toContain('<vellum-attachment');
88
+ expect(result.cleanText).toContain("<vellum-attachment");
84
89
  });
85
90
 
86
- test('rejects directive with invalid source value', () => {
91
+ test("rejects directive with invalid source value", () => {
87
92
  const text = '<vellum-attachment source="cloud" path="x.txt" />';
88
93
  const result = parseDirectives(text);
89
94
 
90
95
  expect(result.directiveRequests).toHaveLength(0);
91
96
  expect(result.parseWarnings).toHaveLength(1);
92
97
  expect(result.parseWarnings[0]).toContain('invalid source="cloud"');
93
- expect(result.cleanText).toContain('<vellum-attachment');
98
+ expect(result.cleanText).toContain("<vellum-attachment");
94
99
  });
95
100
 
96
- test('handles mixed valid and invalid directives', () => {
101
+ test("handles mixed valid and invalid directives", () => {
97
102
  const text = [
98
103
  '<vellum-attachment path="good.png" />',
99
104
  '<vellum-attachment source="nope" path="bad.txt" />',
100
105
  '<vellum-attachment path="also-good.pdf" />',
101
- ].join('\n');
106
+ ].join("\n");
102
107
 
103
108
  const result = parseDirectives(text);
104
109
 
105
110
  expect(result.directiveRequests).toHaveLength(2);
106
- expect(result.directiveRequests[0].path).toBe('good.png');
107
- expect(result.directiveRequests[1].path).toBe('also-good.pdf');
111
+ expect(result.directiveRequests[0].path).toBe("good.png");
112
+ expect(result.directiveRequests[1].path).toBe("also-good.pdf");
108
113
  expect(result.parseWarnings).toHaveLength(1);
109
114
  });
110
115
 
111
- test('returns original text when no directives present', () => {
112
- const text = 'Hello world, no attachments here.';
116
+ test("returns original text when no directives present", () => {
117
+ const text = "Hello world, no attachments here.";
113
118
  const result = parseDirectives(text);
114
119
 
115
120
  expect(result.cleanText).toBe(text);
@@ -117,55 +122,57 @@ describe('parseDirectives', () => {
117
122
  expect(result.parseWarnings).toHaveLength(0);
118
123
  });
119
124
 
120
- test('preserves whitespace when no directives are present', () => {
121
- const text = '\n Leading space\n\n\n\nTrailing space \n';
125
+ test("preserves whitespace when no directives are present", () => {
126
+ const text = "\n Leading space\n\n\n\nTrailing space \n";
122
127
  const result = parseDirectives(text);
123
128
 
124
129
  expect(result.cleanText).toBe(text);
125
130
  expect(result.directiveRequests).toHaveLength(0);
126
131
  });
127
132
 
128
- test('preserves non-self-closing tags as plain text', () => {
129
- const text = '<vellum-attachment path="file.txt">content</vellum-attachment>';
133
+ test("preserves non-self-closing tags as plain text", () => {
134
+ const text =
135
+ '<vellum-attachment path="file.txt">content</vellum-attachment>';
130
136
  const result = parseDirectives(text);
131
137
 
132
138
  // The regex only matches self-closing tags, so non-self-closing is not matched
133
139
  expect(result.directiveRequests).toHaveLength(0);
134
- expect(result.cleanText).toContain('content</vellum-attachment>');
140
+ expect(result.cleanText).toContain("content</vellum-attachment>");
135
141
  });
136
142
 
137
- test('handles single-quoted attributes', () => {
138
- const text = "<vellum-attachment path='report.pdf' filename='my report.pdf' />";
143
+ test("handles single-quoted attributes", () => {
144
+ const text =
145
+ "<vellum-attachment path='report.pdf' filename='my report.pdf' />";
139
146
  const result = parseDirectives(text);
140
147
 
141
148
  expect(result.directiveRequests).toHaveLength(1);
142
- expect(result.directiveRequests[0].path).toBe('report.pdf');
143
- expect(result.directiveRequests[0].filename).toBe('my report.pdf');
149
+ expect(result.directiveRequests[0].path).toBe("report.pdf");
150
+ expect(result.directiveRequests[0].filename).toBe("my report.pdf");
144
151
  });
145
152
 
146
- test('collapses excess blank lines after tag removal', () => {
153
+ test("collapses excess blank lines after tag removal", () => {
147
154
  const text = 'Before\n\n<vellum-attachment path="x.png" />\n\n\nAfter';
148
155
  const result = parseDirectives(text);
149
156
 
150
157
  // Should not have triple+ newlines
151
158
  expect(result.cleanText).not.toMatch(/\n{3,}/);
152
- expect(result.cleanText).toBe('Before\n\nAfter');
159
+ expect(result.cleanText).toBe("Before\n\nAfter");
153
160
  });
154
161
 
155
- test('handles directive with multiline attributes', () => {
162
+ test("handles directive with multiline attributes", () => {
156
163
  const text = [
157
- '<vellum-attachment',
164
+ "<vellum-attachment",
158
165
  ' source="host"',
159
166
  ' path="/tmp/data.csv"',
160
167
  ' mime_type="text/csv"',
161
- '/>',
162
- ].join('\n');
168
+ "/>",
169
+ ].join("\n");
163
170
  const result = parseDirectives(text);
164
171
 
165
172
  expect(result.directiveRequests).toHaveLength(1);
166
- expect(result.directiveRequests[0].source).toBe('host');
167
- expect(result.directiveRequests[0].path).toBe('/tmp/data.csv');
168
- expect(result.directiveRequests[0].mimeType).toBe('text/csv');
173
+ expect(result.directiveRequests[0].source).toBe("host");
174
+ expect(result.directiveRequests[0].path).toBe("/tmp/data.csv");
175
+ expect(result.directiveRequests[0].mimeType).toBe("text/csv");
169
176
  });
170
177
  });
171
178
 
@@ -173,110 +180,138 @@ describe('parseDirectives', () => {
173
180
  // resolveSandboxDirective
174
181
  // ---------------------------------------------------------------------------
175
182
 
176
- describe('resolveSandboxDirective', () => {
183
+ describe("resolveSandboxDirective", () => {
177
184
  beforeEach(() => {
178
- mkdirSync(join(TEST_DIR, 'sub'), { recursive: true });
185
+ mkdirSync(join(TEST_DIR, "sub"), { recursive: true });
179
186
  });
180
187
 
181
188
  afterEach(() => {
182
189
  rmSync(TEST_DIR, { recursive: true, force: true });
183
190
  });
184
191
 
185
- function makeDirective(overrides: Partial<DirectiveRequest> = {}): DirectiveRequest {
192
+ function makeDirective(
193
+ overrides: Partial<DirectiveRequest> = {},
194
+ ): DirectiveRequest {
186
195
  return {
187
- source: 'sandbox',
188
- path: 'hello.txt',
196
+ source: "sandbox",
197
+ path: "hello.txt",
189
198
  filename: undefined,
190
199
  mimeType: undefined,
191
200
  ...overrides,
192
201
  };
193
202
  }
194
203
 
195
- test('resolves a valid sandbox file to a draft', () => {
196
- const filePath = join(TEST_DIR, 'hello.txt');
197
- writeFileSync(filePath, 'hello world');
204
+ test("resolves a valid sandbox file to a draft", () => {
205
+ const filePath = join(TEST_DIR, "hello.txt");
206
+ writeFileSync(filePath, "hello world");
198
207
 
199
- const result = resolveSandboxDirective(makeDirective({ path: 'hello.txt' }), TEST_DIR);
208
+ const result = resolveSandboxDirective(
209
+ makeDirective({ path: "hello.txt" }),
210
+ TEST_DIR,
211
+ );
200
212
 
201
213
  expect(result.draft).not.toBeNull();
202
214
  expect(result.warning).toBeNull();
203
- expect(result.draft!.sourceType).toBe('sandbox_file');
204
- expect(result.draft!.filename).toBe('hello.txt');
205
- expect(result.draft!.mimeType).toBe('text/plain');
215
+ expect(result.draft!.sourceType).toBe("sandbox_file");
216
+ expect(result.draft!.filename).toBe("hello.txt");
217
+ expect(result.draft!.mimeType).toBe("text/plain");
206
218
  expect(result.draft!.sizeBytes).toBe(11);
207
- expect(result.draft!.kind).toBe('document');
219
+ expect(result.draft!.kind).toBe("document");
208
220
  });
209
221
 
210
- test('uses directive filename override when provided', () => {
211
- writeFileSync(join(TEST_DIR, 'data.txt'), 'content');
222
+ test("uses directive filename override when provided", () => {
223
+ writeFileSync(join(TEST_DIR, "data.txt"), "content");
212
224
 
213
225
  const result = resolveSandboxDirective(
214
- makeDirective({ path: 'data.txt', filename: 'custom-name.txt' }),
226
+ makeDirective({ path: "data.txt", filename: "custom-name.txt" }),
215
227
  TEST_DIR,
216
228
  );
217
229
 
218
- expect(result.draft!.filename).toBe('custom-name.txt');
230
+ expect(result.draft!.filename).toBe("custom-name.txt");
219
231
  });
220
232
 
221
- test('uses directive mimeType override when provided', () => {
222
- writeFileSync(join(TEST_DIR, 'data.bin'), Buffer.from([1, 2, 3]));
233
+ test("uses directive mimeType override when provided", () => {
234
+ writeFileSync(join(TEST_DIR, "data.bin"), Buffer.from([1, 2, 3]));
223
235
 
224
236
  const result = resolveSandboxDirective(
225
- makeDirective({ path: 'data.bin', mimeType: 'application/x-custom' }),
237
+ makeDirective({ path: "data.bin", mimeType: "application/x-custom" }),
226
238
  TEST_DIR,
227
239
  );
228
240
 
229
- expect(result.draft!.mimeType).toBe('application/x-custom');
241
+ expect(result.draft!.mimeType).toBe("application/x-custom");
230
242
  });
231
243
 
232
- test('infers MIME type from extension', () => {
233
- writeFileSync(join(TEST_DIR, 'image.png'), Buffer.from([0x89, 0x50, 0x4e, 0x47]));
244
+ test("infers MIME type from extension", () => {
245
+ writeFileSync(
246
+ join(TEST_DIR, "image.png"),
247
+ Buffer.from([0x89, 0x50, 0x4e, 0x47]),
248
+ );
234
249
 
235
- const result = resolveSandboxDirective(makeDirective({ path: 'image.png' }), TEST_DIR);
250
+ const result = resolveSandboxDirective(
251
+ makeDirective({ path: "image.png" }),
252
+ TEST_DIR,
253
+ );
236
254
 
237
- expect(result.draft!.mimeType).toBe('image/png');
238
- expect(result.draft!.kind).toBe('image');
255
+ expect(result.draft!.mimeType).toBe("image/png");
256
+ expect(result.draft!.kind).toBe("image");
239
257
  });
240
258
 
241
- test('rejects paths that escape sandbox boundary', () => {
242
- const result = resolveSandboxDirective(makeDirective({ path: '../../etc/passwd' }), TEST_DIR);
259
+ test("rejects paths that escape sandbox boundary", () => {
260
+ const result = resolveSandboxDirective(
261
+ makeDirective({ path: "../../etc/passwd" }),
262
+ TEST_DIR,
263
+ );
243
264
 
244
265
  expect(result.draft).toBeNull();
245
- expect(result.warning).toContain('outside the working directory');
266
+ expect(result.warning).toContain("outside the working directory");
246
267
  });
247
268
 
248
- test('warns when file does not exist', () => {
249
- const result = resolveSandboxDirective(makeDirective({ path: 'nonexistent.txt' }), TEST_DIR);
269
+ test("warns when file does not exist", () => {
270
+ const result = resolveSandboxDirective(
271
+ makeDirective({ path: "nonexistent.txt" }),
272
+ TEST_DIR,
273
+ );
250
274
 
251
275
  expect(result.draft).toBeNull();
252
- expect(result.warning).toContain('file not found');
276
+ expect(result.warning).toContain("file not found");
253
277
  });
254
278
 
255
- test('warns when path is a directory', () => {
256
- const result = resolveSandboxDirective(makeDirective({ path: 'sub' }), TEST_DIR);
279
+ test("warns when path is a directory", () => {
280
+ const result = resolveSandboxDirective(
281
+ makeDirective({ path: "sub" }),
282
+ TEST_DIR,
283
+ );
257
284
 
258
285
  expect(result.draft).toBeNull();
259
- expect(result.warning).toContain('not a regular file');
286
+ expect(result.warning).toContain("not a regular file");
260
287
  });
261
288
 
262
- test('resolves relative subdirectory paths', () => {
263
- writeFileSync(join(TEST_DIR, 'sub', 'nested.json'), '{"key":"value"}');
289
+ test("resolves relative subdirectory paths", () => {
290
+ writeFileSync(join(TEST_DIR, "sub", "nested.json"), '{"key":"value"}');
264
291
 
265
- const result = resolveSandboxDirective(makeDirective({ path: 'sub/nested.json' }), TEST_DIR);
292
+ const result = resolveSandboxDirective(
293
+ makeDirective({ path: "sub/nested.json" }),
294
+ TEST_DIR,
295
+ );
266
296
 
267
297
  expect(result.draft).not.toBeNull();
268
- expect(result.draft!.filename).toBe('nested.json');
269
- expect(result.draft!.mimeType).toBe('application/json');
298
+ expect(result.draft!.filename).toBe("nested.json");
299
+ expect(result.draft!.mimeType).toBe("application/json");
270
300
  });
271
301
 
272
- test('base64 encodes file content correctly', () => {
273
- const content = 'test content';
274
- writeFileSync(join(TEST_DIR, 'test.txt'), content);
302
+ test("base64 encodes file content correctly", () => {
303
+ const content = "test content";
304
+ writeFileSync(join(TEST_DIR, "test.txt"), content);
275
305
 
276
- const result = resolveSandboxDirective(makeDirective({ path: 'test.txt' }), TEST_DIR);
306
+ const result = resolveSandboxDirective(
307
+ makeDirective({ path: "test.txt" }),
308
+ TEST_DIR,
309
+ );
277
310
 
278
311
  expect(result.draft).not.toBeNull();
279
- const decoded = Buffer.from(result.draft!.dataBase64, 'base64').toString('utf-8');
312
+ const decoded = Buffer.from(result.draft!.dataBase64, "base64").toString(
313
+ "utf-8",
314
+ );
280
315
  expect(decoded).toBe(content);
281
316
  });
282
317
  });
@@ -285,19 +320,21 @@ describe('resolveSandboxDirective', () => {
285
320
  // resolveHostDirective
286
321
  // ---------------------------------------------------------------------------
287
322
 
288
- describe('resolveHostDirective', () => {
323
+ describe("resolveHostDirective", () => {
289
324
  beforeEach(() => {
290
- mkdirSync(join(TEST_DIR, 'host-sub'), { recursive: true });
325
+ mkdirSync(join(TEST_DIR, "host-sub"), { recursive: true });
291
326
  });
292
327
 
293
328
  afterEach(() => {
294
329
  rmSync(TEST_DIR, { recursive: true, force: true });
295
330
  });
296
331
 
297
- function makeHostDirective(overrides: Partial<DirectiveRequest> = {}): DirectiveRequest {
332
+ function makeHostDirective(
333
+ overrides: Partial<DirectiveRequest> = {},
334
+ ): DirectiveRequest {
298
335
  return {
299
- source: 'host',
300
- path: join(TEST_DIR, 'doc.txt'),
336
+ source: "host",
337
+ path: join(TEST_DIR, "doc.txt"),
301
338
  filename: undefined,
302
339
  mimeType: undefined,
303
340
  ...overrides,
@@ -307,96 +344,104 @@ describe('resolveHostDirective', () => {
307
344
  const alwaysApprove = async () => true;
308
345
  const alwaysDeny = async () => false;
309
346
 
310
- test('resolves an approved host file to a draft', async () => {
311
- const filePath = join(TEST_DIR, 'doc.txt');
312
- writeFileSync(filePath, 'host content');
347
+ test("resolves an approved host file to a draft", async () => {
348
+ const filePath = join(TEST_DIR, "doc.txt");
349
+ writeFileSync(filePath, "host content");
313
350
 
314
- const result = await resolveHostDirective(makeHostDirective(), alwaysApprove);
351
+ const result = await resolveHostDirective(
352
+ makeHostDirective(),
353
+ alwaysApprove,
354
+ );
315
355
 
316
356
  expect(result.draft).not.toBeNull();
317
357
  expect(result.warning).toBeNull();
318
- expect(result.draft!.sourceType).toBe('host_file');
319
- expect(result.draft!.filename).toBe('doc.txt');
320
- expect(result.draft!.mimeType).toBe('text/plain');
358
+ expect(result.draft!.sourceType).toBe("host_file");
359
+ expect(result.draft!.filename).toBe("doc.txt");
360
+ expect(result.draft!.mimeType).toBe("text/plain");
321
361
  expect(result.draft!.sizeBytes).toBe(12);
322
362
  });
323
363
 
324
- test('skips when user denies', async () => {
325
- writeFileSync(join(TEST_DIR, 'secret.txt'), 'private');
364
+ test("skips when user denies", async () => {
365
+ writeFileSync(join(TEST_DIR, "secret.txt"), "private");
326
366
 
327
367
  const result = await resolveHostDirective(
328
- makeHostDirective({ path: join(TEST_DIR, 'secret.txt') }),
368
+ makeHostDirective({ path: join(TEST_DIR, "secret.txt") }),
329
369
  alwaysDeny,
330
370
  );
331
371
 
332
372
  expect(result.draft).toBeNull();
333
- expect(result.warning).toContain('access denied by user');
373
+ expect(result.warning).toContain("access denied by user");
334
374
  });
335
375
 
336
- test('rejects relative paths', async () => {
376
+ test("rejects relative paths", async () => {
337
377
  const result = await resolveHostDirective(
338
- makeHostDirective({ path: 'relative/path.txt' }),
378
+ makeHostDirective({ path: "relative/path.txt" }),
339
379
  alwaysApprove,
340
380
  );
341
381
 
342
382
  expect(result.draft).toBeNull();
343
- expect(result.warning).toContain('must be absolute');
383
+ expect(result.warning).toContain("must be absolute");
344
384
  });
345
385
 
346
- test('warns when file does not exist', async () => {
386
+ test("warns when file does not exist", async () => {
347
387
  const result = await resolveHostDirective(
348
- makeHostDirective({ path: join(TEST_DIR, 'nonexistent.txt') }),
388
+ makeHostDirective({ path: join(TEST_DIR, "nonexistent.txt") }),
349
389
  alwaysApprove,
350
390
  );
351
391
 
352
392
  expect(result.draft).toBeNull();
353
- expect(result.warning).toContain('file not found');
393
+ expect(result.warning).toContain("file not found");
354
394
  });
355
395
 
356
- test('warns when path is a directory', async () => {
396
+ test("warns when path is a directory", async () => {
357
397
  const result = await resolveHostDirective(
358
- makeHostDirective({ path: join(TEST_DIR, 'host-sub') }),
398
+ makeHostDirective({ path: join(TEST_DIR, "host-sub") }),
359
399
  alwaysApprove,
360
400
  );
361
401
 
362
402
  expect(result.draft).toBeNull();
363
- expect(result.warning).toContain('not a regular file');
403
+ expect(result.warning).toContain("not a regular file");
364
404
  });
365
405
 
366
- test('uses directive filename and mimeType overrides', async () => {
367
- writeFileSync(join(TEST_DIR, 'data.bin'), Buffer.from([1, 2, 3]));
406
+ test("uses directive filename and mimeType overrides", async () => {
407
+ writeFileSync(join(TEST_DIR, "data.bin"), Buffer.from([1, 2, 3]));
368
408
 
369
409
  const result = await resolveHostDirective(
370
410
  makeHostDirective({
371
- path: join(TEST_DIR, 'data.bin'),
372
- filename: 'custom.dat',
373
- mimeType: 'application/x-custom',
411
+ path: join(TEST_DIR, "data.bin"),
412
+ filename: "custom.dat",
413
+ mimeType: "application/x-custom",
374
414
  }),
375
415
  alwaysApprove,
376
416
  );
377
417
 
378
- expect(result.draft!.filename).toBe('custom.dat');
379
- expect(result.draft!.mimeType).toBe('application/x-custom');
418
+ expect(result.draft!.filename).toBe("custom.dat");
419
+ expect(result.draft!.mimeType).toBe("application/x-custom");
380
420
  });
381
421
 
382
- test('handles approval callback error gracefully', async () => {
383
- writeFileSync(join(TEST_DIR, 'doc.txt'), 'content');
422
+ test("handles approval callback error gracefully", async () => {
423
+ writeFileSync(join(TEST_DIR, "doc.txt"), "content");
384
424
 
385
- const failApprove = async () => { throw new Error('IPC disconnected'); };
425
+ const failApprove = async () => {
426
+ throw new Error("IPC disconnected");
427
+ };
386
428
  const result = await resolveHostDirective(makeHostDirective(), failApprove);
387
429
 
388
430
  expect(result.draft).toBeNull();
389
- expect(result.warning).toContain('approval request failed');
431
+ expect(result.warning).toContain("approval request failed");
390
432
  });
391
433
 
392
- test('calls approve with the resolved absolute path', async () => {
393
- writeFileSync(join(TEST_DIR, 'doc.txt'), 'content');
434
+ test("calls approve with the resolved absolute path", async () => {
435
+ writeFileSync(join(TEST_DIR, "doc.txt"), "content");
394
436
 
395
- let approvedPath = '';
396
- const captureApprove = async (p: string) => { approvedPath = p; return true; };
437
+ let approvedPath = "";
438
+ const captureApprove = async (p: string) => {
439
+ approvedPath = p;
440
+ return true;
441
+ };
397
442
 
398
443
  await resolveHostDirective(makeHostDirective(), captureApprove);
399
444
 
400
- expect(approvedPath).toBe(join(TEST_DIR, 'doc.txt'));
445
+ expect(approvedPath).toBe(join(TEST_DIR, "doc.txt"));
401
446
  });
402
447
  });