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
@@ -537,6 +537,25 @@ exports[`IPC message snapshots ClientMessage types vercel_api_config serializes
537
537
  }
538
538
  `;
539
539
 
540
+ exports[`IPC message snapshots ClientMessage types twitter_integration_config serializes to expected JSON 1`] = `
541
+ {
542
+ "action": "get",
543
+ "type": "twitter_integration_config",
544
+ }
545
+ `;
546
+
547
+ exports[`IPC message snapshots ClientMessage types twitter_auth_start serializes to expected JSON 1`] = `
548
+ {
549
+ "type": "twitter_auth_start",
550
+ }
551
+ `;
552
+
553
+ exports[`IPC message snapshots ClientMessage types twitter_auth_status serializes to expected JSON 1`] = `
554
+ {
555
+ "type": "twitter_auth_status",
556
+ }
557
+ `;
558
+
540
559
  exports[`IPC message snapshots ClientMessage types link_open_request serializes to expected JSON 1`] = `
541
560
  {
542
561
  "type": "link_open_request",
@@ -668,17 +687,6 @@ exports[`IPC message snapshots ClientMessage types work_item_get serializes to e
668
687
  }
669
688
  `;
670
689
 
671
- exports[`IPC message snapshots ClientMessage types work_item_create serializes to expected JSON 1`] = `
672
- {
673
- "notes": "High priority",
674
- "priorityTier": 1,
675
- "sortIndex": 0,
676
- "taskId": "task-001",
677
- "title": "Process report",
678
- "type": "work_item_create",
679
- }
680
- `;
681
-
682
690
  exports[`IPC message snapshots ClientMessage types work_item_update serializes to expected JSON 1`] = `
683
691
  {
684
692
  "id": "wi-001",
@@ -709,6 +717,38 @@ exports[`IPC message snapshots ClientMessage types work_item_run_task serializes
709
717
  }
710
718
  `;
711
719
 
720
+ exports[`IPC message snapshots ClientMessage types work_item_output serializes to expected JSON 1`] = `
721
+ {
722
+ "id": "wi-001",
723
+ "type": "work_item_output",
724
+ }
725
+ `;
726
+
727
+ exports[`IPC message snapshots ClientMessage types work_item_preflight serializes to expected JSON 1`] = `
728
+ {
729
+ "id": "wi-001",
730
+ "type": "work_item_preflight",
731
+ }
732
+ `;
733
+
734
+ exports[`IPC message snapshots ClientMessage types work_item_approve_permissions serializes to expected JSON 1`] = `
735
+ {
736
+ "approvedTools": [
737
+ "bash",
738
+ "file_write",
739
+ ],
740
+ "id": "wi-001",
741
+ "type": "work_item_approve_permissions",
742
+ }
743
+ `;
744
+
745
+ exports[`IPC message snapshots ClientMessage types work_item_cancel serializes to expected JSON 1`] = `
746
+ {
747
+ "id": "wi-001",
748
+ "type": "work_item_cancel",
749
+ }
750
+ `;
751
+
712
752
  exports[`IPC message snapshots ClientMessage types document_save serializes to expected JSON 1`] = `
713
753
  {
714
754
  "content": "# Hello",
@@ -743,7 +783,6 @@ exports[`IPC message snapshots ClientMessage types subagent_abort serializes to
743
783
 
744
784
  exports[`IPC message snapshots ClientMessage types subagent_status serializes to expected JSON 1`] = `
745
785
  {
746
- "sessionId": "sess-001",
747
786
  "subagentId": "sub-001",
748
787
  "type": "subagent_status",
749
788
  }
@@ -1451,12 +1490,14 @@ exports[`IPC message snapshots ServerMessage types schedules_list_response seria
1451
1490
  "cronExpression": "0 9 * * 1-5",
1452
1491
  "description": "Every weekday at 9:00 AM",
1453
1492
  "enabled": true,
1493
+ "expression": "0 9 * * 1-5",
1454
1494
  "id": "sched-001",
1455
1495
  "lastRunAt": 1700000000000,
1456
1496
  "lastStatus": "ok",
1457
1497
  "message": "Remind me about the standup",
1458
1498
  "name": "Daily standup reminder",
1459
1499
  "nextRunAt": 1700100000000,
1500
+ "syntax": "cron",
1460
1501
  "timezone": "America/Los_Angeles",
1461
1502
  },
1462
1503
  ],
