@vellumai/assistant 0.6.5 → 0.6.6

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 (443) hide show
  1. package/AGENTS.md +9 -1
  2. package/ARCHITECTURE.md +15 -17
  3. package/Dockerfile +6 -4
  4. package/__tests__/permissions/gateway-threshold-reader.test.ts +283 -0
  5. package/docs/architecture/integrations.md +32 -39
  6. package/docs/architecture/memory.md +25 -30
  7. package/docs/architecture/security.md +7 -6
  8. package/docs/browser-use-architecture-phase2.md +63 -20
  9. package/docs/plugins.md +761 -0
  10. package/examples/plugins/echo/README.md +132 -0
  11. package/examples/plugins/echo/package.json +17 -0
  12. package/examples/plugins/echo/register.ts +187 -0
  13. package/node_modules/@vellumai/egress-proxy/src/types.ts +19 -0
  14. package/openapi.yaml +212 -68
  15. package/package.json +1 -1
  16. package/src/__tests__/app-compiler.test.ts +57 -0
  17. package/src/__tests__/approval-cascade.test.ts +7 -2
  18. package/src/__tests__/auto-analysis-end-to-end.test.ts +1 -0
  19. package/src/__tests__/avatar-generator.test.ts +4 -2
  20. package/src/__tests__/bundled-asset.test.ts +6 -6
  21. package/src/__tests__/catalog-cache.test.ts +69 -0
  22. package/src/__tests__/checker.test.ts +459 -171
  23. package/src/__tests__/circuit-breaker-pipeline.test.ts +406 -0
  24. package/src/__tests__/compaction-events.test.ts +501 -0
  25. package/src/__tests__/compaction-pipeline.test.ts +210 -0
  26. package/src/__tests__/compaction-strip-metadata-clear.test.ts +181 -0
  27. package/src/__tests__/compaction-timeout-recovery.test.ts +262 -0
  28. package/src/__tests__/config-model-image-provider.test.ts +110 -0
  29. package/src/__tests__/config-schema.test.ts +22 -9
  30. package/src/__tests__/config-watcher-cleanup-throttle.test.ts +0 -4
  31. package/src/__tests__/contacts-tools.test.ts +26 -0
  32. package/src/__tests__/context-overflow-policy.test.ts +7 -7
  33. package/src/__tests__/context-window-manager.test.ts +355 -4
  34. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  35. package/src/__tests__/conversation-agent-loop-overflow.test.ts +26 -30
  36. package/src/__tests__/conversation-agent-loop.test.ts +30 -141
  37. package/src/__tests__/conversation-confirmation-signals.test.ts +6 -1
  38. package/src/__tests__/conversation-history-web-search.test.ts +1 -0
  39. package/src/__tests__/conversation-init.benchmark.test.ts +2 -16
  40. package/src/__tests__/conversation-pairing.test.ts +174 -10
  41. package/src/__tests__/conversation-pre-run-repair.test.ts +4 -1
  42. package/src/__tests__/conversation-process-callsite.test.ts +3 -0
  43. package/src/__tests__/conversation-provider-retry-repair.test.ts +16 -7
  44. package/src/__tests__/conversation-queue.test.ts +29 -14
  45. package/src/__tests__/conversation-routes-disk-view.test.ts +7 -6
  46. package/src/__tests__/conversation-runtime-assembly.test.ts +155 -110
  47. package/src/__tests__/conversation-runtime-workspace.test.ts +23 -38
  48. package/src/__tests__/conversation-seed-composer.test.ts +2 -2
  49. package/src/__tests__/conversation-slash-queue.test.ts +7 -2
  50. package/src/__tests__/conversation-slash-unknown.test.ts +25 -2
  51. package/src/__tests__/conversation-speed-override.test.ts +6 -1
  52. package/src/__tests__/conversation-title-service.test.ts +116 -0
  53. package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +41 -2
  54. package/src/__tests__/conversation-usage.test.ts +1 -1
  55. package/src/__tests__/conversation-workspace-cache-state.test.ts +4 -1
  56. package/src/__tests__/conversation-workspace-injection.test.ts +3 -0
  57. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -1
  58. package/src/__tests__/credential-health-service.test.ts +78 -9
  59. package/src/__tests__/credential-security-invariants.test.ts +2 -2
  60. package/src/__tests__/db-schedule-syntax-migration.test.ts +1 -0
  61. package/src/__tests__/empty-response-pipeline.test.ts +305 -0
  62. package/src/__tests__/extension-id-sync-guard.test.ts +3 -3
  63. package/src/__tests__/first-greeting.test.ts +247 -5
  64. package/src/__tests__/headless-browser-mode.test.ts +57 -0
  65. package/src/__tests__/history-repair-pipeline.test.ts +399 -0
  66. package/src/__tests__/host-browser-e2e-cloud.test.ts +307 -0
  67. package/src/__tests__/host-browser-e2e-self-hosted.test.ts +3 -3
  68. package/src/__tests__/host-proxy-interface.test.ts +36 -2
  69. package/src/__tests__/image-credentials.test.ts +137 -0
  70. package/src/__tests__/image-service-dispatcher.test.ts +186 -0
  71. package/src/__tests__/injector-chain.test.ts +526 -0
  72. package/src/__tests__/intent-routing.test.ts +0 -26
  73. package/src/__tests__/llm-call-pipeline.test.ts +285 -0
  74. package/src/__tests__/llm-schema.test.ts +1 -1
  75. package/src/__tests__/media-generate-image.test.ts +119 -13
  76. package/src/__tests__/memory-retrieval-pipeline.test.ts +401 -0
  77. package/src/__tests__/memory-upsert-concurrency.test.ts +1 -0
  78. package/src/__tests__/migration-import-from-url.test.ts +5 -68
  79. package/src/__tests__/model-intents.test.ts +4 -2
  80. package/src/__tests__/notification-broadcaster.test.ts +3 -3
  81. package/src/__tests__/notification-decision-strategy.test.ts +0 -11
  82. package/src/__tests__/notification-schedule-notify-dedup.test.ts +108 -0
  83. package/src/__tests__/oauth-apps-routes.test.ts +1 -1
  84. package/src/__tests__/oauth-cli.test.ts +14 -12
  85. package/src/__tests__/oauth-connect-orchestrator.test.ts +4 -13
  86. package/src/__tests__/oauth-provider-serializer.test.ts +6 -4
  87. package/src/__tests__/oauth-provider-visibility.test.ts +3 -5
  88. package/src/__tests__/oauth-providers-routes.test.ts +3 -2
  89. package/src/__tests__/oauth-store.test.ts +41 -76
  90. package/src/__tests__/onboarding-template-contract.test.ts +16 -64
  91. package/src/__tests__/openai-image-service.test.ts +368 -0
  92. package/src/__tests__/overflow-reduce-pipeline.test.ts +676 -0
  93. package/src/__tests__/permission-checker-host-gate.test.ts +0 -24
  94. package/src/__tests__/persist-onboarding-artifacts.test.ts +266 -0
  95. package/src/__tests__/persistence-pipeline.test.ts +377 -0
  96. package/src/__tests__/pipeline-runner.test.ts +565 -0
  97. package/src/__tests__/platform.test.ts +5 -2
  98. package/src/__tests__/plugin-bootstrap.test.ts +483 -0
  99. package/src/__tests__/plugin-registry.test.ts +273 -0
  100. package/src/__tests__/plugin-route-contribution.test.ts +288 -0
  101. package/src/__tests__/plugin-skill-contribution.test.ts +367 -0
  102. package/src/__tests__/plugin-tool-contribution.test.ts +286 -0
  103. package/src/__tests__/plugin-types.test.ts +320 -0
  104. package/src/__tests__/pricing.test.ts +44 -12
  105. package/src/__tests__/proxy-approval-callback.test.ts +69 -8
  106. package/src/__tests__/reaction-persistence.test.ts +1 -0
  107. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
  108. package/src/__tests__/registry.test.ts +0 -2
  109. package/src/__tests__/schedule-routes.test.ts +131 -1
  110. package/src/__tests__/scheduler-recurrence.test.ts +14 -70
  111. package/src/__tests__/scheduler-reuse-conversation.test.ts +10 -50
  112. package/src/__tests__/secret-detection-handler.test.ts +0 -10
  113. package/src/__tests__/shell-identity.test.ts +0 -134
  114. package/src/__tests__/suggestion-routes.test.ts +103 -4
  115. package/src/__tests__/task-memory-cleanup.test.ts +1 -0
  116. package/src/__tests__/task-scheduler.test.ts +3 -15
  117. package/src/__tests__/test-preload.ts +11 -0
  118. package/src/__tests__/title-generate-pipeline.test.ts +224 -0
  119. package/src/__tests__/token-estimate-pipeline.test.ts +431 -0
  120. package/src/__tests__/tool-error-pipeline.test.ts +244 -0
  121. package/src/__tests__/tool-execute-pipeline.test.ts +431 -0
  122. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -6
  123. package/src/__tests__/tool-executor-shell-integration.test.ts +7 -10
  124. package/src/__tests__/tool-executor.test.ts +141 -0
  125. package/src/__tests__/tool-result-truncate-pipeline.test.ts +356 -0
  126. package/src/__tests__/tool-result-truncation.test.ts +0 -110
  127. package/src/__tests__/user-plugin-loader.test.ts +191 -0
  128. package/src/__tests__/workspace-migration-046-seed-conversation-starters-callsite.test.ts +185 -0
  129. package/src/__tests__/workspace-migration-049-release-notes-default-sonnet.test.ts +100 -0
  130. package/src/__tests__/workspace-migration-050-seed-main-agent-opus-callsite.test.ts +171 -0
  131. package/src/__tests__/workspace-migration-051-seed-conversation-summarization-callsite.test.ts +252 -0
  132. package/src/__tests__/workspace-migration-remove-hooks.test.ts +99 -0
  133. package/src/__tests__/workspace-policy.test.ts +21 -3
  134. package/src/agent/loop.ts +340 -102
  135. package/src/approvals/__tests__/guardian-feed-event.test.ts +304 -0
  136. package/src/approvals/guardian-request-resolvers.ts +80 -0
  137. package/src/backup/__tests__/backup-worker.test.ts +2 -13
  138. package/src/backup/backup-worker.ts +3 -15
  139. package/src/bundler/app-compiler.ts +84 -1
  140. package/src/calls/call-state.ts +2 -2
  141. package/src/channels/__tests__/types.test.ts +3 -3
  142. package/src/channels/types.ts +6 -4
  143. package/src/cli/__tests__/notifications.test.ts +87 -211
  144. package/src/cli/commands/__tests__/backup.test.ts +1 -1
  145. package/src/cli/commands/__tests__/image-generation.test.ts +255 -35
  146. package/src/cli/commands/__tests__/inference-send.test.ts +12 -0
  147. package/src/cli/commands/__tests__/tts-synthesize.test.ts +12 -0
  148. package/src/cli/commands/backup.ts +2 -2
  149. package/src/cli/commands/clients.ts +138 -0
  150. package/src/cli/commands/completions.ts +2 -9
  151. package/src/cli/commands/conversations.ts +55 -7
  152. package/src/cli/commands/image-generation.ts +33 -34
  153. package/src/cli/commands/notifications.ts +68 -103
  154. package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -1
  155. package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -1
  156. package/src/cli/commands/oauth/connect.ts +2 -2
  157. package/src/cli/commands/oauth/providers.ts +176 -8
  158. package/src/cli/commands/oauth/status.ts +46 -36
  159. package/src/cli/commands/skills.ts +3 -4
  160. package/src/cli/program.ts +25 -29
  161. package/src/config/__tests__/backup-schema.test.ts +7 -2
  162. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  163. package/src/config/bundled-skills/app-builder/references/WIDGETS.md +10 -10
  164. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +66 -87
  165. package/src/config/bundled-skills/contacts/tools/contact-search.ts +28 -51
  166. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +22 -40
  167. package/src/config/bundled-skills/image-studio/SKILL.md +2 -1
  168. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -1
  169. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +23 -39
  170. package/src/config/bundled-skills/messaging/SKILL.md +3 -3
  171. package/src/config/bundled-skills/messaging/tools/__tests__/messaging-feed-events.test.ts +207 -0
  172. package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +12 -0
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +58 -0
  174. package/src/config/bundled-skills/schedule/SKILL.md +8 -3
  175. package/src/config/bundled-skills/schedule/TOOLS.json +15 -7
  176. package/src/config/bundled-skills/schedule/references/SCRIPT_MODE_PATTERNS.md +59 -0
  177. package/src/config/bundled-tool-registry.ts +0 -15
  178. package/src/config/feature-flag-registry.json +17 -1
  179. package/src/config/schema.ts +19 -0
  180. package/src/config/schemas/backup.ts +1 -1
  181. package/src/config/schemas/conversations.ts +16 -0
  182. package/src/config/schemas/llm.ts +2 -3
  183. package/src/config/schemas/security.ts +6 -6
  184. package/src/config/schemas/tts.ts +11 -0
  185. package/src/config/skill-state.ts +6 -2
  186. package/src/config/skills.ts +94 -5
  187. package/src/context/__tests__/compact-prompt.test.ts +27 -9
  188. package/src/context/prompts/compact.md +26 -12
  189. package/src/context/tool-result-truncation.ts +3 -63
  190. package/src/context/window-manager.ts +190 -16
  191. package/src/credential-health/credential-health-service.ts +19 -6
  192. package/src/daemon/__tests__/conversation-feed-event.test.ts +317 -0
  193. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +4 -12
  194. package/src/daemon/__tests__/conversation-tool-setup.test.ts +14 -15
  195. package/src/daemon/config-watcher.ts +0 -2
  196. package/src/daemon/context-overflow-policy.ts +4 -13
  197. package/src/daemon/conversation-agent-loop-handlers.ts +83 -22
  198. package/src/daemon/conversation-agent-loop.ts +984 -683
  199. package/src/daemon/conversation-history.ts +10 -19
  200. package/src/daemon/conversation-lifecycle.ts +37 -19
  201. package/src/daemon/conversation-notifiers.ts +2 -110
  202. package/src/daemon/conversation-process.ts +14 -7
  203. package/src/daemon/conversation-runtime-assembly.ts +532 -411
  204. package/src/daemon/conversation-tool-setup.ts +41 -4
  205. package/src/daemon/conversation.ts +80 -35
  206. package/src/daemon/external-plugins-bootstrap.ts +478 -0
  207. package/src/daemon/first-greeting.ts +191 -14
  208. package/src/daemon/handlers/config-model.ts +11 -0
  209. package/src/daemon/handlers/skills.ts +5 -1
  210. package/src/daemon/lifecycle.ts +33 -68
  211. package/src/daemon/message-types/computer-use.ts +2 -34
  212. package/src/daemon/message-types/conversations.ts +49 -0
  213. package/src/daemon/message-types/messages.ts +12 -0
  214. package/src/daemon/server.ts +5 -3
  215. package/src/daemon/shutdown-handlers.ts +2 -12
  216. package/src/daemon/tool-side-effects.ts +14 -56
  217. package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +160 -0
  218. package/src/heartbeat/heartbeat-service.ts +24 -1
  219. package/src/home/__tests__/feed-population-integration.test.ts +312 -0
  220. package/src/home/emit-feed-event.ts +7 -0
  221. package/src/home/feed-types.ts +41 -2
  222. package/src/home/rewrite-command-preview.ts +66 -0
  223. package/src/ipc/__tests__/socket-path.test.ts +11 -50
  224. package/src/ipc/cli-client.ts +1 -1
  225. package/src/ipc/cli-server.ts +3 -3
  226. package/src/ipc/gateway-client.ts +4 -1
  227. package/src/ipc/routes/browser-context.ts +2 -0
  228. package/src/ipc/routes/browser.ts +1 -0
  229. package/src/ipc/routes/get-contact.ts +16 -0
  230. package/src/ipc/routes/index.ts +14 -0
  231. package/src/ipc/routes/list-clients.ts +31 -0
  232. package/src/ipc/routes/merge-contacts.ts +17 -0
  233. package/src/ipc/routes/notification.ts +133 -0
  234. package/src/ipc/routes/rename-conversation.ts +59 -0
  235. package/src/ipc/routes/search-contacts.ts +19 -0
  236. package/src/ipc/routes/upsert-contact.ts +25 -0
  237. package/src/ipc/socket-path.ts +14 -38
  238. package/src/media/app-icon-generator.ts +23 -46
  239. package/src/media/avatar-router.ts +26 -41
  240. package/src/media/gemini-image-service.ts +8 -41
  241. package/src/media/image-credentials.ts +73 -0
  242. package/src/media/image-service.ts +85 -0
  243. package/src/media/openai-image-service.ts +131 -0
  244. package/src/media/types.ts +46 -0
  245. package/src/memory/conversation-crud.ts +48 -18
  246. package/src/memory/conversation-queries.ts +57 -4
  247. package/src/memory/conversation-title-service.ts +25 -0
  248. package/src/memory/db-init.ts +8 -0
  249. package/src/memory/embedding-gemini.test.ts +41 -2
  250. package/src/memory/embedding-gemini.ts +6 -1
  251. package/src/memory/graph/bootstrap.test.ts +282 -0
  252. package/src/memory/graph/bootstrap.ts +8 -5
  253. package/src/memory/graph/extraction.ts +10 -2
  254. package/src/memory/graph/graph-search.test.ts +1 -0
  255. package/src/memory/graph/inspect.ts +2 -2
  256. package/src/memory/graph/retriever.ts +10 -3
  257. package/src/memory/migrations/041-approval-prompt-ts-tracker.ts +26 -0
  258. package/src/memory/migrations/149-oauth-tables.ts +1 -0
  259. package/src/memory/migrations/223-schedule-script-column.ts +11 -0
  260. package/src/memory/migrations/224-oauth-providers-managed-service-is-paid.ts +24 -0
  261. package/src/memory/migrations/225-oauth-providers-available-scopes.ts +13 -0
  262. package/src/memory/migrations/index.ts +4 -0
  263. package/src/memory/pkb/pkb-index.test.ts +1 -0
  264. package/src/memory/pkb/pkb-reconcile.test.ts +1 -0
  265. package/src/memory/pkb/pkb-search.test.ts +65 -4
  266. package/src/memory/pkb/pkb-search.ts +40 -18
  267. package/src/memory/qdrant-client.test.ts +60 -0
  268. package/src/memory/qdrant-client.ts +25 -0
  269. package/src/memory/schema/infrastructure.ts +1 -0
  270. package/src/memory/schema/oauth.ts +4 -1
  271. package/src/messaging/providers/slack/render-transcript.test.ts +77 -29
  272. package/src/messaging/providers/slack/render-transcript.ts +58 -0
  273. package/src/notifications/conversation-pairing.ts +78 -19
  274. package/src/notifications/copy-composer.ts +0 -5
  275. package/src/notifications/emit-signal.ts +1 -1
  276. package/src/notifications/signal.ts +1 -2
  277. package/src/oauth/AGENTS.md +1 -1
  278. package/src/oauth/__tests__/identity-verifier.test.ts +2 -1
  279. package/src/oauth/connect-orchestrator.ts +8 -34
  280. package/src/oauth/connect-types.ts +6 -10
  281. package/src/oauth/manual-token-connection.ts +23 -0
  282. package/src/oauth/oauth-store.ts +30 -14
  283. package/src/oauth/provider-serializer.ts +6 -1
  284. package/src/oauth/seed-providers.ts +56 -108
  285. package/src/outbound-proxy/http-forwarder.ts +9 -0
  286. package/src/permissions/approval-policy.test.ts +293 -18
  287. package/src/permissions/approval-policy.ts +110 -58
  288. package/src/permissions/arg-parser.test.ts +161 -0
  289. package/src/permissions/arg-parser.ts +141 -0
  290. package/src/permissions/bash-risk-classifier.test.ts +414 -2
  291. package/src/permissions/bash-risk-classifier.ts +303 -60
  292. package/src/permissions/checker.ts +157 -29
  293. package/src/permissions/command-registry.test.ts +239 -0
  294. package/src/permissions/command-registry.ts +234 -54
  295. package/src/permissions/defaults.ts +5 -4
  296. package/src/permissions/gateway-threshold-reader.ts +196 -0
  297. package/src/permissions/prompter.ts +4 -0
  298. package/src/permissions/risk-types.ts +61 -4
  299. package/src/permissions/schedule-risk-classifier.test.ts +129 -0
  300. package/src/permissions/schedule-risk-classifier.ts +85 -0
  301. package/src/permissions/shell-identity.ts +2 -42
  302. package/src/permissions/types.ts +2 -0
  303. package/src/permissions/workspace-policy.ts +8 -3
  304. package/src/plugins/defaults/circuit-breaker.ts +146 -0
  305. package/src/plugins/defaults/compaction.ts +145 -0
  306. package/src/plugins/defaults/empty-response.ts +126 -0
  307. package/src/plugins/defaults/history-repair.ts +85 -0
  308. package/src/plugins/defaults/index.ts +116 -0
  309. package/src/plugins/defaults/injectors.ts +491 -0
  310. package/src/plugins/defaults/llm-call.ts +82 -0
  311. package/src/plugins/defaults/memory-retrieval.ts +226 -0
  312. package/src/plugins/defaults/overflow-reduce.ts +181 -0
  313. package/src/plugins/defaults/persistence.ts +129 -0
  314. package/src/plugins/defaults/title-generate.ts +95 -0
  315. package/src/plugins/defaults/token-estimate.ts +104 -0
  316. package/src/plugins/defaults/tool-error.ts +126 -0
  317. package/src/plugins/defaults/tool-execute.ts +89 -0
  318. package/src/plugins/defaults/tool-result-truncate.ts +88 -0
  319. package/src/plugins/pipeline.ts +316 -0
  320. package/src/plugins/plugin-skill-contributions.ts +292 -0
  321. package/src/plugins/registry.ts +241 -0
  322. package/src/plugins/types.ts +1134 -0
  323. package/src/plugins/user-loader.ts +177 -0
  324. package/src/prompts/templates/BOOTSTRAP.md +27 -77
  325. package/src/providers/model-catalog.ts +52 -29
  326. package/src/providers/model-intents.ts +1 -1
  327. package/src/providers/openrouter/client.ts +5 -1
  328. package/src/providers/speech-to-text/deepgram-realtime.test.ts +61 -0
  329. package/src/providers/speech-to-text/deepgram-realtime.ts +57 -0
  330. package/src/providers/speech-to-text/xai-realtime.test.ts +72 -4
  331. package/src/providers/speech-to-text/xai-realtime.ts +39 -14
  332. package/src/runtime/AGENTS.md +25 -16
  333. package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +3 -3
  334. package/src/runtime/__tests__/client-registry.test.ts +293 -0
  335. package/src/runtime/client-registry.ts +261 -0
  336. package/src/runtime/http-server.ts +77 -8
  337. package/src/runtime/http-types.ts +0 -2
  338. package/src/runtime/migrations/vbundle-builder.ts +1 -22
  339. package/src/runtime/routes/approval-prompt-ts-tracker.ts +51 -31
  340. package/src/runtime/routes/approval-routes.ts +17 -0
  341. package/src/runtime/routes/browser-extension-pair-routes.ts +27 -8
  342. package/src/runtime/routes/conversation-routes.ts +223 -116
  343. package/src/runtime/routes/inbound-message-handler.ts +88 -13
  344. package/src/runtime/routes/memory-item-routes.test.ts +1 -0
  345. package/src/runtime/routes/migration-routes.ts +0 -3
  346. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +284 -0
  347. package/src/runtime/routes/playground/__tests__/guard.test.ts +80 -0
  348. package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +294 -0
  349. package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +271 -0
  350. package/src/runtime/routes/playground/__tests__/seed-conversation.test.ts +202 -0
  351. package/src/runtime/routes/playground/__tests__/seeded-conversations.test.ts +309 -0
  352. package/src/runtime/routes/playground/__tests__/state.test.ts +224 -0
  353. package/src/runtime/routes/playground/conversation-not-found.ts +29 -0
  354. package/src/runtime/routes/playground/deps.ts +56 -0
  355. package/src/runtime/routes/playground/force-compact.ts +73 -0
  356. package/src/runtime/routes/playground/guard.ts +37 -0
  357. package/src/runtime/routes/playground/index.ts +28 -0
  358. package/src/runtime/routes/playground/inject-failures.ts +159 -0
  359. package/src/runtime/routes/playground/reset-circuit.ts +115 -0
  360. package/src/runtime/routes/playground/seed-conversation.ts +139 -0
  361. package/src/runtime/routes/playground/seeded-conversations.ts +78 -0
  362. package/src/runtime/routes/playground/state.ts +78 -0
  363. package/src/runtime/routes/schedule-routes.ts +89 -8
  364. package/src/runtime/skill-route-registry.ts +75 -15
  365. package/src/schedule/run-script.ts +68 -0
  366. package/src/schedule/schedule-store.ts +7 -1
  367. package/src/schedule/scheduler.ts +48 -8
  368. package/src/skills/catalog-cache.ts +12 -5
  369. package/src/tools/browser/__tests__/browser-status.test.ts +189 -0
  370. package/src/tools/browser/browser-execution.ts +88 -19
  371. package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +230 -0
  372. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +146 -3
  373. package/src/tools/browser/cdp-client/extension-cdp-client.ts +54 -3
  374. package/src/tools/browser/cdp-client/factory.ts +15 -4
  375. package/src/tools/executor.ts +126 -74
  376. package/src/tools/network/script-proxy/session-manager.ts +37 -1
  377. package/src/tools/permission-checker.ts +98 -49
  378. package/src/tools/policy-context.ts +4 -0
  379. package/src/tools/registry.ts +140 -3
  380. package/src/tools/schedule/create.ts +23 -8
  381. package/src/tools/schedule/update.ts +3 -1
  382. package/src/tools/secret-detection-handler.ts +0 -51
  383. package/src/tools/system/avatar-generator.ts +6 -2
  384. package/src/tools/types.ts +28 -2
  385. package/src/util/platform.ts +7 -2
  386. package/src/util/pricing.ts +26 -3
  387. package/src/workspace/migrations/006-services-config.ts +2 -4
  388. package/src/workspace/migrations/022-move-hooks-to-workspace.ts +2 -3
  389. package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +3 -4
  390. package/src/workspace/migrations/046-seed-conversation-starters-callsite.ts +108 -0
  391. package/src/workspace/migrations/047-remove-watch-callsites.ts +54 -0
  392. package/src/workspace/migrations/048-remove-workspace-hooks.ts +81 -0
  393. package/src/workspace/migrations/049-release-notes-default-sonnet.ts +80 -0
  394. package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +86 -0
  395. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +128 -0
  396. package/src/workspace/migrations/registry.ts +12 -0
  397. package/tsconfig.json +1 -1
  398. package/hook-templates/debug-prompt-logger/hook.json +0 -7
  399. package/hook-templates/debug-prompt-logger/run.sh +0 -66
  400. package/src/__tests__/compaction-circuit-breaker.test.ts +0 -336
  401. package/src/__tests__/context-overflow-approval.test.ts +0 -156
  402. package/src/__tests__/hooks-blocking.test.ts +0 -178
  403. package/src/__tests__/hooks-cli.test.ts +0 -182
  404. package/src/__tests__/hooks-config.test.ts +0 -108
  405. package/src/__tests__/hooks-discovery.test.ts +0 -211
  406. package/src/__tests__/hooks-integration.test.ts +0 -196
  407. package/src/__tests__/hooks-manager.test.ts +0 -226
  408. package/src/__tests__/hooks-runner.test.ts +0 -175
  409. package/src/__tests__/hooks-settings.test.ts +0 -160
  410. package/src/__tests__/hooks-templates.test.ts +0 -169
  411. package/src/__tests__/hooks-ts-runner.test.ts +0 -170
  412. package/src/__tests__/hooks-watch.test.ts +0 -112
  413. package/src/__tests__/notification-schedule-dedup.test.ts +0 -213
  414. package/src/__tests__/oauth-scope-policy.test.ts +0 -180
  415. package/src/__tests__/send-notification-tool.test.ts +0 -83
  416. package/src/cli/commands/shotgun.ts +0 -266
  417. package/src/config/bundled-skills/conversations/SKILL.md +0 -20
  418. package/src/config/bundled-skills/conversations/TOOLS.json +0 -23
  419. package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +0 -88
  420. package/src/config/bundled-skills/heartbeat/SKILL.md +0 -43
  421. package/src/config/bundled-skills/notifications/SKILL.md +0 -40
  422. package/src/config/bundled-skills/notifications/TOOLS.json +0 -80
  423. package/src/config/bundled-skills/notifications/tools/send-notification.ts +0 -152
  424. package/src/config/bundled-skills/notifications/tools/shared.ts +0 -13
  425. package/src/config/bundled-skills/screen-watch/SKILL.md +0 -27
  426. package/src/config/bundled-skills/screen-watch/TOOLS.json +0 -35
  427. package/src/config/bundled-skills/screen-watch/tools/start-screen-watch.ts +0 -12
  428. package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -84
  429. package/src/daemon/context-overflow-approval.ts +0 -52
  430. package/src/daemon/watch-handler.ts +0 -399
  431. package/src/hooks/cli.ts +0 -253
  432. package/src/hooks/config.ts +0 -100
  433. package/src/hooks/discovery.ts +0 -135
  434. package/src/hooks/manager.ts +0 -179
  435. package/src/hooks/runner.ts +0 -117
  436. package/src/hooks/templates.ts +0 -77
  437. package/src/hooks/types.ts +0 -75
  438. package/src/oauth/scope-policy.ts +0 -89
  439. package/src/runtime/gateway-internal-client.ts +0 -94
  440. package/src/runtime/routes/watch-routes.ts +0 -156
  441. package/src/signals/shotgun.ts +0 -203
  442. package/src/tools/watch/screen-watch.ts +0 -144
  443. package/src/tools/watch/watch-state.ts +0 -142
