vellum 0.2.1 → 0.2.7

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 (361) hide show
  1. package/README.md +15 -2
  2. package/bun.lock +71 -100
  3. package/package.json +5 -3
  4. package/scripts/capture-x-graphql.ts +562 -0
  5. package/scripts/ipc/check-swift-decoder-drift.ts +2 -1
  6. package/scripts/test.sh +5 -0
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +133 -34
  8. package/src/__tests__/account-registry.test.ts +2 -1
  9. package/src/__tests__/agent-heartbeat-service.test.ts +250 -0
  10. package/src/__tests__/asset-materialize-tool.test.ts +16 -15
  11. package/src/__tests__/asset-search-tool.test.ts +23 -22
  12. package/src/__tests__/attachments-store.test.ts +56 -127
  13. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
  14. package/src/__tests__/browser-skill-endstate.test.ts +4 -3
  15. package/src/__tests__/call-bridge.test.ts +385 -0
  16. package/src/__tests__/call-constants.test.ts +40 -0
  17. package/src/__tests__/call-orchestrator.test.ts +130 -4
  18. package/src/__tests__/call-recovery.test.ts +518 -0
  19. package/src/__tests__/call-routes-http.test.ts +459 -0
  20. package/src/__tests__/call-state-machine.test.ts +143 -0
  21. package/src/__tests__/call-store.test.ts +216 -1
  22. package/src/__tests__/cli-discover.test.ts +1 -1
  23. package/src/__tests__/commit-message-enrichment-service.test.ts +148 -7
  24. package/src/__tests__/compaction.benchmark.test.ts +176 -0
  25. package/src/__tests__/computer-use-tools.test.ts +250 -0
  26. package/src/__tests__/config-schema.test.ts +305 -3
  27. package/src/__tests__/conflict-store.test.ts +2 -1
  28. package/src/__tests__/contacts-tools.test.ts +331 -0
  29. package/src/__tests__/conversation-store.test.ts +30 -32
  30. package/src/__tests__/credential-security-invariants.test.ts +4 -0
  31. package/src/__tests__/date-context.test.ts +373 -0
  32. package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
  33. package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
  34. package/src/__tests__/followup-tools.test.ts +303 -0
  35. package/src/__tests__/handlers-twilio-config.test.ts +221 -0
  36. package/src/__tests__/handlers-twitter-config.test.ts +718 -0
  37. package/src/__tests__/intent-routing.test.ts +64 -57
  38. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
  39. package/src/__tests__/ipc-snapshot.test.ts +71 -28
  40. package/src/__tests__/llm-usage-store.test.ts +3 -8
  41. package/src/__tests__/media-generate-image.test.ts +1 -1
  42. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
  43. package/src/__tests__/memory-regressions.test.ts +100 -2
  44. package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
  45. package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
  46. package/src/__tests__/playbook-tools.test.ts +342 -0
  47. package/src/__tests__/profile-compiler.test.ts +2 -1
  48. package/src/__tests__/provider-commit-message-generator.test.ts +303 -0
  49. package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
  50. package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
  51. package/src/__tests__/recurrence-engine.test.ts +69 -0
  52. package/src/__tests__/recurrence-types.test.ts +71 -0
  53. package/src/__tests__/registry.test.ts +5 -3
  54. package/src/__tests__/relay-server.test.ts +633 -0
  55. package/src/__tests__/reminder-store.test.ts +6 -3
  56. package/src/__tests__/reminder.test.ts +43 -77
  57. package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
  58. package/src/__tests__/run-orchestrator.test.ts +4 -4
  59. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
  60. package/src/__tests__/runtime-runs-http.test.ts +4 -4
  61. package/src/__tests__/runtime-runs.test.ts +4 -4
  62. package/src/__tests__/schedule-store.test.ts +482 -0
  63. package/src/__tests__/schedule-tools.test.ts +700 -0
  64. package/src/__tests__/scheduler-recurrence.test.ts +329 -0
  65. package/src/__tests__/server-history-render.test.ts +14 -13
  66. package/src/__tests__/session-conflict-gate.test.ts +28 -25
  67. package/src/__tests__/session-error.test.ts +28 -0
  68. package/src/__tests__/session-init.benchmark.test.ts +462 -0
  69. package/src/__tests__/session-queue.test.ts +71 -48
  70. package/src/__tests__/session-runtime-assembly.test.ts +161 -0
  71. package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
  72. package/src/__tests__/signup-e2e.test.ts +2 -1
  73. package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
  74. package/src/__tests__/skill-script-runner.test.ts +159 -0
  75. package/src/__tests__/speaker-identification.test.ts +52 -0
  76. package/src/__tests__/subagent-manager-notify.test.ts +42 -10
  77. package/src/__tests__/subagent-tools.test.ts +141 -41
  78. package/src/__tests__/task-compiler.test.ts +2 -1
  79. package/src/__tests__/task-runner.test.ts +2 -1
  80. package/src/__tests__/task-scheduler.test.ts +2 -1
  81. package/src/__tests__/task-tools.test.ts +49 -56
  82. package/src/__tests__/tool-audit-listener.test.ts +1 -0
  83. package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
  84. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
  85. package/src/__tests__/tool-executor.test.ts +13 -17
  86. package/src/__tests__/turn-commit.test.ts +218 -3
  87. package/src/__tests__/twilio-provider.test.ts +143 -0
  88. package/src/__tests__/twilio-routes.test.ts +789 -0
  89. package/src/__tests__/twitter-auth-handler.test.ts +581 -0
  90. package/src/__tests__/view-image-tool.test.ts +217 -0
  91. package/src/__tests__/workspace-git-service.test.ts +186 -0
  92. package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
  93. package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
  94. package/src/bundler/app-bundler.ts +12 -8
  95. package/src/calls/__tests__/twilio-webhook-urls.test.ts +162 -0
  96. package/src/calls/call-bridge.ts +95 -0
  97. package/src/calls/call-constants.ts +43 -5
  98. package/src/calls/call-domain.ts +276 -0
  99. package/src/calls/call-orchestrator.ts +43 -17
  100. package/src/calls/call-recovery.ts +207 -0
  101. package/src/calls/call-state-machine.ts +68 -0
  102. package/src/calls/call-store.ts +192 -5
  103. package/src/calls/relay-server.ts +41 -4
  104. package/src/calls/speaker-identification.ts +213 -0
  105. package/src/calls/twilio-config.ts +8 -8
  106. package/src/calls/twilio-provider.ts +13 -9
  107. package/src/calls/twilio-routes.ts +90 -76
  108. package/src/calls/twilio-webhook-urls.ts +50 -0
  109. package/src/calls/types.ts +1 -1
  110. package/src/cli/config-commands.ts +334 -0
  111. package/src/cli/core-commands.ts +776 -0
  112. package/src/cli/doordash.ts +251 -1
  113. package/src/cli/ipc-client.ts +82 -0
  114. package/src/cli/map.ts +270 -0
  115. package/src/cli/twitter.ts +575 -0
  116. package/src/cli.ts +7 -5
  117. package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
  118. package/src/commands/cc-command-registry.ts +209 -0
  119. package/src/config/bundled-skills/contacts/SKILL.md +39 -0
  120. package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
  121. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
  122. package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
  123. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
  124. package/src/config/bundled-skills/document/SKILL.md +18 -0
  125. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  126. package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
  127. package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
  128. package/src/config/bundled-skills/doordash/SKILL.md +82 -23
  129. package/src/config/bundled-skills/followups/SKILL.md +32 -0
  130. package/src/config/bundled-skills/followups/TOOLS.json +100 -0
  131. package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
  132. package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
  133. package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
  134. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
  135. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
  136. package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
  137. package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
  138. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
  139. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
  140. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
  141. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
  142. package/src/config/bundled-skills/reminder/SKILL.md +20 -0
  143. package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
  144. package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
  145. package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
  146. package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
  147. package/src/config/bundled-skills/schedule/SKILL.md +74 -0
  148. package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
  149. package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
  150. package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
  151. package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
  152. package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
  153. package/src/config/bundled-skills/subagent/SKILL.md +25 -0
  154. package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
  155. package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
  156. package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
  157. package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
  158. package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
  159. package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
  160. package/src/config/bundled-skills/tasks/SKILL.md +28 -0
  161. package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
  162. package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
  163. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
  164. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
  165. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
  166. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
  167. package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
  168. package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
  169. package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
  170. package/src/config/bundled-skills/twitter/SKILL.md +134 -0
  171. package/src/config/bundled-skills/watcher/SKILL.md +27 -0
  172. package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
  173. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
  174. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
  175. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
  176. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
  177. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
  178. package/src/config/defaults.ts +34 -0
  179. package/src/config/loader.ts +4 -1
  180. package/src/config/schema.ts +165 -1
  181. package/src/config/system-prompt.ts +61 -16
  182. package/src/config/templates/IDENTITY.md +7 -0
  183. package/src/config/types.ts +4 -0
  184. package/src/config/vellum-skills/telegram-setup/SKILL.md +1 -5
  185. package/src/contacts/contact-store.ts +4 -4
  186. package/src/daemon/assistant-attachments.ts +10 -0
  187. package/src/daemon/classifier.ts +3 -1
  188. package/src/daemon/computer-use-session.ts +3 -1
  189. package/src/daemon/date-context.ts +136 -0
  190. package/src/daemon/handlers/apps.ts +16 -1
  191. package/src/daemon/handlers/browser.ts +54 -0
  192. package/src/daemon/handlers/computer-use.ts +7 -1
  193. package/src/daemon/handlers/config.ts +205 -5
  194. package/src/daemon/handlers/diagnostics.ts +5 -1
  195. package/src/daemon/handlers/documents.ts +18 -29
  196. package/src/daemon/handlers/home-base.ts +5 -1
  197. package/src/daemon/handlers/index.ts +40 -277
  198. package/src/daemon/handlers/misc.ts +9 -1
  199. package/src/daemon/handlers/publish.ts +6 -1
  200. package/src/daemon/handlers/sessions.ts +65 -12
  201. package/src/daemon/handlers/shared.ts +36 -1
  202. package/src/daemon/handlers/signing.ts +37 -0
  203. package/src/daemon/handlers/skills.ts +20 -6
  204. package/src/daemon/handlers/subagents.ts +8 -3
  205. package/src/daemon/handlers/twitter-auth.ts +169 -0
  206. package/src/daemon/handlers/work-items.ts +384 -68
  207. package/src/daemon/ipc-contract-inventory.json +32 -4
  208. package/src/daemon/ipc-contract.ts +156 -37
  209. package/src/daemon/ipc-protocol.ts +7 -2
  210. package/src/daemon/lifecycle.ts +21 -0
  211. package/src/daemon/main.ts +10 -4
  212. package/src/daemon/ride-shotgun-handler.ts +75 -10
  213. package/src/daemon/server.ts +143 -26
  214. package/src/daemon/session-agent-loop.ts +922 -0
  215. package/src/daemon/session-attachments.ts +28 -5
  216. package/src/daemon/session-conflict-gate.ts +18 -109
  217. package/src/daemon/session-error.ts +24 -3
  218. package/src/daemon/session-lifecycle.ts +147 -0
  219. package/src/daemon/session-media-retry.ts +147 -0
  220. package/src/daemon/session-messaging.ts +145 -0
  221. package/src/daemon/session-notifiers.ts +164 -0
  222. package/src/daemon/session-process.ts +2 -2
  223. package/src/daemon/session-queue-manager.ts +1 -0
  224. package/src/daemon/session-runtime-assembly.ts +52 -0
  225. package/src/daemon/session-skill-tools.ts +124 -5
  226. package/src/daemon/session-slash.ts +3 -0
  227. package/src/daemon/session-surfaces.ts +77 -2
  228. package/src/daemon/session-tool-setup.ts +216 -2
  229. package/src/daemon/session-usage.ts +0 -2
  230. package/src/daemon/session.ts +114 -1404
  231. package/src/daemon/video-thumbnail.ts +60 -0
  232. package/src/doordash/client.ts +121 -27
  233. package/src/doordash/queries.ts +1 -2
  234. package/src/export/formatter.ts +3 -1
  235. package/src/followups/followup-store.ts +4 -2
  236. package/src/followups/types.ts +6 -0
  237. package/src/hooks/templates.ts +1 -1
  238. package/src/index.ts +32 -1153
  239. package/src/memory/attachments-store.ts +28 -83
  240. package/src/memory/channel-delivery-store.ts +7 -21
  241. package/src/memory/clarification-resolver.ts +6 -5
  242. package/src/memory/conflict-intent.ts +114 -0
  243. package/src/memory/contradiction-checker.ts +3 -2
  244. package/src/memory/conversation-key-store.ts +10 -29
  245. package/src/memory/conversation-store.ts +2 -1
  246. package/src/memory/db.ts +96 -2
  247. package/src/memory/entity-extractor.ts +6 -3
  248. package/src/memory/items-extractor.ts +5 -4
  249. package/src/memory/job-handlers/conflict.ts +23 -1
  250. package/src/memory/jobs-store.ts +3 -2
  251. package/src/memory/llm-usage-store.ts +1 -2
  252. package/src/memory/runs-store.ts +1 -2
  253. package/src/memory/schema.ts +23 -2
  254. package/src/messaging/style-analyzer.ts +3 -2
  255. package/src/messaging/thread-summarizer.ts +8 -12
  256. package/src/messaging/triage-engine.ts +4 -2
  257. package/src/providers/openrouter/client.ts +20 -0
  258. package/src/providers/registry.ts +8 -0
  259. package/src/runtime/gateway-client.ts +36 -0
  260. package/src/runtime/http-server.ts +166 -22
  261. package/src/runtime/routes/attachment-routes.ts +2 -3
  262. package/src/runtime/routes/call-routes.ts +140 -0
  263. package/src/runtime/routes/channel-routes.ts +125 -88
  264. package/src/runtime/routes/conversation-routes.ts +5 -5
  265. package/src/runtime/routes/run-routes.ts +2 -2
  266. package/src/runtime/run-orchestrator.ts +9 -3
  267. package/src/schedule/recurrence-engine.ts +138 -0
  268. package/src/schedule/recurrence-types.ts +67 -0
  269. package/src/schedule/schedule-store.ts +102 -57
  270. package/src/schedule/scheduler.ts +9 -6
  271. package/src/security/oauth2.ts +29 -4
  272. package/src/security/secret-allowlist.ts +46 -0
  273. package/src/skills/clawhub.ts +1 -1
  274. package/src/subagent/manager.ts +40 -8
  275. package/src/swarm/backend-claude-code.ts +64 -9
  276. package/src/swarm/worker-prompts.ts +2 -1
  277. package/src/tasks/SPEC.md +34 -28
  278. package/src/tasks/ephemeral-permissions.ts +16 -7
  279. package/src/tasks/task-compiler.ts +5 -4
  280. package/src/tasks/task-runner.ts +10 -5
  281. package/src/tasks/task-scheduler.ts +1 -1
  282. package/src/tasks/tool-sanitizer.ts +36 -0
  283. package/src/tools/assets/search.ts +4 -4
  284. package/src/tools/browser/api-map.ts +293 -0
  285. package/src/tools/browser/auto-navigate.ts +270 -0
  286. package/src/tools/browser/browser-execution.ts +2 -1
  287. package/src/tools/browser/browser-manager.ts +2 -2
  288. package/src/tools/browser/network-recorder.ts +5 -4
  289. package/src/tools/browser/x-auto-navigate.ts +207 -0
  290. package/src/tools/calls/call-end.ts +17 -67
  291. package/src/tools/calls/call-start.ts +24 -85
  292. package/src/tools/calls/call-status.ts +35 -51
  293. package/src/tools/claude-code/claude-code.ts +207 -11
  294. package/src/tools/contacts/contact-merge.ts +46 -78
  295. package/src/tools/contacts/contact-search.ts +35 -79
  296. package/src/tools/contacts/contact-upsert.ts +35 -108
  297. package/src/tools/credentials/vault.ts +20 -4
  298. package/src/tools/document/document-tool.ts +71 -144
  299. package/src/tools/executor.ts +129 -10
  300. package/src/tools/followups/followup_create.ts +46 -88
  301. package/src/tools/followups/followup_list.ts +34 -74
  302. package/src/tools/followups/followup_resolve.ts +31 -66
  303. package/src/tools/host-terminal/cli-discover.ts +2 -1
  304. package/src/tools/host-terminal/host-shell.ts +10 -0
  305. package/src/tools/memory/handlers.ts +5 -4
  306. package/src/tools/network/__tests__/web-search.test.ts +427 -0
  307. package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
  308. package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
  309. package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
  310. package/src/tools/network/web-fetch.ts +18 -6
  311. package/src/tools/playbooks/index.ts +4 -5
  312. package/src/tools/playbooks/playbook-create.ts +3 -47
  313. package/src/tools/playbooks/playbook-delete.ts +1 -25
  314. package/src/tools/playbooks/playbook-list.ts +1 -28
  315. package/src/tools/playbooks/playbook-update.ts +3 -51
  316. package/src/tools/reminder/reminder.ts +5 -78
  317. package/src/tools/schedule/create.ts +69 -74
  318. package/src/tools/schedule/delete.ts +21 -47
  319. package/src/tools/schedule/list.ts +55 -74
  320. package/src/tools/schedule/update.ts +77 -84
  321. package/src/tools/subagent/abort.ts +29 -58
  322. package/src/tools/subagent/message.ts +30 -63
  323. package/src/tools/subagent/read.ts +53 -84
  324. package/src/tools/subagent/spawn.ts +43 -82
  325. package/src/tools/subagent/status.ts +42 -71
  326. package/src/tools/swarm/delegate.ts +2 -1
  327. package/src/tools/tasks/index.ts +8 -8
  328. package/src/tools/tasks/task-delete.ts +60 -88
  329. package/src/tools/tasks/task-list.ts +31 -52
  330. package/src/tools/tasks/task-run.ts +72 -108
  331. package/src/tools/tasks/task-save.ts +33 -65
  332. package/src/tools/tasks/work-item-enqueue.ts +183 -215
  333. package/src/tools/tasks/work-item-list.ts +33 -63
  334. package/src/tools/tasks/work-item-remove.ts +45 -97
  335. package/src/tools/tasks/work-item-update.ts +91 -163
  336. package/src/tools/terminal/backends/native.ts +3 -1
  337. package/src/tools/tool-manifest.ts +0 -62
  338. package/src/tools/types.ts +6 -0
  339. package/src/tools/ui-surface/definitions.ts +3 -1
  340. package/src/tools/watch/screen-watch.ts +3 -1
  341. package/src/tools/watcher/create.ts +52 -98
  342. package/src/tools/watcher/delete.ts +20 -46
  343. package/src/tools/watcher/digest.ts +36 -70
  344. package/src/tools/watcher/list.ts +49 -79
  345. package/src/tools/watcher/update.ts +45 -91
  346. package/src/twitter/client.ts +690 -0
  347. package/src/twitter/session.ts +91 -0
  348. package/src/usage/types.ts +0 -1
  349. package/src/util/truncate.ts +6 -0
  350. package/src/watcher/providers/slack.ts +2 -1
  351. package/src/watcher/watcher-store.ts +3 -2
  352. package/src/work-items/work-item-store.ts +27 -2
  353. package/src/workspace/commit-message-enrichment-service.ts +31 -7
  354. package/src/workspace/git-service.ts +87 -22
  355. package/src/workspace/provider-commit-message-generator.ts +269 -0
  356. package/src/workspace/turn-commit.ts +62 -3
  357. package/src/tools/contacts/index.ts +0 -4
  358. package/src/tools/document/index.ts +0 -5
  359. package/src/tools/followups/index.ts +0 -3
  360. package/src/tools/subagent/index.ts +0 -5
  361. /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vellum",