@@ -1737,6 +1778,34 @@ exports[`IPC message snapshots ServerMessage types vercel_api_config_response se
1737
1778
  }
1738
1779
  `;
1739
1780
 
1781
+ exports[`IPC message snapshots ServerMessage types twitter_integration_config_response serializes to expected JSON 1`] = `
1782
+ {
1783
+ "connected": false,
1784
+ "localClientConfigured": true,
1785
+ "managedAvailable": false,
1786
+ "mode": "local_byo",
1787
+ "success": true,
1788
+ "type": "twitter_integration_config_response",
1789
+ }
1790
+ `;
1791
+
1792
+ exports[`IPC message snapshots ServerMessage types twitter_auth_result serializes to expected JSON 1`] = `
1793
+ {
1794
+ "accountInfo": "@vellum_test",
1795
+ "success": true,
1796
+ "type": "twitter_auth_result",
1797
+ }
1798
+ `;
1799
+
1800
+ exports[`IPC message snapshots ServerMessage types twitter_auth_status_response serializes to expected JSON 1`] = `
1801
+ {
1802
+ "accountInfo": "@vellum_test",
1803
+ "connected": true,
1804
+ "mode": "local_byo",
1805
+ "type": "twitter_auth_status_response",
1806
+ }
1807
+ `;
1808
+
1740
1809
  exports[`IPC message snapshots ServerMessage types open_url serializes to expected JSON 1`] = `
1741
1810
  {
1742
1811
  "title": "Example",
@@ -1987,28 +2056,6 @@ exports[`IPC message snapshots ServerMessage types work_item_get_response serial
1987
2056
  }
1988
2057
  `;
1989
2058
 
1990
- exports[`IPC message snapshots ServerMessage types work_item_create_response serializes to expected JSON 1`] = `
1991
- {
1992
- "item": {
1993
- "createdAt": 1700000000,
1994
- "id": "wi-001",
1995
- "lastRunConversationId": null,
1996
- "lastRunId": null,
1997
- "lastRunStatus": null,
1998
- "notes": null,
1999
- "priorityTier": 1,
2000
- "sortIndex": null,
2001
- "sourceId": null,
2002
- "sourceType": null,
2003
- "status": "queued",
2004
- "taskId": "task-001",
2005
- "title": "Process report",
2006
- "updatedAt": 1700000000,
2007
- },
2008
- "type": "work_item_create_response",
2009
- }
2010
- `;
2011
-
2012
2059
  exports[`IPC message snapshots ServerMessage types work_item_update_response serializes to expected JSON 1`] = `