@@ -1,175 +0,0 @@
1
- import { chmodSync, mkdirSync, 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
- // Set VELLUM_WORKSPACE_DIR before importing modules that use platform helpers
7
- const testDir = join(tmpdir(), `hooks-runner-test-${Date.now()}`);
8
- process.env.VELLUM_WORKSPACE_DIR = testDir;
9
-
10
- import { runHookScript } from "../hooks/runner.js";
11
- import type { DiscoveredHook, HookEventData } from "../hooks/types.js";
12
-
13
- function createTestHook(
14
- hooksDir: string,
15
- name: string,
16
- scriptContent: string,
17
- ): DiscoveredHook {
18
- const hookDir = join(hooksDir, name);
19
- mkdirSync(hookDir, { recursive: true });
20
- const scriptPath = join(hookDir, "run.sh");
21
- writeFileSync(scriptPath, scriptContent);
22
- chmodSync(scriptPath, 0o755);
23
-
24
- return {
25
- name,
26
- dir: hookDir,
27
- manifest: {
28
- name,
29
- description: "Test hook",
30
- version: "1.0.0",
31
- events: ["pre-tool-execute"],
32
- script: "run.sh",
33
- },
34
- scriptPath,
35
- enabled: true,
36
- };
37
- }
38
-
39
- describe("Hook Runner", () => {
40
- let hooksDir: string;
41
-
42
- beforeEach(() => {
43
- hooksDir = join(testDir, "hooks");
44
- mkdirSync(hooksDir, { recursive: true });
45
- });
46
-
47
- afterEach(() => {
48
- rmSync(testDir, { recursive: true, force: true });
49
- });
50
-
51
- test("runs a script and captures stdout", async () => {
52
- const hook = createTestHook(
53
- hooksDir,
54
- "echo-hook",
55
- '#!/bin/bash\necho "hello from hook"',
56
- );
57
- const eventData: HookEventData = { event: "pre-tool-execute" };
58
-
59
- const result = await runHookScript(hook, eventData);
60
- expect(result.exitCode).toBe(0);
61
- expect(result.stdout.trim()).toBe("hello from hook");
62
- expect(result.stderr).toBe("");
63
- });
64
-
65
- test("captures stderr", async () => {
66
- const hook = createTestHook(
67
- hooksDir,
68
- "stderr-hook",
69
- '#!/bin/bash\necho "error output" >&2',
70
- );
71
- const eventData: HookEventData = { event: "on-error" };
72
-
73
- const result = await runHookScript(hook, eventData);
74
- expect(result.exitCode).toBe(0);
75
- expect(result.stderr.trim()).toBe("error output");
76
- });
77
-
78
- test("reports non-zero exit code", async () => {
79
- const hook = createTestHook(hooksDir, "fail-hook", "#!/bin/bash\nexit 42");
80
- const eventData: HookEventData = { event: "on-error" };
81
-
82
- const result = await runHookScript(hook, eventData);
83
- expect(result.exitCode).toBe(42);
84
- });
85
-
86
- test("pipes event data to stdin as JSON", async () => {
87
- const hook = createTestHook(hooksDir, "stdin-hook", "#!/bin/bash\ncat");
88
- const eventData: HookEventData = {
89
- event: "pre-tool-execute",
90
- tool: "Bash",
91
- command: "ls",
92
- };
93
-
94
- const result = await runHookScript(hook, eventData);
95
- expect(result.exitCode).toBe(0);
96
- const parsed = JSON.parse(result.stdout);
97
- expect(parsed.event).toBe("pre-tool-execute");
98
- expect(parsed.tool).toBe("Bash");
99
- expect(parsed.command).toBe("ls");
100
- });
101
-
102
- test("sets environment variables", async () => {
103
- const hook = createTestHook(
104
- hooksDir,
105
- "env-hook",
106
- '#!/bin/bash\necho "$VELLUM_HOOK_EVENT|$VELLUM_HOOK_NAME"',
107
- );
108
- const eventData: HookEventData = { event: "post-message" };
109
-
110
- const result = await runHookScript(hook, eventData);
111
- expect(result.exitCode).toBe(0);
112
- expect(result.stdout.trim()).toBe("post-message|env-hook");
113
- });
114
-
115
- test("sets VELLUM_ROOT_DIR environment variable", async () => {
116
- const hook = createTestHook(
117
- hooksDir,
118
- "rootdir-hook",
119
- '#!/bin/bash\necho "$VELLUM_ROOT_DIR"',
120
- );
121
- const eventData: HookEventData = { event: "daemon-start" };
122
-
123
- const result = await runHookScript(hook, eventData);
124
- expect(result.exitCode).toBe(0);
125
- expect(result.stdout.trim()).toContain(".vellum");
126
- });
127
-
128
- test("sets VELLUM_WORKSPACE_DIR environment variable", async () => {
129
- const hook = createTestHook(
130
- hooksDir,
131
- "wsdir-hook",
132
- '#!/bin/bash\necho "$VELLUM_WORKSPACE_DIR"',
133
- );
134
- const eventData: HookEventData = { event: "daemon-start" };
135
-
136
- const result = await runHookScript(hook, eventData);
137
- expect(result.exitCode).toBe(0);
138
- const wsDir = result.stdout.trim();
139
- expect(wsDir.length).toBeGreaterThan(0);
140
- expect(wsDir).toBe(testDir);
141
- });
142
-
143
- test("handles non-existent script gracefully", async () => {
144
- const hook: DiscoveredHook = {
145
- name: "missing-script",
146
- dir: join(hooksDir, "missing-script"),
147
- manifest: {
148
- name: "missing-script",
149
- description: "Missing",
150
- version: "1.0.0",
151
- events: ["on-error"],
152
- script: "nonexistent.sh",
153
- },
154
- scriptPath: join(hooksDir, "missing-script", "nonexistent.sh"),
155
- enabled: true,
156
- };
157
- mkdirSync(hook.dir, { recursive: true });
158
- const eventData: HookEventData = { event: "on-error" };
159
-
160
- const result = await runHookScript(hook, eventData);
161
- expect(result.exitCode).toBeNull();
162
- expect(result.stderr).toBeTruthy();
163
- });
164
-
165
- test("runs script in hook directory as cwd", async () => {
166
- const hook = createTestHook(hooksDir, "cwd-hook", "#!/bin/bash\npwd -P");
167
- const eventData: HookEventData = { event: "pre-tool-execute" };
168
-
169
- const result = await runHookScript(hook, eventData);
170
- expect(result.exitCode).toBe(0);
171
- // Use realpathSync to resolve macOS /var -> /private/var symlinks
172
- const { realpathSync } = await import("node:fs");
173
- expect(result.stdout.trim()).toBe(realpathSync(hook.dir));
174
- });
175
- });
@@ -1,160 +0,0 @@
1
- import {
2
- chmodSync,
3
- mkdirSync,
4
- readFileSync,
5
- rmSync,
6
- writeFileSync,
7
- } from "node:fs";
8
- import { tmpdir } from "node:os";
9
- import { join } from "node:path";
10
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
11
-
12
- // Set VELLUM_WORKSPACE_DIR before importing modules that use platform helpers
13
- const testDir = join(tmpdir(), `hooks-settings-test-${Date.now()}`);
14
- process.env.VELLUM_WORKSPACE_DIR = testDir;
15
-
16
- import { getHookSettings } from "../hooks/config.js";
17
- import { saveHooksConfig } from "../hooks/config.js";
18
- import { runHookScript } from "../hooks/runner.js";
19
- import type { DiscoveredHook, HookManifest } from "../hooks/types.js";
20
-
21
- describe("Hook Settings", () => {
22
- let hooksDir: string;
23
-
24
- beforeEach(() => {
25
- hooksDir = join(testDir, "hooks");
26
- mkdirSync(hooksDir, { recursive: true });
27
- });
28
-
29
- afterEach(() => {
30
- rmSync(testDir, { recursive: true, force: true });
31
- });
32
-
33
- test("getHookSettings returns empty object when no schema and no config", () => {
34
- const manifest: HookManifest = {
35
- name: "test",
36
- description: "Test",
37
- version: "1.0.0",
38
- events: ["on-error"],
39
- script: "run.sh",
40
- };
41
-
42
- const settings = getHookSettings("test", manifest);
43
- expect(settings).toEqual({});
44
- });
45
-
46
- test("getHookSettings returns defaults from manifest schema", () => {
47
- const manifest: HookManifest = {
48
- name: "test",
49
- description: "Test",
50
- version: "1.0.0",
51
- events: ["on-error"],
52
- script: "run.sh",
53
- settingsSchema: {
54
- maxLen: { type: "number", default: 2000, description: "Max length" },
55
- verbose: { type: "boolean", default: false },
56
- },
57
- };
58
-
59
- const settings = getHookSettings("test", manifest);
60
- expect(settings.maxLen).toBe(2000);
61
- expect(settings.verbose).toBe(false);
62
- });
63
-
64
- test("getHookSettings merges user overrides over defaults", () => {
65
- const manifest: HookManifest = {
66
- name: "test",
67
- description: "Test",
68
- version: "1.0.0",
69
- events: ["on-error"],
70
- script: "run.sh",
71
- settingsSchema: {
72
- maxLen: { type: "number", default: 2000 },
73
- verbose: { type: "boolean", default: false },
74
- },
75
- };
76
-
77
- saveHooksConfig({
78
- version: 1,
79
- hooks: {
80
- test: {
81
- enabled: true,
82
- settings: { maxLen: 5000 },
83
- },
84
- },
85
- });
86
-
87
- const settings = getHookSettings("test", manifest);
88
- expect(settings.maxLen).toBe(5000); // Overridden
89
- expect(settings.verbose).toBe(false); // Default preserved
90
- });
91
-
92
- test("getHookSettings allows user settings without schema", () => {
93
- const manifest: HookManifest = {
94
- name: "test",
95
- description: "Test",
96
- version: "1.0.0",
97
- events: ["on-error"],
98
- script: "run.sh",
99
- };
100
-
101
- saveHooksConfig({
102
- version: 1,
103
- hooks: {
104
- test: {
105
- enabled: true,
106
- settings: { customKey: "customValue" },
107
- },
108
- },
109
- });
110
-
111
- const settings = getHookSettings("test", manifest);
112
- expect(settings.customKey).toBe("customValue");
113
- });
114
-
115
- test("VELLUM_HOOK_SETTINGS env var is passed to script", async () => {
116
- const hookDir = join(hooksDir, "settings-hook");
117
- mkdirSync(hookDir, { recursive: true });
118
-
119
- const manifest: HookManifest = {
120
- name: "settings-hook",
121
- description: "Settings test",
122
- version: "1.0.0",
123
- events: ["on-error"],
124
- script: "run.sh",
125
- settingsSchema: {
126
- greeting: { type: "string", default: "hello" },
127
- },
128
- };
129
- writeFileSync(join(hookDir, "hook.json"), JSON.stringify(manifest));
130
-
131
- const outputFile = join(testDir, "settings-output.txt");
132
- const scriptContent = `#!/bin/bash\necho "$VELLUM_HOOK_SETTINGS" > "${outputFile}"`;
133
- writeFileSync(join(hookDir, "run.sh"), scriptContent);
134
- chmodSync(join(hookDir, "run.sh"), 0o755);
135
-
136
- saveHooksConfig({
137
- version: 1,
138
- hooks: {
139
- "settings-hook": {
140
- enabled: true,
141
- settings: { greeting: "world" },
142
- },
143
- },
144
- });
145
-
146
- const hook: DiscoveredHook = {
147
- name: "settings-hook",
148
- dir: hookDir,
149
- manifest,
150
- scriptPath: join(hookDir, "run.sh"),
151
- enabled: true,
152
- };
153
-
154
- await runHookScript(hook, { event: "on-error" });
155
-
156
- await new Promise((r) => setTimeout(r, 200));
157
- const output = JSON.parse(readFileSync(outputFile, "utf-8").trim());
158
- expect(output.greeting).toBe("world"); // User override
159
- });
160
- });
@@ -1,169 +0,0 @@
1
- import {
2
- chmodSync,
3
- cpSync,
4
- existsSync,
5
- mkdirSync,
6
- readdirSync,
7
- readFileSync,
8
- rmSync,
9
- statSync,
10
- writeFileSync,
11
- } from "node:fs";
12
- import { tmpdir } from "node:os";
13
- import { join } from "node:path";
14
- import { afterEach, beforeEach, describe, expect, test } from "bun:test";
15
-
16
- // Set VELLUM_WORKSPACE_DIR before importing modules that use platform helpers
17
- const testDir = join(tmpdir(), `hooks-templates-test-${Date.now()}`);
18
- process.env.VELLUM_WORKSPACE_DIR = testDir;
19
-
20
- import {
21
- ensureHookInConfig,
22
- loadHooksConfig,
23
- setHookEnabled,
24
- } from "../hooks/config.js";
25
- import { installTemplates } from "../hooks/templates.js";
26
-
27
- /**
28
- * Create a fake hook-templates directory structure that mimics
29
- * the bundled templates at `assistant/hook-templates/`.
30
- */
31
- function createTemplateDir(
32
- templatesDir: string,
33
- name: string,
34
- manifest: Record<string, unknown>,
35
- scriptContent = "#!/bin/bash\nexit 0",
36
- ): void {
37
- const dir = join(templatesDir, name);
38
- mkdirSync(dir, { recursive: true });
39
- writeFileSync(join(dir, "hook.json"), JSON.stringify(manifest));
40
- const scriptName = (manifest.script as string) ?? "run.sh";
41
- writeFileSync(join(dir, scriptName), scriptContent);
42
- }
43
-
44
- describe("Hook Templates", () => {
45
- let hooksDir: string;
46
-
47
- beforeEach(() => {
48
- hooksDir = join(testDir, "hooks");
49
- mkdirSync(hooksDir, { recursive: true });
50
- });
51
-
52
- afterEach(() => {
53
- rmSync(testDir, { recursive: true, force: true });
54
- });
55
-
56
- test("installs template to hooks directory", () => {
57
- // installTemplates looks relative to its own file location.
58
- // We'll test by creating a template in the actual hook-templates dir
59
- // and verifying the installer's behavior through the public API.
60
- // For unit testing, we test the core logic directly.
61
-
62
- // Create a mock templates directory
63
- const templatesDir = join(testDir, "hook-templates");
64
- createTemplateDir(templatesDir, "test-hook", {
65
- name: "test-hook",
66
- description: "A test template",
67
- version: "1.0.0",
68
- events: ["on-error"],
69
- script: "run.sh",
70
- });
71
-
72
- // Manually simulate what installTemplates does
73
- const entries = readdirSync(templatesDir, { withFileTypes: true });
74
- for (const entry of entries) {
75
- if (!entry.isDirectory()) continue;
76
- const targetDir = join(hooksDir, entry.name);
77
- if (existsSync(targetDir)) continue;
78
- cpSync(join(templatesDir, entry.name), targetDir, { recursive: true });
79
- const manifestPath = join(targetDir, "hook.json");
80
- const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
81
- if (manifest.script) {
82
- chmodSync(join(targetDir, manifest.script), 0o755);
83
- }
84
- }
85
-
86
- expect(existsSync(join(hooksDir, "test-hook", "hook.json"))).toBe(true);
87
- expect(existsSync(join(hooksDir, "test-hook", "run.sh"))).toBe(true);
88
-
89
- // Check script is executable
90
- const stat = statSync(join(hooksDir, "test-hook", "run.sh"));
91
- expect((stat.mode & 0o111) !== 0).toBe(true);
92
- });
93
-
94
- test("does not overwrite existing hooks", () => {
95
- // Pre-create a hook with custom content
96
- const existingHookDir = join(hooksDir, "existing-hook");
97
- mkdirSync(existingHookDir, { recursive: true });
98
- writeFileSync(
99
- join(existingHookDir, "hook.json"),
100
- JSON.stringify({
101
- name: "existing-hook",
102
- description: "User-modified hook",
103
- version: "2.0.0",
104
- events: ["on-error"],
105
- script: "run.sh",
106
- }),
107
- );
108
- writeFileSync(
109
- join(existingHookDir, "run.sh"),
110
- '#!/bin/bash\necho "custom"',
111
- );
112
-
113
- // Create a template with the same name but different content
114
- const templatesDir = join(testDir, "hook-templates");
115
- createTemplateDir(
116
- templatesDir,
117
- "existing-hook",
118
- {
119
- name: "existing-hook",
120
- description: "Template version",
121
- version: "1.0.0",
122
- events: ["on-error"],
123
- script: "run.sh",
124
- },
125
- '#!/bin/bash\necho "template"',
126
- );
127
-
128
- // Simulate install — should skip
129
- const entries = readdirSync(templatesDir, { withFileTypes: true });
130
- for (const entry of entries) {
131
- if (!entry.isDirectory()) continue;
132
- const targetDir = join(hooksDir, entry.name);
133
- if (existsSync(targetDir)) continue; // Should skip
134
- cpSync(join(templatesDir, entry.name), targetDir, { recursive: true });
135
- }
136
-
137
- // Verify original content preserved
138
- const manifest = JSON.parse(
139
- readFileSync(join(existingHookDir, "hook.json"), "utf-8"),
140
- );
141
- expect(manifest.version).toBe("2.0.0");
142
- expect(manifest.description).toBe("User-modified hook");
143
- });
144
-
145
- test("installTemplates runs without error when no templates dir exists", () => {
146
- // installTemplates should gracefully handle missing templates dir
147
- // This tests the guard: if (!existsSync(templatesDir)) return;
148
- expect(() => installTemplates()).not.toThrow();
149
- });
150
-
151
- test("config entry is added for installed template", () => {
152
- // Manually install a template and call ensureHookInConfig
153
- ensureHookInConfig("new-template", { enabled: false });
154
-
155
- const config = loadHooksConfig();
156
- expect(config.hooks["new-template"]).toEqual({ enabled: false });
157
- });
158
-
159
- test("config entry preserves existing enabled state", () => {
160
- // Set hook as enabled first
161
- setHookEnabled("my-hook", true);
162
-
163
- // ensureHookInConfig should not overwrite
164
- ensureHookInConfig("my-hook", { enabled: false });
165
-
166
- const config = loadHooksConfig();
167
- expect(config.hooks["my-hook"].enabled).toBe(true);
168
- });
169
- });
@@ -1,170 +0,0 @@
1
- import { mkdirSync, 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 { runHookScript } from "../hooks/runner.js";
7
- import type {
8
- DiscoveredHook,
9
- HookEventData,
10
- HookManifest,
11
- } from "../hooks/types.js";
12
-
13
- let hooksDir: string;
14
-
15
- function createHook(
16
- name: string,
17
- scriptName: string,
18
- scriptContent: string,
19
- ): DiscoveredHook {
20
- const hookDir = join(hooksDir, name);
21
- mkdirSync(hookDir, { recursive: true });
22
-
23
- const manifest: HookManifest = {
24
- name,
25
- description: `Test hook: ${name}`,
26
- version: "1.0.0",
27
- events: ["pre-llm-call"],
28
- script: scriptName,
29
- };
30
-
31
- writeFileSync(join(hookDir, "hook.json"), JSON.stringify(manifest));
32
-
33
- const scriptPath = join(hookDir, scriptName);
34
- writeFileSync(scriptPath, scriptContent, { mode: 0o755 });
35
-
36
- return {
37
- name,
38
- dir: hookDir,
39
- manifest,
40
- scriptPath,
41
- enabled: true,
42
- };
43
- }
44
-
45
- describe("TypeScript hooks runner", () => {
46
- beforeEach(() => {
47
- hooksDir = join(
48
- tmpdir(),
49
- `hooks-ts-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
50
- );
51
- mkdirSync(hooksDir, { recursive: true });
52
- });
53
-
54
- afterEach(() => {
55
- rmSync(hooksDir, { recursive: true, force: true });
56
- });
57
-
58
- test("[experimental] runs .ts hook via bun run", async () => {
59
- const hook = createHook(
60
- "ts-hook",
61
- "run.ts",
62
- `
63
- const chunks: Buffer[] = [];
64
- for await (const chunk of Bun.stdin.stream()) {
65
- chunks.push(Buffer.from(chunk));
66
- }
67
- const data = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
68
- console.log(JSON.stringify({ event: data.event, ok: true }));
69
- `,
70
- );
71
-
72
- const eventData: HookEventData = { event: "pre-llm-call", model: "test" };
73
- const result = await runHookScript(hook, eventData);
74
-
75
- expect(result.exitCode).toBe(0);
76
- const output = JSON.parse(result.stdout.trim());
77
- expect(output.event).toBe("pre-llm-call");
78
- expect(output.ok).toBe(true);
79
- });
80
-
81
- test("[experimental] .ts hook receives event data on stdin", async () => {
82
- const hook = createHook(
83
- "stdin-hook",
84
- "handler.ts",
85
- `
86
- const chunks: Buffer[] = [];
87
- for await (const chunk of Bun.stdin.stream()) {
88
- chunks.push(Buffer.from(chunk));
89
- }
90
- const data = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
91
- console.log(data.customField);
92
- `,
93
- );
94
-
95
- const eventData: HookEventData = {
96
- event: "pre-llm-call",
97
- customField: "hello-ts",
98
- };
99
- const result = await runHookScript(hook, eventData);
100
-
101
- expect(result.exitCode).toBe(0);
102
- expect(result.stdout.trim()).toBe("hello-ts");
103
- });
104
-
105
- test(".ts hook receives env vars", async () => {
106
- const hook = createHook(
107
- "env-hook",
108
- "check-env.ts",
109
- `
110
- console.log(process.env.VELLUM_HOOK_EVENT);
111
- console.log(process.env.VELLUM_HOOK_NAME);
112
- `,
113
- );
114
-
115
- const eventData: HookEventData = { event: "pre-llm-call" };
116
- const result = await runHookScript(hook, eventData);
117
-
118
- expect(result.exitCode).toBe(0);
119
- const lines = result.stdout.trim().split("\n");
120
- expect(lines[0]).toBe("pre-llm-call");
121
- expect(lines[1]).toBe("env-hook");
122
- });
123
-
124
- test(".ts hook non-zero exit returns exit code", async () => {
125
- const hook = createHook(
126
- "fail-hook",
127
- "fail.ts",
128
- `
129
- process.exit(1);
130
- `,
131
- );
132
-
133
- const eventData: HookEventData = { event: "pre-llm-call" };
134
- const result = await runHookScript(hook, eventData);
135
-
136
- expect(result.exitCode).toBe(1);
137
- });
138
-
139
- test(".sh hook still runs directly (not via bun)", async () => {
140
- const hook = createHook(
141
- "sh-hook",
142
- "run.sh",
143
- `#!/bin/sh
144
- echo "shell-hook"
145
- `,
146
- );
147
-
148
- const eventData: HookEventData = { event: "pre-llm-call" };
149
- const result = await runHookScript(hook, eventData);
150
-
151
- expect(result.exitCode).toBe(0);
152
- expect(result.stdout.trim()).toBe("shell-hook");
153
- });
154
-
155
- test(".ts hook can write to stderr", async () => {
156
- const hook = createHook(
157
- "stderr-hook",
158
- "log.ts",
159
- `
160
- console.error('ts-stderr-output');
161
- `,
162
- );
163
-
164
- const eventData: HookEventData = { event: "pre-llm-call" };
165
- const result = await runHookScript(hook, eventData);
166
-
167
- expect(result.exitCode).toBe(0);
168
- expect(result.stderr).toContain("ts-stderr-output");
169
- });
170
- });