3
- "version": "0.2.1",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "vellum": "./src/index.ts"
@@ -18,6 +18,7 @@
18
18
  "typecheck": "bunx tsc --noEmit",
19
19
  "test": "bash scripts/test.sh",
20
20
  "test:stable": "EXCLUDE_EXPERIMENTAL=true bash scripts/test.sh",
21
+ "test:bench": "find src/__tests__ -maxdepth 1 -type f -name '*.benchmark.test.ts' -print0 | xargs -0 -P 1 -I {} bun test {}",
21
22
  "test:filesystem-tools": "bash scripts/test-filesystem-tools.sh"
22
23
  },
23
24
  "dependencies": {
@@ -27,8 +28,8 @@
27
28
  "@huggingface/transformers": "^3.8.1",
28
29
  "@qdrant/js-client-rest": "^1.16.2",
29
30
  "@sentry/node": "^10.38.0",
30
- "@vellumai/cli": "0.1.1",
31
- "@vellumai/vellum-gateway": "^0.1.7",
31
+ "@vellumai/cli": "0.1.8",
32
+ "@vellumai/vellum-gateway": "0.1.9",
32
33
  "agentmail": "^0.1.0",
33
34
  "archiver": "^7.0.1",
34
35
  "commander": "^13.1.0",
@@ -44,6 +45,7 @@
44
45
  "playwright": "^1.58.2",
45
46
  "postgres": "^3.4.8",
46
47
  "react": "^19.2.4",
48
+ "rrule": "^2.8.1",
47
49
  "tldts": "^7.0.23",
48
50
  "tree-sitter-bash": "0.25.1",
49
51
  "uuid": "^11.1.0",
@@ -0,0 +1,562 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * Capture X.com GraphQL API calls via Chrome CDP.
4
+ *
5
+ * Usage:
6
+ * 1. Make sure Chrome is running with CDP (vellum x refresh will do this)
7
+ * 2. Run: bun run scripts/capture-x-graphql.ts [--auto] [--all]
8
+ * 3. In --auto mode, Chrome is navigated automatically. Otherwise browse X manually.
9
+ * 4. Press Ctrl+C to stop (or wait for --auto to finish).
10
+ *
11
+ * Flags:
12
+ * --auto Automatically navigate Chrome through X.com pages via CDP
13
+ * --all Capture ALL GraphQL queries (skip relevance filter)
14
+ */
15
+
16
+ import { mkdirSync, existsSync } from 'node:fs';
17
+
18
+ const CDP_BASE = 'http://localhost:9222';
19
+ const CAPTURE_DIR = '/tmp/x-graphql-capture';
20
+
21
+ // ─── Relevance filter ────────────────────────────────────────────────────────
22
+
23
+ const RELEVANT_QUERIES = new Set([
24
+ // Reads
25
+ 'UserByScreenName', 'UserTweets', 'TweetDetail', 'TweetResultByRestId',
26
+ 'SearchTimeline', 'Bookmarks', 'Likes', 'Favoriters',
27
+ 'Followers', 'Following', 'HomeTimeline', 'HomeLatestTimeline',
28
+ 'NotificationsTimeline', 'ListTimeline',
29
+ 'UserMedia',
30
+ // Writes
31
+ 'CreateTweet', 'DeleteTweet', 'FavoriteTweet',
32
+ 'UnfavoriteTweet', 'CreateRetweet', 'DeleteRetweet', 'CreateBookmark',
33
+ 'DeleteBookmark',
34
+ ]);
35
+
36
+ // ─── Types ───────────────────────────────────────────────────────────────────
37
+
38
+ interface CapturedQuery {
39
+ queryName: string;
40
+ queryId: string;
41
+ method: string;
42
+ variables: unknown;
43
+ features?: unknown;
44
+ response?: unknown;
45
+ timestamp: number;
46
+ }
47
+
48
+ // ─── Minimal CDP WebSocket client ────────────────────────────────────────────
49
+
50
+ class CDPClient {
51
+ private ws: WebSocket | null = null;
52
+ private nextId = 1;
53
+ private callbacks = new Map<number, { resolve: (v: unknown) => void; reject: (e: Error) => void }>();
54
+ private eventHandlers = new Map<string, Array<(params: Record<string, unknown>) => void>>();
55
+
56
+ async connect(wsUrl: string): Promise<void> {
57
+ return new Promise((resolve, reject) => {
58
+ const ws = new WebSocket(wsUrl);
59
+ ws.onopen = () => { this.ws = ws; resolve(); };
60
+ ws.onerror = (e) => reject(new Error(`CDP error: ${e}`));
61
+ ws.onclose = () => { this.ws = null; };
62
+ ws.onmessage = (event) => {
63
+ const msg = JSON.parse(String(event.data));
64
+ if (msg.id != null) {
65
+ const cb = this.callbacks.get(msg.id);
66
+ if (cb) { this.callbacks.delete(msg.id); msg.error ? cb.reject(new Error(msg.error.message)) : cb.resolve(msg.result); }
67
+ } else if (msg.method) {
68
+ for (const h of this.eventHandlers.get(msg.method) ?? []) h(msg.params ?? {});
69
+ }
70
+ };
71
+ });
72
+ }
73
+
74
+ async send(method: string, params?: Record<string, unknown>): Promise<unknown> {
75
+ const id = this.nextId++;
76
+ return new Promise((resolve, reject) => {
77
+ this.callbacks.set(id, { resolve, reject });
78
+ this.ws!.send(JSON.stringify({ id, method, params }));
79
+ });
80
+ }
81
+
82
+ on(event: string, handler: (params: Record<string, unknown>) => void) {
83
+ const list = this.eventHandlers.get(event) ?? [];
84
+ list.push(handler);
85
+ this.eventHandlers.set(event, list);
86
+ }
87
+
88
+ close() { this.ws?.close(); }
89
+ }
90
+
91
+ // ─── State ───────────────────────────────────────────────────────────────────
92
+
93
+ const args = process.argv.slice(2);
94
+ const autoMode = args.includes('--auto');
95
+ const captureAll = args.includes('--all');
96
+
97
+ const captured: CapturedQuery[] = [];
98
+ const seenQueries = new Set<string>();
99
+
100
+ // Ensure capture directory exists
101
+ if (!existsSync(CAPTURE_DIR)) mkdirSync(CAPTURE_DIR, { recursive: true });
102
+
103
+ // ─── Auto-navigation steps ───────────────────────────────────────────────────
104
+
105
+ interface GuideStep {
106
+ label: string;
107
+ url?: string;
108
+ clickSelector?: string;
109
+ expectedQueries: string[];
110
+ }
111
+
112
+ // Resolve the logged-in user's screen name for profile-based URLs
113
+ async function getScreenName(): Promise<string | null> {
114
+ if (!navigationClient) return null;
115
+ try {
116
+ const result = await navigationClient.send('Runtime.evaluate', {
117
+ expression: `
118
+ (function() {
119
+ const link = document.querySelector('a[data-testid="AppTabBar_Profile_Link"]');
120
+ if (link) return link.getAttribute('href')?.replace('/', '') ?? null;
121
+ return null;
122
+ })()
123
+ `,
124
+ awaitPromise: false,
125
+ returnByValue: true,
126
+ }) as { result?: { value?: string | null } };
127
+ return result?.result?.value ?? null;
128
+ } catch {
129
+ return null;
130
+ }
131
+ }
132
+
133
+ const GUIDE_STEPS: GuideStep[] = [
134
+ {
135
+ label: 'Home timeline',
136
+ url: 'https://x.com/home',
137
+ expectedQueries: ['HomeTimeline', 'HomeLatestTimeline'],
138
+ },
139
+ {
140
+ label: 'Profile',
141
+ // URL set dynamically in runAutoMode after resolving screen name
142
+ clickSelector: 'a[data-testid="AppTabBar_Profile_Link"]',
143
+ expectedQueries: ['UserByScreenName', 'UserTweets'],
144
+ },
145
+ {
146
+ label: 'Tweet detail',
147
+ clickSelector: 'article[data-testid="tweet"] a[href*="/status/"]',
148
+ expectedQueries: ['TweetDetail'],
149
+ },
150
+ {
151
+ label: 'Search',
152
+ url: 'https://x.com/search?q=hello&src=typed_query',
153
+ expectedQueries: ['SearchTimeline'],
154
+ },
155
+ {
156
+ label: 'Bookmarks',
157
+ url: 'https://x.com/i/bookmarks',
158
+ expectedQueries: ['Bookmarks'],
159
+ },
160
+ {
161
+ label: 'Notifications',
162
+ url: 'https://x.com/notifications',
163
+ expectedQueries: ['NotificationsTimeline'],
164
+ },
165
+ {
166
+ label: 'Likes',
167
+ // URL set dynamically
168
+ expectedQueries: ['Likes'],
169
+ },
170
+ {
171
+ label: 'Followers',
172
+ // URL set dynamically
173
+ expectedQueries: ['Followers'],
174
+ },
175
+ {
176
+ label: 'Following',
177
+ // URL set dynamically
178
+ expectedQueries: ['Following'],
179
+ },
180
+ {
181
+ label: 'Media',
182
+ // URL set dynamically
183
+ expectedQueries: ['UserMedia'],
184
+ },
185
+ ];
186
+
187
+ // ─── Discover Chrome tabs ────────────────────────────────────────────────────
188
+
189
+ const res = await fetch(`${CDP_BASE}/json/list`);
190
+ if (!res.ok) {
191
+ console.error('Chrome CDP not available. Run `vellum x refresh` first.');
192
+ process.exit(1);
193
+ }
194
+ const targets = (await res.json()) as Array<{ type: string; url: string; webSocketDebuggerUrl: string }>;
195
+ const pages = targets.filter(t => t.type === 'page');
196
+
197
+ if (pages.length === 0) {
198
+ console.error('No pages found in Chrome.');
199
+ process.exit(1);
200
+ }
201
+
202
+ console.log(`Found ${pages.length} tab(s). Attaching to all...`);
203
+
204
+ // ─── Pending request tracking ────────────────────────────────────────────────
205
+
206
+ const pendingRequests = new Map<string, { url: string; queryName: string }>();
207
+ // Resolve callbacks for --auto mode: queryName → resolve function
208
+ const queryWaiters = new Map<string, () => void>();
209
+
210
+ function notifyQuerySeen(queryName: string) {
211
+ seenQueries.add(queryName);
212
+ const waiter = queryWaiters.get(queryName);
213
+ if (waiter) {
214
+ queryWaiters.delete(queryName);
215
+ waiter();
216
+ }
217
+ }
218
+
219
+ function waitForQuery(queryName: string, timeoutMs = 15000): Promise<boolean> {
220
+ if (seenQueries.has(queryName)) return Promise.resolve(true);
221
+ return new Promise(resolve => {
222
+ const timer = setTimeout(() => {
223
+ queryWaiters.delete(queryName);
224
+ resolve(false);
225
+ }, timeoutMs);
226
+ queryWaiters.set(queryName, () => {
227
+ clearTimeout(timer);
228
+ resolve(true);
229
+ });
230
+ });
231
+ }
232
+
233
+ function waitForAnyQuery(queryNames: string[], timeoutMs = 15000): Promise<boolean> {
234
+ if (queryNames.some(q => seenQueries.has(q))) return Promise.resolve(true);
235
+ return new Promise(resolve => {
236
+ const timer = setTimeout(() => {
237
+ for (const q of queryNames) queryWaiters.delete(q);
238
+ resolve(false);
239
+ }, timeoutMs);
240
+ for (const q of queryNames) {
241
+ queryWaiters.set(q, () => {
242
+ clearTimeout(timer);
243
+ for (const q2 of queryNames) queryWaiters.delete(q2);
244
+ resolve(true);
245
+ });
246
+ }
247
+ });
248
+ }
249
+
250
+ // ─── Attach to all tabs ──────────────────────────────────────────────────────
251
+
252
+ // We'll keep a reference to one client that's on an x.com tab for navigation
253
+ let navigationClient: CDPClient | null = null;
254
+ let navigationWsUrl: string | null = null;
255
+
256
+ for (const page of pages) {
257
+ const client = new CDPClient();
258
+ await client.connect(page.webSocketDebuggerUrl);
259
+ await client.send('Network.enable');
260
+
261
+ // Track which client is on an x.com tab for navigation
262
+ if (page.url.includes('x.com') || page.url.includes('twitter.com')) {
263
+ navigationClient = client;
264
+ navigationWsUrl = page.webSocketDebuggerUrl;
265
+ }
266
+
267
+ client.on('Network.requestWillBeSent', (params) => {
268
+ const req = params.request as Record<string, unknown> | undefined;
269
+ const url = (req?.url ?? params.url) as string | undefined;
270
+ if (!url?.includes('/i/api/graphql/')) return;
271
+
272
+ const match = url.match(/\/graphql\/([^/]+)\/([^?]+)/);
273
+ const queryId = match?.[1] ?? 'unknown';
274
+ const queryName = match?.[2] ?? 'unknown';
275
+ const method = (req?.method as string) ?? 'GET';
276
+
277
+ // Apply relevance filter
278
+ if (!captureAll && !RELEVANT_QUERIES.has(queryName)) return;
279
+
280
+ let variables: unknown = undefined;
281
+ let features: unknown = undefined;
282
+
283
+ if (method === 'POST' && req?.postData) {
284
+ try {
285
+ const body = JSON.parse(req.postData as string);
286
+ variables = body.variables;
287
+ features = body.features;
288
+ } catch { /* ignore */ }
289
+ } else if (method === 'GET') {
290
+ try {
291
+ const u = new URL(url);
292
+ const v = u.searchParams.get('variables');
293
+ if (v) variables = JSON.parse(v);
294
+ const f = u.searchParams.get('features');
295
+ if (f) features = JSON.parse(f);
296
+ } catch { /* ignore */ }
297
+ }
298
+
299
+ console.log(`\n>>> ${method} ${queryName} (${queryId})`);
300
+ if (variables) console.log(` variables: ${JSON.stringify(variables).slice(0, 200)}`);
301
+
302
+ pendingRequests.set(params.requestId as string, { url, queryName });
303
+ captured.push({ queryName, queryId, method, variables, features, timestamp: Date.now() });
304
+ });
305
+
306
+ client.on('Network.responseReceived', (params) => {
307
+ const requestId = params.requestId as string;
308
+ const pending = pendingRequests.get(requestId);
309
+ if (!pending) return;
310
+
311
+ const response = params.response as Record<string, unknown>;
312
+ const status = response.status as number;
313
+ console.log(` <<< ${status}`);
314
+
315
+ // Get full response body
316
+ client.send('Network.getResponseBody', { requestId }).then((result) => {
317
+ const body = (result as Record<string, unknown>).body as string;
318
+ try {
319
+ const json = JSON.parse(body);
320
+ // Attach full response to the captured entry
321
+ const entry = [...captured].reverse().find(e => e.queryName === pending.queryName && !e.response);
322
+ if (entry) {
323
+ entry.response = json;
324
+
325
+ // Write individual file
326
+ const filename = `${pending.queryName}-${entry.timestamp}.json`;
327
+ Bun.write(`${CAPTURE_DIR}/${filename}`, JSON.stringify({
328
+ queryName: entry.queryName,
329
+ queryId: entry.queryId,
330
+ method: entry.method,
331
+ variables: entry.variables,
332
+ features: entry.features,
333
+ response: json,
334
+ }, null, 2));
335
+ }
336
+
337
+ // Notify waiters
338
+ notifyQuerySeen(pending.queryName);
339
+ } catch { /* ignore */ }
340
+ }).catch(() => { /* body not available */ });
341
+
342
+ pendingRequests.delete(requestId);
343
+ });
344
+ }
345
+
346
+ // If no x.com tab found, use the first page for navigation
347
+ if (!navigationClient && pages.length > 0) {
348
+ navigationClient = new CDPClient();
349
+ await navigationClient.connect(pages[0].webSocketDebuggerUrl);
350
+ navigationWsUrl = pages[0].webSocketDebuggerUrl;
351
+ }
352
+
353
+ // ─── CDP navigation helpers ──────────────────────────────────────────────────
354
+
355
+ async function navigateTo(url: string): Promise<void> {
356
+ if (!navigationClient) return;
357
+ await navigationClient.send('Page.navigate', { url });
358
+ // Wait for page to load
359
+ await new Promise(r => setTimeout(r, 3000));
360
+ }
361
+
362
+ async function clickElement(selector: string): Promise<boolean> {
363
+ if (!navigationClient) return false;
364
+ try {
365
+ const result = await navigationClient.send('Runtime.evaluate', {
366
+ expression: `
367
+ (function() {
368
+ const el = document.querySelector(${JSON.stringify(selector)});
369
+ if (!el) return false;
370
+ el.scrollIntoView({ block: 'center' });
371
+ el.click();
372
+ return true;
373
+ })()
374
+ `,
375
+ awaitPromise: false,
376
+ returnByValue: true,
377
+ }) as { result?: { value?: boolean } };
378
+ return result?.result?.value === true;
379
+ } catch {
380
+ return false;
381
+ }
382
+ }
383
+
384
+ async function scrollDown(): Promise<void> {
385
+ if (!navigationClient) return;
386
+ try {
387
+ await navigationClient.send('Runtime.evaluate', {
388
+ expression: 'window.scrollBy(0, 800)',
389
+ awaitPromise: false,
390
+ });
391
+ } catch { /* ignore */ }
392
+ }
393
+
394
+ // ─── Auto mode ───────────────────────────────────────────────────────────────
395
+
396
+ async function runAutoMode() {
397
+ console.log('\n🚗 Auto mode: navigating Chrome through X.com...\n');
398
+
399
+ // Enable Page domain for navigation
400
+ if (navigationClient) {
401
+ await navigationClient.send('Page.enable').catch(() => {});
402
+ }
403
+
404
+ // Navigate to home first to discover the screen name
405
+ await navigateTo('https://x.com/home');
406
+ const screenName = await getScreenName();
407
+ if (screenName) {
408
+ console.log(` Detected user: @${screenName}\n`);
409
+ // Fill in profile-based URLs
410
+ for (const step of GUIDE_STEPS) {
411
+ if (step.label === 'Likes') step.url = `https://x.com/${screenName}/likes`;
412
+ if (step.label === 'Followers') step.url = `https://x.com/${screenName}/followers`;
413
+ if (step.label === 'Following') step.url = `https://x.com/${screenName}/following`;
414
+ if (step.label === 'Media') step.url = `https://x.com/${screenName}/media`;
415
+ }
416
+ } else {
417
+ console.log(' Could not detect screen name — some steps will use click navigation\n');
418
+ }
419
+
420
+ const completedSteps: string[] = [];
421
+ const failedSteps: string[] = [];
422
+
423
+ for (const step of GUIDE_STEPS) {
424
+ const alreadySeen = step.expectedQueries.some(q => seenQueries.has(q));
425
+ if (alreadySeen) {
426
+ console.log(` ✓ ${step.label} (already captured)`);
427
+ completedSteps.push(step.label);
428
+ continue;
429
+ }
430
+
431
+ process.stdout.write(` ⏳ ${step.label}...`);
432
+
433
+ // Navigate if URL provided
434
+ if (step.url) {
435
+ await navigateTo(step.url);
436
+ }
437
+
438
+ // Click if selector provided
439
+ if (step.clickSelector) {
440
+ await new Promise(r => setTimeout(r, 1500)); // wait for page to settle
441
+ const clicked = await clickElement(step.clickSelector);
442
+ if (!clicked) {
443
+ // Try scrolling and clicking again
444
+ await scrollDown();
445
+ await new Promise(r => setTimeout(r, 1000));
446
+ await clickElement(step.clickSelector);
447
+ }
448
+ await new Promise(r => setTimeout(r, 2000)); // wait for navigation
449
+ }
450
+
451
+ // Scroll to trigger lazy-loaded content
452
+ await scrollDown();
453
+
454
+ // Wait for any expected query
455
+ const seen = await waitForAnyQuery(step.expectedQueries, 10000);
456
+
457
+ if (seen) {
458
+ const captured = step.expectedQueries.filter(q => seenQueries.has(q));
459
+ console.log(`\r ✅ ${step.label} → ${captured.join(', ')}`);
460
+ completedSteps.push(step.label);
461
+ } else {
462
+ console.log(`\r ⚠️ ${step.label} → no queries captured (page may need manual interaction)`);
463
+ failedSteps.push(step.label);
464
+ }
465
+ }
466
+
467
+ console.log(`\n🏁 Auto navigation complete: ${completedSteps.length}/${GUIDE_STEPS.length} steps succeeded`);
468
+ if (failedSteps.length > 0) {
469
+ console.log(` Missed: ${failedSteps.join(', ')}`);
470
+ }
471
+
472
+ // Finish
473
+ printSummary();
474
+ process.exit(0);
475
+ }
476
+
477
+ // ─── Summary ─────────────────────────────────────────────────────────────────
478
+
479
+ function printSummary() {
480
+ console.log(`\n\n${'='.repeat(60)}`);
481
+ console.log(` Captured ${captured.length} GraphQL requests`);
482
+ console.log(`${'='.repeat(60)}\n`);
483
+
484
+ // Dedupe by queryName
485
+ const seen = new Set<string>();
486
+ const unique = captured.filter(q => {
487
+ if (seen.has(q.queryName)) return false;
488
+ seen.add(q.queryName);
489
+ return true;
490
+ });
491
+
492
+ // Print table
493
+ const nameWidth = Math.max(25, ...unique.map(q => q.queryName.length));
494
+ const idWidth = 22;
495
+ const methodWidth = 6;
496
+
497
+ console.log(
498
+ ` ${'Query'.padEnd(nameWidth)} ${'QueryID'.padEnd(idWidth)} ${'Method'.padEnd(methodWidth)} Variables`,
499
+ );
500
+ console.log(` ${'─'.repeat(nameWidth)} ${'─'.repeat(idWidth)} ${'─'.repeat(methodWidth)} ${'─'.repeat(30)}`);
501
+
502
+ for (const q of unique) {
503
+ const varKeys = q.variables && typeof q.variables === 'object'
504
+ ? Object.keys(q.variables as Record<string, unknown>).join(', ')
505
+ : '—';
506
+ console.log(
507
+ ` ${q.queryName.padEnd(nameWidth)} ${q.queryId.padEnd(idWidth)} ${q.method.padEnd(methodWidth)} ${varKeys}`,
508
+ );
509
+ }
510
+
511
+ // Gap analysis
512
+ if (!captureAll) {
513
+ const notSeen = [...RELEVANT_QUERIES].filter(q => !seenQueries.has(q));
514
+ if (notSeen.length > 0) {
515
+ console.log(`\n ⚠️ Not captured (${notSeen.length}):`);
516
+ for (const q of notSeen) {
517
+ console.log(` • ${q}`);
518
+ }
519
+ } else {
520
+ console.log('\n ✅ All relevant queries captured!');
521
+ }
522
+ }
523
+
524
+ // Save summary
525
+ const summaryPath = `${CAPTURE_DIR}/summary.json`;
526
+ const summary = {
527
+ capturedAt: new Date().toISOString(),
528
+ totalRequests: captured.length,
529
+ uniqueQueries: unique.map(q => ({
530
+ queryName: q.queryName,
531
+ queryId: q.queryId,
532
+ method: q.method,
533
+ variableKeys: q.variables && typeof q.variables === 'object'
534
+ ? Object.keys(q.variables as Record<string, unknown>)
535
+ : [],
536
+ })),
537
+ notCaptured: captureAll ? [] : [...RELEVANT_QUERIES].filter(q => !seenQueries.has(q)),
538
+ };
539
+ Bun.write(summaryPath, JSON.stringify(summary, null, 2));
540
+ console.log(`\n Summary saved to ${summaryPath}`);
541
+ console.log(` Individual captures in ${CAPTURE_DIR}/`);
542
+ }
543
+
544
+ // ─── Main ────────────────────────────────────────────────────────────────────
545
+
546
+ if (autoMode) {
547
+ console.log('\nRecording X.com GraphQL requests (auto mode)...');
548
+ console.log(`Filter: ${captureAll ? 'ALL queries' : `${RELEVANT_QUERIES.size} relevant queries`}`);
549
+ // Give network listeners a moment to settle, then start auto-navigation
550
+ setTimeout(() => runAutoMode(), 1000);
551
+ } else {
552
+ console.log('\nRecording X.com GraphQL requests...');
553
+ console.log(`Filter: ${captureAll ? 'ALL queries' : `${RELEVANT_QUERIES.size} relevant queries`}`);
554
+ console.log('Browse X in Chrome — visit a profile, scroll tweets, search.');
555
+ console.log('Press Ctrl+C to stop and dump results.\n');
556
+ }
557
+
558
+ // Ctrl+C handler
559
+ process.on('SIGINT', () => {
560
+ printSummary();
561
+ process.exit(0);
562
+ });
@@ -47,10 +47,11 @@ const SWIFT_OMIT_ALLOWLIST = new Set<string>([
47
47
  // Watcher messages — not yet consumed by the macOS client
48
48
  'watcher_escalation',
49
49
  'watcher_notification',
50
+ // Agent heartbeat alerts — not yet consumed by the macOS client
51
+ 'agent_heartbeat_alert',
50
52
  // Browser handoff — not yet consumed by the macOS client
51
53
  'browser_handoff_request',
52
54
  // Work item messages — not yet consumed by the macOS client
53
- 'work_item_create_response',
54
55
  'work_item_get_response',
55
56
  'work_item_run_task_response',
56
57
  'work_item_status_changed',
package/scripts/test.sh CHANGED
@@ -36,6 +36,11 @@ while IFS= read -r test_file; do
36
36
  continue
37
37
  fi
38
38
  fi
39
+ # Always exclude benchmark files — run them with `bun run test:bench` instead
40
+ if [[ "$(basename "${test_file}")" == *.benchmark.test.ts ]]; then
41
+ continue
42
+ fi
43
+
39
44
  test_files+=("${test_file}")
40
45
  done < <(find src/__tests__ -maxdepth 1 -type f -name '*.test.ts' | sort)
41
46