2013
2060
  {
2014
2061
  "item": {
@@ -2048,6 +2095,58 @@ exports[`IPC message snapshots ServerMessage types work_item_run_task_response s
2048
2095
  }
2049
2096
  `;
2050
2097
 
2098
+ exports[`IPC message snapshots ServerMessage types work_item_output_response serializes to expected JSON 1`] = `
2099
+ {
2100
+ "id": "wi-001",
2101
+ "output": {
2102
+ "completedAt": 1700002000,
2103
+ "conversationId": "conv-001",
2104
+ "highlights": [
2105
+ "- Key finding 1",
2106
+ "- Key finding 2",
2107
+ ],
2108
+ "runId": "run-001",
2109
+ "status": "completed",
2110
+ "summary": "Report processed successfully.",
2111
+ "title": "Process report",
2112
+ },
2113
+ "success": true,
2114
+ "type": "work_item_output_response",
2115
+ }
2116
+ `;
2117
+
2118
+ exports[`IPC message snapshots ServerMessage types work_item_preflight_response serializes to expected JSON 1`] = `
2119
+ {
2120
+ "id": "wi-001",
2121
+ "permissions": [
2122
+ {
2123
+ "currentDecision": "prompt",
2124
+ "description": "Run shell commands",
2125
+ "riskLevel": "medium",
2126
+ "tool": "bash",
2127
+ },
2128
+ ],
2129
+ "success": true,
2130
+ "type": "work_item_preflight_response",
2131
+ }
2132
+ `;
2133
+
2134
+ exports[`IPC message snapshots ServerMessage types work_item_approve_permissions_response serializes to expected JSON 1`] = `
2135
+ {
2136
+ "id": "wi-001",
2137
+ "success": true,
2138
+ "type": "work_item_approve_permissions_response",
2139
+ }
2140
+ `;
2141
+
2142
+ exports[`IPC message snapshots ServerMessage types work_item_cancel_response serializes to expected JSON 1`] = `
2143
+ {
2144
+ "id": "wi-001",
2145
+ "success": true,
2146
+ "type": "work_item_cancel_response",
2147
+ }
2148
+ `;
2149
+
2051
2150
  exports[`IPC message snapshots ServerMessage types work_item_status_changed serializes to expected JSON 1`] = `
2052
2151
  {
2053
2152
  "item": {
@@ -28,7 +28,7 @@ mock.module('../tools/registry.js', () => ({
28
28
  registerTool: () => {},
29
29
  }));
30
30
 
31
- import { initializeDb, getDb } from '../memory/db.js';
31
+ import { initializeDb, getDb, resetDb } from '../memory/db.js';
32
32
  import {
33
33
  createAccount,
34
34
  listAccounts,
@@ -47,6 +47,7 @@ const _ctx: ToolContext = {
47
47
  };
48
48
 
49
49
  afterAll(() => {
50
+ resetDb();
50
51
  mock.restore();
51
52
  try { rmSync(testDir, { recursive: true }); } catch { /* best effort */ }
52
53
  });
@@ -0,0 +1,250 @@
1
+ import { describe, test, expect, beforeEach, mock } from 'bun:test';
2
+ import { mkdirSync, writeFileSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { tmpdir } from 'node:os';
5
+
6
+ // Mock platform to use a temp workspace dir
7
+ let testWorkspaceDir: string;
8
+
9
+ mock.module('../util/platform.js', () => ({
10
+ getWorkspacePromptPath: (file: string) => join(testWorkspaceDir, file),
11
+ }));
12
+
13
+ // Mock config loader
14
+ let mockConfig = {
15
+ agentHeartbeat: {
16
+ enabled: true,
17
+ intervalMs: 60_000,
18
+ activeHoursStart: undefined as number | undefined,
19
+ activeHoursEnd: undefined as number | undefined,
20
+ },
21
+ };
22
+
23
+ mock.module('../config/loader.js', () => ({
24
+ getConfig: () => mockConfig,
25
+ loadConfig: () => mockConfig,
26
+ }));
27
+
28
+ // Mock conversation store
29
+ const createdConversations: Array<{ title: string; threadType: string }> = [];
30
+ let conversationIdCounter = 0;
31
+
32
+ mock.module('../memory/conversation-store.js', () => ({
33
+ createConversation: (opts: { title: string; threadType: string }) => {
34
+ createdConversations.push(opts);
35
+ return { id: `conv-${++conversationIdCounter}`, ...opts };
36
+ },
37
+ }));
38
+
39
+ // Mock logger
40
+ mock.module('../util/logger.js', () => ({
41
+ getLogger: () => ({
42
+ info: () => {},
43
+ debug: () => {},
44
+ warn: () => {},
45
+ error: () => {},
46
+ }),
47
+ }));
48
+
49
+ // Import after mocks are set up
50
+ const { AgentHeartbeatService } = await import('../agent-heartbeat/agent-heartbeat-service.js');
51
+
52
+ describe('AgentHeartbeatService', () => {
53
+ let processMessageCalls: Array<{ conversationId: string; content: string }>;
54
+ let alerterCalls: Array<{ type: string; title: string; body: string }>;
55
+
56
+ beforeEach(() => {
57
+ testWorkspaceDir = join(tmpdir(), `vellum-agent-hb-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
58
+ mkdirSync(testWorkspaceDir, { recursive: true });
59
+
60
+ processMessageCalls = [];
61
+ alerterCalls = [];
62
+ createdConversations.length = 0;
63
+ conversationIdCounter = 0;
64
+
65
+ mockConfig = {
66
+ agentHeartbeat: {
67
+ enabled: true,
68
+ intervalMs: 60_000,
69
+ activeHoursStart: undefined,
70
+ activeHoursEnd: undefined,
71
+ },
72
+ };
73
+ });
74
+
75
+ function createService(overrides?: {
76
+ processMessage?: (id: string, content: string) => Promise<{ messageId: string }>;
77
+ getCurrentHour?: () => number;
78
+ }) {
79
+ return new AgentHeartbeatService({
80
+ processMessage: overrides?.processMessage ?? (async (conversationId: string, content: string) => {
81
+ processMessageCalls.push({ conversationId, content });
82
+ return { messageId: 'msg-1' };
83
+ }),
84
+ alerter: (alert: { type: string; title: string; body: string }) => {
85
+ alerterCalls.push(alert);
86
+ },
87
+ getCurrentHour: overrides?.getCurrentHour,
88
+ });
89
+ }
90
+
91
+ test('runOnce() calls processMessage with correct prompt', async () => {
92
+ const service = createService();
93
+ await service.runOnce();
94
+
95
+ expect(processMessageCalls).toHaveLength(1);
96
+ expect(processMessageCalls[0].conversationId).toBe('conv-1');
97
+ expect(processMessageCalls[0].content).toContain('<heartbeat-checklist>');
98
+ expect(processMessageCalls[0].content).toContain('<heartbeat-disposition>');
99
+ expect(processMessageCalls[0].content).toContain('HEARTBEAT_OK');
100
+ expect(processMessageCalls[0].content).toContain('HEARTBEAT_ALERT');
101
+ });
102
+
103
+ test('HEARTBEAT.md content is embedded in prompt when file exists', async () => {
104
+ const customChecklist = '- Check the weather\n- Water the plants';
105
+ writeFileSync(join(testWorkspaceDir, 'HEARTBEAT.md'), customChecklist);
106
+
107
+ const service = createService();
108
+ await service.runOnce();
109
+
110
+ expect(processMessageCalls).toHaveLength(1);
111
+ expect(processMessageCalls[0].content).toContain('Check the weather');
112
+ expect(processMessageCalls[0].content).toContain('Water the plants');
113
+ });
114
+
115
+ test('default checklist used when no HEARTBEAT.md', async () => {
116
+ const service = createService();
117
+ await service.runOnce();
118
+
119
+ expect(processMessageCalls).toHaveLength(1);
120
+ expect(processMessageCalls[0].content).toContain('Check the current weather');
121
+ });
122
+
123
+ test('creates background conversation titled "Agent Heartbeat"', async () => {
124
+ const service = createService();
125
+ await service.runOnce();
126
+
127
+ expect(createdConversations).toHaveLength(1);
128
+ expect(createdConversations[0].title).toBe('Agent Heartbeat');
129
+ expect(createdConversations[0].threadType).toBe('background');
130
+ });
131
+
132
+ test('active hours guard skips outside window', async () => {
133
+ mockConfig.agentHeartbeat.activeHoursStart = 9;
134
+ mockConfig.agentHeartbeat.activeHoursEnd = 17;
135
+
136
+ const service = createService({ getCurrentHour: () => 3 });
137
+ await service.runOnce();
138
+
139
+ expect(processMessageCalls).toHaveLength(0);
140
+ });
141
+
142
+ test('active hours guard allows within window', async () => {
143
+ mockConfig.agentHeartbeat.activeHoursStart = 9;
144
+ mockConfig.agentHeartbeat.activeHoursEnd = 17;
145
+
146
+ const service = createService({ getCurrentHour: () => 12 });
147
+ await service.runOnce();
148
+
149
+ expect(processMessageCalls).toHaveLength(1);
150
+ });
151
+
152
+ test('active hours handles overnight window', async () => {
153
+ mockConfig.agentHeartbeat.activeHoursStart = 22;
154
+ mockConfig.agentHeartbeat.activeHoursEnd = 6;
155
+
156
+ // 23:00 should be within the window
157
+ const service = createService({ getCurrentHour: () => 23 });
158
+ await service.runOnce();
159
+ expect(processMessageCalls).toHaveLength(1);
160
+
161
+ // 10:00 should be outside the window
162
+ processMessageCalls.length = 0;
163
+ createdConversations.length = 0;
164
+ const service2 = createService({ getCurrentHour: () => 10 });
165
+ await service2.runOnce();
166
+ expect(processMessageCalls).toHaveLength(0);
167
+ });
168
+
169
+ test('overlap prevention works', async () => {
170
+ let resolveFirst: () => void;
171
+ const firstPromise = new Promise<void>((r) => { resolveFirst = r; });
172
+
173
+ const service = createService({
174
+ processMessage: async () => {
175
+ await firstPromise;
176
+ processMessageCalls.push({ conversationId: 'slow', content: 'slow' });
177
+ return { messageId: 'msg-1' };
178
+ },
179
+ });
180
+
181
+ // Start first run (will block)
182
+ const run1 = service.runOnce();
183
+ // Give the first run a tick to set activeRun
184
+ await new Promise((r) => setTimeout(r, 10));
185
+
186
+ // Second run should be skipped due to overlap
187
+ await service.runOnce();
188
+
189
+ // Resolve the first run
190
+ resolveFirst!();
191
+ await run1;
192
+
193
+ // Only the first run should have called processMessage
194
+ expect(processMessageCalls).toHaveLength(1);
195
+ });
196
+
197
+ test('disabled config prevents start', () => {
198
+ mockConfig.agentHeartbeat.enabled = false;
199
+ const service = createService();
200
+ service.start();
201
+ // No error, just a no-op. We can verify by calling stop which should also be a no-op.
202
+ // The key assertion is that no timer is set (verified by stop not hanging).
203
+ service.stop();
204
+ });
205
+
206
+ test('disabled config prevents runOnce', async () => {
207
+ mockConfig.agentHeartbeat.enabled = false;
208
+ const service = createService();
209
+ await service.runOnce();
210
+
211
+ expect(processMessageCalls).toHaveLength(0);
212
+ });
213
+
214
+ test('alerts on processMessage failure', async () => {
215
+ const service = createService({
216
+ processMessage: async () => {
217
+ throw new Error('LLM timeout');
218
+ },
219
+ });
220
+
221
+ await service.runOnce();
222
+
223
+ expect(alerterCalls).toHaveLength(1);
224
+ expect(alerterCalls[0].type).toBe('agent_heartbeat_alert');
225
+ expect(alerterCalls[0].title).toBe('Agent Heartbeat Failed');
226
+ expect(alerterCalls[0].body).toBe('LLM timeout');
227
+ });
228
+
229
+ test('alerts on conversation creation failure', async () => {
230
+ // Override createConversation to throw via a fresh import trick:
231
+ // Since createConversation is mocked at module level, we simulate
232
+ // this by having processMessage throw before it's called — but the
233
+ // real fix is that executeRun wraps createConversation in the try/catch.
234
+ // We verify by checking that any error in executeRun triggers the alert.
235
+ const service = createService({
236
+ processMessage: async () => {
237
+ throw new Error('DB locked');
238
+ },
239
+ });
240
+
241
+ await service.runOnce();
242
+
243
+ expect(alerterCalls).toHaveLength(1);
244
+ expect(alerterCalls[0].body).toBe('DB locked');
245
+ });
246
+
247
+ test('cleanup', () => {
248
+ try { rmSync(testWorkspaceDir, { recursive: true, force: true }); } catch { /* ignore */ }
249
+ });
250
+ });
@@ -36,7 +36,7 @@ mock.module('../config/loader.js', () => ({
36
36
  }),
37
37
  }));
38
38
 
39
- import { initializeDb, getDb } from '../memory/db.js';
39
+ import { initializeDb, getDb, resetDb } from '../memory/db.js';
40
40
  import { uploadAttachment, linkAttachmentToMessage } from '../memory/attachments-store.js';
41
41
  import { createConversation, addMessage } from '../memory/conversation-store.js';
42
42
  import { assetMaterializeTool } from '../tools/assets/materialize.js';
@@ -49,6 +49,7 @@ import { mkdirSync } from 'node:fs';
49
49
  mkdirSync(sandboxDir, { recursive: true });
50
50
 
51
51
  afterAll(() => {
52
+ resetDb();
52
53
  try { rmSync(testDir, { recursive: true }); } catch { /* best effort */ }
53
54
  });
54
55
 
@@ -103,7 +104,7 @@ describe('AssetMaterializeTool sandbox path enforcement', () => {
103
104
  beforeEach(resetTables);
104
105
 
105
106
  test('rejects path that escapes sandbox via ../', async () => {
106
- const stored = uploadAttachment('ast-1', 'test.png', 'image/png', 'AAAA');
107
+ const stored = uploadAttachment('test.png', 'image/png', 'AAAA');
107
108
  const result = await assetMaterializeTool.execute(
108
109
  { attachment_id: stored.id, destination_path: '../../etc/evil.png' },
109
110
  dummyContext,
@@ -113,7 +114,7 @@ describe('AssetMaterializeTool sandbox path enforcement', () => {
113
114
  });
114
115
 
115
116
  test('rejects absolute path outside sandbox', async () => {
116
- const stored = uploadAttachment('ast-1', 'test.png', 'image/png', 'AAAA');
117
+ const stored = uploadAttachment('test.png', 'image/png', 'AAAA');
117
118
  const result = await assetMaterializeTool.execute(
118
119
  { attachment_id: stored.id, destination_path: '/tmp/outside-sandbox/evil.png' },
119
120
  dummyContext,
@@ -123,7 +124,7 @@ describe('AssetMaterializeTool sandbox path enforcement', () => {
123
124
  });
124
125
 
125
126
  test('accepts relative path inside sandbox', async () => {
126
- const stored = uploadAttachment('ast-1', 'test.png', 'image/png', 'AAAA');
127
+ const stored = uploadAttachment('test.png', 'image/png', 'AAAA');
127
128
  const result = await assetMaterializeTool.execute(
128
129
  { attachment_id: stored.id, destination_path: 'output.png' },
129
130
  dummyContext,
@@ -133,7 +134,7 @@ describe('AssetMaterializeTool sandbox path enforcement', () => {
133
134
  });
134
135
 
135
136
  test('accepts nested path inside sandbox with auto-created subdirs', async () => {
136
- const stored = uploadAttachment('ast-1', 'test.png', 'image/png', 'AAAA');
137
+ const stored = uploadAttachment('test.png', 'image/png', 'AAAA');
137
138
  const result = await assetMaterializeTool.execute(
138
139
  { attachment_id: stored.id, destination_path: 'subdir/deep/output.png' },
139
140
  dummyContext,
@@ -172,7 +173,7 @@ describe('AssetMaterializeTool materialization', () => {
172
173
  const originalContent = 'Hello, World!';
173
174
  const base64Content = Buffer.from(originalContent).toString('base64');
174
175
 
175
- const stored = uploadAttachment('ast-1', 'hello.txt', 'text/plain', base64Content);
176
+ const stored = uploadAttachment('hello.txt', 'text/plain', base64Content);
176
177
 
177
178
  const destPath = 'materialized-hello.txt';
178
179
  const result = await assetMaterializeTool.execute(
@@ -194,7 +195,7 @@ describe('AssetMaterializeTool materialization', () => {
194
195
  const binaryBytes = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]);
195
196
  const base64Content = binaryBytes.toString('base64');
196
197
 
197
- const stored = uploadAttachment('ast-1', 'tiny.png', 'image/png', base64Content);
198
+ const stored = uploadAttachment('tiny.png', 'image/png', base64Content);
198
199
 
199
200
  const result = await assetMaterializeTool.execute(
200
201
  { attachment_id: stored.id, destination_path: 'images/tiny.png' },
@@ -209,7 +210,7 @@ describe('AssetMaterializeTool materialization', () => {
209
210
 
210
211
  test('result includes resolved path', async () => {
211
212
  const base64Content = Buffer.from('test').toString('base64');
212
- const stored = uploadAttachment('ast-1', 'doc.txt', 'text/plain', base64Content);
213
+ const stored = uploadAttachment('doc.txt', 'text/plain', base64Content);
213
214
 
214
215
  const result = await assetMaterializeTool.execute(
215
216
  { attachment_id: stored.id, destination_path: 'output/doc.txt' },
@@ -222,7 +223,7 @@ describe('AssetMaterializeTool materialization', () => {
222
223
 
223
224
  test('result includes filename, MIME type and size info', async () => {
224
225
  const base64Content = Buffer.from('some data here').toString('base64');
225
- const stored = uploadAttachment('ast-1', 'report.pdf', 'application/pdf', base64Content);
226
+ const stored = uploadAttachment('report.pdf', 'application/pdf', base64Content);
226
227
 
227
228
  const result = await assetMaterializeTool.execute(
228
229
  { attachment_id: stored.id, destination_path: 'report.pdf' },
@@ -307,7 +308,7 @@ describe('AssetMaterializeTool visibility policy', () => {
307
308
  test('materializing from a standard thread works from any context', async () => {
308
309
  const standardConv = createConversation({ title: 'standard-conv' });
309
310
  const base64Content = Buffer.from('standard content').toString('base64');
310
- const attachment = uploadAttachment('ast-1', 'public.txt', 'text/plain', base64Content);
311
+ const attachment = uploadAttachment('public.txt', 'text/plain', base64Content);
311
312
  const msg = addMessage(standardConv.id, 'user', 'standard message');
312
313
  linkAttachmentToMessage(msg.id, attachment.id, 0);
313
314
 
@@ -330,7 +331,7 @@ describe('AssetMaterializeTool visibility policy', () => {
330
331
  test('materializing from a private thread works within the same private thread', async () => {
331
332
  const privateConv = createConversation({ title: 'private-conv', threadType: 'private' });
332
333
  const base64Content = Buffer.from('private content').toString('base64');
333
- const attachment = uploadAttachment('ast-1', 'secret.txt', 'text/plain', base64Content);
334
+ const attachment = uploadAttachment('secret.txt', 'text/plain', base64Content);
334
335
  const msg = addMessage(privateConv.id, 'user', 'private message');
335
336
  linkAttachmentToMessage(msg.id, attachment.id, 0);
336
337
 
@@ -352,7 +353,7 @@ describe('AssetMaterializeTool visibility policy', () => {
352
353
  test('materializing from a private thread is REJECTED from a different conversation', async () => {
353
354
  const privateConv = createConversation({ title: 'private-conv', threadType: 'private' });
354
355
  const base64Content = Buffer.from('private content').toString('base64');
355
- const attachment = uploadAttachment('ast-1', 'secret.txt', 'text/plain', base64Content);
356
+ const attachment = uploadAttachment('secret.txt', 'text/plain', base64Content);
356
357
  const msg = addMessage(privateConv.id, 'user', 'private message');
357
358
  linkAttachmentToMessage(msg.id, attachment.id, 0);
358
359
 
@@ -376,7 +377,7 @@ describe('AssetMaterializeTool visibility policy', () => {
376
377
  test('error message is user-actionable', async () => {
377
378
  const privateConv = createConversation({ title: 'private-conv', threadType: 'private' });
378
379
  const base64Content = Buffer.from('private content').toString('base64');
379
- const attachment = uploadAttachment('ast-1', 'confidential.pdf', 'application/pdf', base64Content);
380
+ const attachment = uploadAttachment('confidential.pdf', 'application/pdf', base64Content);
380
381
  const msg = addMessage(privateConv.id, 'user', 'private message');
381
382
  linkAttachmentToMessage(msg.id, attachment.id, 0);
382
383
 
@@ -402,7 +403,7 @@ describe('AssetMaterializeTool visibility policy', () => {
402
403
  test('materializing from a different private thread is REJECTED', async () => {
403
404
  const privateConv1 = createConversation({ title: 'private-conv-1', threadType: 'private' });
404
405
  const base64Content = Buffer.from('private content').toString('base64');
405
- const attachment = uploadAttachment('ast-1', 'secret.txt', 'text/plain', base64Content);
406
+ const attachment = uploadAttachment('secret.txt', 'text/plain', base64Content);
406
407
  const msg = addMessage(privateConv1.id, 'user', 'private message');
407
408
  linkAttachmentToMessage(msg.id, attachment.id, 0);
408
409
 
@@ -426,7 +427,7 @@ describe('AssetMaterializeTool visibility policy', () => {
426
427
  const privateConv = createConversation({ title: 'private-conv', threadType: 'private' });
427
428
  const standardConv = createConversation({ title: 'standard-conv' });
428
429
  const base64Content = Buffer.from('shared content').toString('base64');
429
- const attachment = uploadAttachment('ast-1', 'shared.txt', 'text/plain', base64Content);
430
+ const attachment = uploadAttachment('shared.txt', 'text/plain', base64Content);
430
431
 
431
432
  const msg1 = addMessage(privateConv.id, 'user', 'private message');
432
433
  const msg2 = addMessage(standardConv.id, 'user', 'standard message');