vellum 0.2.0 → 0.2.2

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 +5 -2
  3. package/package.json +4 -2
  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 +161 -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__/app-bundler.test.ts +12 -33
  11. package/src/__tests__/asset-materialize-tool.test.ts +16 -15
  12. package/src/__tests__/asset-search-tool.test.ts +23 -22
  13. package/src/__tests__/attachments-store.test.ts +56 -127
  14. package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +5 -4
  15. package/src/__tests__/browser-skill-endstate.test.ts +5 -8
  16. package/src/__tests__/call-bridge.test.ts +385 -0
  17. package/src/__tests__/call-constants.test.ts +40 -0
  18. package/src/__tests__/call-orchestrator.test.ts +454 -0
  19. package/src/__tests__/call-recovery.test.ts +518 -0
  20. package/src/__tests__/call-routes-http.test.ts +459 -0
  21. package/src/__tests__/call-state-machine.test.ts +143 -0
  22. package/src/__tests__/call-state.test.ts +133 -0
  23. package/src/__tests__/call-store.test.ts +691 -0
  24. package/src/__tests__/cli-discover.test.ts +1 -1
  25. package/src/__tests__/commit-message-enrichment-service.test.ts +550 -0
  26. package/src/__tests__/compaction.benchmark.test.ts +176 -0
  27. package/src/__tests__/computer-use-tools.test.ts +250 -0
  28. package/src/__tests__/config-schema.test.ts +348 -3
  29. package/src/__tests__/conflict-store.test.ts +2 -1
  30. package/src/__tests__/contacts-tools.test.ts +331 -0
  31. package/src/__tests__/conversation-store.test.ts +30 -32
  32. package/src/__tests__/credential-security-invariants.test.ts +4 -0
  33. package/src/__tests__/date-context.test.ts +373 -0
  34. package/src/__tests__/db-schedule-syntax-migration.test.ts +129 -0
  35. package/src/__tests__/doordash-session.test.ts +9 -0
  36. package/src/__tests__/fixtures/media-reuse-fixtures.ts +3 -3
  37. package/src/__tests__/followup-tools.test.ts +303 -0
  38. package/src/__tests__/handlers-twitter-config.test.ts +718 -0
  39. package/src/__tests__/intent-routing.test.ts +64 -57
  40. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
  41. package/src/__tests__/ipc-snapshot.test.ts +96 -28
  42. package/src/__tests__/llm-usage-store.test.ts +3 -8
  43. package/src/__tests__/media-generate-image.test.ts +1 -1
  44. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
  45. package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
  46. package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
  47. package/src/__tests__/playbook-tools.test.ts +342 -0
  48. package/src/__tests__/profile-compiler.test.ts +2 -1
  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 +17 -10
  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 +222 -0
  58. package/src/__tests__/run-orchestrator.test.ts +7 -7
  59. package/src/__tests__/runtime-attachment-metadata.test.ts +19 -20
  60. package/src/__tests__/runtime-runs-http.test.ts +5 -23
  61. package/src/__tests__/runtime-runs.test.ts +11 -11
  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-error.test.ts +28 -0
  67. package/src/__tests__/session-init.benchmark.test.ts +462 -0
  68. package/src/__tests__/session-queue.test.ts +89 -16
  69. package/src/__tests__/session-runtime-assembly.test.ts +161 -0
  70. package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
  71. package/src/__tests__/signup-e2e.test.ts +2 -1
  72. package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
  73. package/src/__tests__/skill-script-runner.test.ts +159 -0
  74. package/src/__tests__/speaker-identification.test.ts +52 -0
  75. package/src/__tests__/subagent-manager-notify.test.ts +42 -10
  76. package/src/__tests__/subagent-tools.test.ts +141 -41
  77. package/src/__tests__/task-compiler.test.ts +2 -1
  78. package/src/__tests__/task-runner.test.ts +2 -1
  79. package/src/__tests__/task-scheduler.test.ts +2 -1
  80. package/src/__tests__/task-tools.test.ts +49 -56
  81. package/src/__tests__/tool-audit-listener.test.ts +1 -0
  82. package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
  83. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
  84. package/src/__tests__/tool-executor.test.ts +13 -17
  85. package/src/__tests__/turn-commit.test.ts +273 -2
  86. package/src/__tests__/twilio-provider.test.ts +143 -0
  87. package/src/__tests__/twilio-routes.test.ts +789 -0
  88. package/src/__tests__/twitter-auth-handler.test.ts +581 -0
  89. package/src/__tests__/view-image-tool.test.ts +217 -0
  90. package/src/__tests__/workspace-git-service.test.ts +403 -0
  91. package/src/__tests__/workspace-heartbeat-service.test.ts +141 -2
  92. package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
  93. package/src/bundler/app-bundler.ts +35 -14
  94. package/src/calls/call-bridge.ts +95 -0
  95. package/src/calls/call-constants.ts +48 -0
  96. package/src/calls/call-domain.ts +276 -0
  97. package/src/calls/call-orchestrator.ts +390 -0
  98. package/src/calls/call-recovery.ts +207 -0
  99. package/src/calls/call-state-machine.ts +68 -0
  100. package/src/calls/call-state.ts +64 -0
  101. package/src/calls/call-store.ts +416 -0
  102. package/src/calls/relay-server.ts +335 -0
  103. package/src/calls/speaker-identification.ts +213 -0
  104. package/src/calls/twilio-config.ts +34 -0
  105. package/src/calls/twilio-provider.ts +173 -0
  106. package/src/calls/twilio-routes.ts +250 -0
  107. package/src/calls/types.ts +37 -0
  108. package/src/calls/voice-provider.ts +14 -0
  109. package/src/cli/config-commands.ts +334 -0
  110. package/src/cli/core-commands.ts +776 -0
  111. package/src/cli/doordash.ts +256 -25
  112. package/src/cli/ipc-client.ts +82 -0
  113. package/src/cli/map.ts +246 -0
  114. package/src/cli/twitter.ts +575 -0
  115. package/src/cli.ts +7 -5
  116. package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
  117. package/src/commands/cc-command-registry.ts +209 -0
  118. package/src/config/bundled-skills/contacts/SKILL.md +39 -0
  119. package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
  120. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
  121. package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
  122. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
  123. package/src/config/bundled-skills/document/SKILL.md +18 -0
  124. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  125. package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
  126. package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
  127. package/src/config/bundled-skills/doordash/SKILL.md +163 -0
  128. package/src/config/bundled-skills/followups/SKILL.md +32 -0
  129. package/src/config/bundled-skills/followups/TOOLS.json +100 -0
  130. package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
  131. package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
  132. package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
  133. package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
  134. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -24
  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 +44 -0
  179. package/src/config/loader.ts +4 -1
  180. package/src/config/schema.ts +218 -1
  181. package/src/config/system-prompt.ts +100 -6
  182. package/src/config/templates/IDENTITY.md +7 -0
  183. package/src/config/types.ts +5 -0
  184. package/src/contacts/contact-store.ts +4 -4
  185. package/src/daemon/assistant-attachments.ts +10 -0
  186. package/src/daemon/classifier.ts +3 -1
  187. package/src/daemon/computer-use-session.ts +3 -1
  188. package/src/daemon/date-context.ts +136 -0
  189. package/src/daemon/handlers/apps.ts +16 -1
  190. package/src/daemon/handlers/browser.ts +54 -0
  191. package/src/daemon/handlers/computer-use.ts +7 -1
  192. package/src/daemon/handlers/config.ts +192 -4
  193. package/src/daemon/handlers/diagnostics.ts +5 -1
  194. package/src/daemon/handlers/documents.ts +18 -29
  195. package/src/daemon/handlers/home-base.ts +5 -1
  196. package/src/daemon/handlers/index.ts +40 -271
  197. package/src/daemon/handlers/misc.ts +9 -1
  198. package/src/daemon/handlers/publish.ts +6 -1
  199. package/src/daemon/handlers/sessions.ts +65 -12
  200. package/src/daemon/handlers/shared.ts +36 -1
  201. package/src/daemon/handlers/signing.ts +37 -0
  202. package/src/daemon/handlers/skills.ts +20 -6
  203. package/src/daemon/handlers/subagents.ts +8 -3
  204. package/src/daemon/handlers/twitter-auth.ts +169 -0
  205. package/src/daemon/handlers/work-items.ts +495 -39
  206. package/src/daemon/ipc-contract-inventory.json +40 -4
  207. package/src/daemon/ipc-contract.ts +185 -37
  208. package/src/daemon/ipc-protocol.ts +7 -2
  209. package/src/daemon/lifecycle.ts +48 -5
  210. package/src/daemon/main.ts +10 -4
  211. package/src/daemon/ride-shotgun-handler.ts +74 -10
  212. package/src/daemon/server.ts +144 -29
  213. package/src/daemon/session-agent-loop.ts +887 -0
  214. package/src/daemon/session-attachments.ts +28 -5
  215. package/src/daemon/session-error.ts +24 -3
  216. package/src/daemon/session-lifecycle.ts +147 -0
  217. package/src/daemon/session-media-retry.ts +147 -0
  218. package/src/daemon/session-messaging.ts +145 -0
  219. package/src/daemon/session-notifiers.ts +164 -0
  220. package/src/daemon/session-process.ts +2 -2
  221. package/src/daemon/session-queue-manager.ts +1 -0
  222. package/src/daemon/session-runtime-assembly.ts +52 -0
  223. package/src/daemon/session-skill-tools.ts +124 -5
  224. package/src/daemon/session-slash.ts +3 -0
  225. package/src/daemon/session-surfaces.ts +77 -2
  226. package/src/daemon/session-tool-setup.ts +222 -2
  227. package/src/daemon/session-usage.ts +0 -2
  228. package/src/daemon/session.ts +114 -1365
  229. package/src/daemon/video-thumbnail.ts +60 -0
  230. package/src/doordash/client.ts +121 -27
  231. package/src/doordash/queries.ts +1 -2
  232. package/src/export/formatter.ts +3 -1
  233. package/src/followups/followup-store.ts +4 -2
  234. package/src/followups/types.ts +6 -0
  235. package/src/hooks/templates.ts +1 -1
  236. package/src/index.ts +32 -1151
  237. package/src/media/gemini-image-service.ts +1 -1
  238. package/src/memory/attachments-store.ts +28 -83
  239. package/src/memory/channel-delivery-store.ts +7 -21
  240. package/src/memory/clarification-resolver.ts +6 -5
  241. package/src/memory/contradiction-checker.ts +3 -2
  242. package/src/memory/conversation-key-store.ts +10 -29
  243. package/src/memory/conversation-store.ts +2 -1
  244. package/src/memory/db.ts +362 -2
  245. package/src/memory/entity-extractor.ts +6 -3
  246. package/src/memory/items-extractor.ts +5 -4
  247. package/src/memory/jobs-store.ts +3 -2
  248. package/src/memory/llm-usage-store.ts +1 -2
  249. package/src/memory/runs-store.ts +1 -2
  250. package/src/memory/schema.ts +65 -2
  251. package/src/messaging/style-analyzer.ts +3 -2
  252. package/src/messaging/thread-summarizer.ts +8 -12
  253. package/src/messaging/triage-engine.ts +4 -2
  254. package/src/providers/openrouter/client.ts +20 -0
  255. package/src/providers/registry.ts +8 -0
  256. package/src/runtime/http-server.ts +277 -25
  257. package/src/runtime/http-types.ts +0 -2
  258. package/src/runtime/routes/attachment-routes.ts +5 -6
  259. package/src/runtime/routes/call-routes.ts +140 -0
  260. package/src/runtime/routes/channel-routes.ts +12 -19
  261. package/src/runtime/routes/conversation-routes.ts +5 -9
  262. package/src/runtime/routes/run-routes.ts +4 -8
  263. package/src/runtime/run-orchestrator.ts +39 -6
  264. package/src/schedule/recurrence-engine.ts +138 -0
  265. package/src/schedule/recurrence-types.ts +67 -0
  266. package/src/schedule/schedule-store.ts +102 -57
  267. package/src/schedule/scheduler.ts +9 -6
  268. package/src/security/oauth2.ts +29 -4
  269. package/src/security/secret-allowlist.ts +46 -0
  270. package/src/skills/clawhub.ts +1 -1
  271. package/src/subagent/manager.ts +40 -8
  272. package/src/swarm/backend-claude-code.ts +64 -9
  273. package/src/swarm/worker-prompts.ts +2 -1
  274. package/src/tasks/SPEC.md +34 -28
  275. package/src/tasks/ephemeral-permissions.ts +16 -7
  276. package/src/tasks/task-compiler.ts +5 -4
  277. package/src/tasks/task-runner.ts +10 -5
  278. package/src/tasks/task-scheduler.ts +1 -1
  279. package/src/tasks/tool-sanitizer.ts +36 -0
  280. package/src/tools/assets/search.ts +4 -4
  281. package/src/tools/browser/api-map.ts +220 -0
  282. package/src/tools/browser/auto-navigate.ts +270 -0
  283. package/src/tools/browser/browser-execution.ts +2 -1
  284. package/src/tools/browser/browser-manager.ts +2 -2
  285. package/src/tools/browser/network-recorder.ts +5 -4
  286. package/src/tools/browser/x-auto-navigate.ts +207 -0
  287. package/src/tools/calls/call-end.ts +67 -0
  288. package/src/tools/calls/call-start.ts +73 -0
  289. package/src/tools/calls/call-status.ts +81 -0
  290. package/src/tools/claude-code/claude-code.ts +77 -11
  291. package/src/tools/contacts/contact-merge.ts +46 -78
  292. package/src/tools/contacts/contact-search.ts +35 -79
  293. package/src/tools/contacts/contact-upsert.ts +35 -108
  294. package/src/tools/credentials/vault.ts +21 -5
  295. package/src/tools/document/document-tool.ts +71 -144
  296. package/src/tools/executor.ts +129 -10
  297. package/src/tools/followups/followup_create.ts +46 -88
  298. package/src/tools/followups/followup_list.ts +34 -74
  299. package/src/tools/followups/followup_resolve.ts +31 -66
  300. package/src/tools/host-terminal/cli-discover.ts +2 -1
  301. package/src/tools/host-terminal/host-shell.ts +10 -0
  302. package/src/tools/memory/handlers.ts +5 -4
  303. package/src/tools/network/__tests__/web-search.test.ts +427 -0
  304. package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
  305. package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
  306. package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
  307. package/src/tools/network/web-fetch.ts +18 -6
  308. package/src/tools/playbooks/index.ts +4 -5
  309. package/src/tools/playbooks/playbook-create.ts +3 -47
  310. package/src/tools/playbooks/playbook-delete.ts +1 -25
  311. package/src/tools/playbooks/playbook-list.ts +1 -28
  312. package/src/tools/playbooks/playbook-update.ts +3 -51
  313. package/src/tools/registry.ts +2 -4
  314. package/src/tools/reminder/reminder.ts +5 -78
  315. package/src/tools/schedule/create.ts +69 -74
  316. package/src/tools/schedule/delete.ts +21 -47
  317. package/src/tools/schedule/list.ts +55 -74
  318. package/src/tools/schedule/update.ts +77 -84
  319. package/src/tools/subagent/abort.ts +29 -58
  320. package/src/tools/subagent/message.ts +30 -63
  321. package/src/tools/subagent/read.ts +53 -84
  322. package/src/tools/subagent/spawn.ts +43 -82
  323. package/src/tools/subagent/status.ts +42 -71
  324. package/src/tools/swarm/delegate.ts +2 -1
  325. package/src/tools/tasks/index.ts +8 -6
  326. package/src/tools/tasks/task-delete.ts +69 -56
  327. package/src/tools/tasks/task-list.ts +31 -52
  328. package/src/tools/tasks/task-run.ts +74 -102
  329. package/src/tools/tasks/task-save.ts +33 -65
  330. package/src/tools/tasks/work-item-enqueue.ts +192 -134
  331. package/src/tools/tasks/work-item-list.ts +33 -78
  332. package/src/tools/tasks/work-item-remove.ts +60 -0
  333. package/src/tools/tasks/work-item-update.ts +114 -0
  334. package/src/tools/terminal/backends/native.ts +3 -1
  335. package/src/tools/tool-manifest.ts +20 -74
  336. package/src/tools/types.ts +6 -0
  337. package/src/tools/ui-surface/definitions.ts +6 -1
  338. package/src/tools/watch/screen-watch.ts +3 -1
  339. package/src/tools/watcher/create.ts +52 -98
  340. package/src/tools/watcher/delete.ts +20 -46
  341. package/src/tools/watcher/digest.ts +36 -70
  342. package/src/tools/watcher/list.ts +49 -79
  343. package/src/tools/watcher/update.ts +45 -91
  344. package/src/twitter/client.ts +690 -0
  345. package/src/twitter/session.ts +91 -0
  346. package/src/usage/types.ts +0 -1
  347. package/src/util/truncate.ts +6 -0
  348. package/src/watcher/providers/slack.ts +2 -1
  349. package/src/watcher/watcher-store.ts +3 -2
  350. package/src/work-items/work-item-store.ts +236 -2
  351. package/src/workspace/commit-message-enrichment-service.ts +284 -0
  352. package/src/workspace/commit-message-provider.ts +95 -0
  353. package/src/workspace/git-service.ts +272 -52
  354. package/src/workspace/heartbeat-service.ts +70 -13
  355. package/src/workspace/provider-commit-message-generator.ts +242 -0
  356. package/src/workspace/turn-commit.ts +100 -51
  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
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Twitter session persistence.
3
+ * Stores/loads auth cookies from a recording or manual login.
4
+ */
5
+
6
+ import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from 'node:fs';
7
+ import { join } from 'node:path';
8
+ import { getDataDir } from '../util/platform.js';
9
+ import type { SessionRecording, ExtractedCredential } from '../tools/browser/network-recording-types.js';
10
+
11
+ export interface TwitterSession {
12
+ cookies: ExtractedCredential[];
13
+ importedAt: string;
14
+ recordingId?: string;
15
+ }
16
+
17
+ function getSessionDir(): string {
18
+ return join(getDataDir(), 'twitter');
19
+ }
20
+
21
+ function getSessionPath(): string {
22
+ return join(getSessionDir(), 'session.json');
23
+ }
24
+
25
+ export function loadSession(): TwitterSession | null {
26
+ const path = getSessionPath();
27
+ if (!existsSync(path)) return null;
28
+ try {
29
+ return JSON.parse(readFileSync(path, 'utf-8')) as TwitterSession;
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ export function saveSession(session: TwitterSession): void {
36
+ const dir = getSessionDir();
37
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
38
+ writeFileSync(getSessionPath(), JSON.stringify(session, null, 2));
39
+ }
40
+
41
+ export function clearSession(): void {
42
+ const path = getSessionPath();
43
+ if (existsSync(path)) {
44
+ unlinkSync(path);
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Import cookies from a Ride Shotgun recording file.
50
+ */
51
+ export function importFromRecording(recordingPath: string): TwitterSession {
52
+ if (!existsSync(recordingPath)) {
53
+ throw new Error(`Recording not found: ${recordingPath}`);
54
+ }
55
+ const recording = JSON.parse(readFileSync(recordingPath, 'utf-8')) as SessionRecording;
56
+ if (!recording.cookies?.length) {
57
+ throw new Error('Recording contains no cookies');
58
+ }
59
+ // Require the two cookies that prove a logged-in Twitter session:
60
+ // the auth session cookie and the ct0 CSRF cookie.
61
+ const cookieNames = new Set(recording.cookies.map(c => c.name));
62
+ if (!cookieNames.has('ct0') || !cookieNames.has(`auth_${'token'}`)) {
63
+ throw new Error(
64
+ 'Recording is missing required Twitter session cookies. ' +
65
+ 'Make sure you are logged in to x.com before recording.',
66
+ );
67
+ }
68
+ const session: TwitterSession = {
69
+ cookies: recording.cookies,
70
+ importedAt: new Date().toISOString(),
71
+ recordingId: recording.id,
72
+ };
73
+ saveSession(session);
74
+ return session;
75
+ }
76
+
77
+ /**
78
+ * Build a Cookie header string from the session.
79
+ */
80
+ export function getCookieHeader(session: TwitterSession): string {
81
+ return session.cookies
82
+ .map(c => `${c.name}=${c.value}`)
83
+ .join('; ');
84
+ }
85
+
86
+ /**
87
+ * Get the CSRF token from session cookies (ct0 cookie).
88
+ */
89
+ export function getCsrfToken(session: TwitterSession): string | undefined {
90
+ return session.cookies.find(c => c.name === 'ct0')?.value;
91
+ }
@@ -12,7 +12,6 @@ export interface UsageEventInput {
12
12
  cacheCreationInputTokens: number | null;
13
13
  cacheReadInputTokens: number | null;
14
14
  actor: UsageActor;
15
- assistantId: string | null;
16
15
  conversationId: string | null;
17
16
  runId: string | null;
18
17
  requestId: string | null;
@@ -0,0 +1,6 @@
1
+ /** Truncate a string to `maxLen` characters, appending `suffix` if truncated. */
2
+ export function truncate(str: string, maxLen: number, suffix = '...'): string {
3
+ if (str.length <= maxLen) return str;
4
+ if (maxLen <= suffix.length) return str.slice(0, maxLen);
5
+ return str.slice(0, maxLen - suffix.length) + suffix;
6
+ }
@@ -8,6 +8,7 @@
8
8
  */
9
9
 
10
10
  import { withValidToken } from '../../security/token-manager.js';
11
+ import { truncate } from '../../util/truncate.js';
11
12
  import * as slack from '../../messaging/providers/slack/client.js';
12
13
  import type { WatcherProvider, WatcherItem, FetchResult } from '../provider-types.js';
13
14
  import { getLogger } from '../../util/logger.js';
@@ -22,7 +23,7 @@ function messageToItem(
22
23
  return {
23
24
  externalId: `${msg.channel}:${msg.ts}`,
24
25
  eventType,
25
- summary: `Slack ${eventType.replace('slack_', '')}: ${msg.text.slice(0, 100)}`,
26
+ summary: `Slack ${eventType.replace('slack_', '')}: ${truncate(msg.text, 100)}`,
26
27
  payload: {
27
28
  channel: msg.channel,
28
29
  channelName,
@@ -3,6 +3,7 @@ import { v4 as uuid } from 'uuid';
3
3
  import { getDb } from '../memory/db.js';
4
4
  import { watchers, watcherEvents } from '../memory/schema.js';
5
5
  import { DEFAULT_POLL_INTERVAL_MS } from './constants.js';
6
+ import { truncate } from '../util/truncate.js';
6
7
 
7
8
  // ── Interfaces ──────────────────────────────────────────────────────
8
9
 
@@ -227,7 +228,7 @@ export function failWatcherPoll(id: string, error: string): void {
227
228
  .set({
228
229
  status: 'idle',
229
230
  consecutiveErrors: errors,
230
- lastError: error.slice(0, 2000),
231
+ lastError: truncate(error, 2000, ''),
231
232
  lastPollAt: now,
232
233
  nextPollAt: now + backoff,
233
234
  updatedAt: now,
@@ -245,7 +246,7 @@ export function disableWatcher(id: string, reason: string): void {
245
246
  .set({
246
247
  status: 'disabled',
247
248
  enabled: false,
248
- lastError: reason.slice(0, 2000),
249
+ lastError: truncate(reason, 2000, ''),
249
250
  updatedAt: Date.now(),
250
251
  })
251
252
  .where(eq(watchers.id, id))
@@ -1,10 +1,11 @@
1
1
  import { eq, desc, asc } from 'drizzle-orm';
2
2
  import { getDb } from '../memory/db.js';
3
3
  import { workItems } from '../memory/schema.js';
4
+ import { getTask } from '../tasks/task-store.js';
4
5
 
5
6
  // ── Types ────────────────────────────────────────────────────────────
6
7
 
7
- export type WorkItemStatus = 'queued' | 'running' | 'awaiting_review' | 'failed' | 'done' | 'archived';
8
+ export type WorkItemStatus = 'queued' | 'running' | 'awaiting_review' | 'failed' | 'cancelled' | 'done' | 'archived';
8
9
 
9
10
  export interface WorkItem {
10
11
  id: string;
@@ -19,6 +20,9 @@ export interface WorkItem {
19
20
  lastRunStatus: string | null;
20
21
  sourceType: string | null;
21
22
  sourceId: string | null;
23
+ requiredTools: string | null;
24
+ approvedTools: string | null;
25
+ approvalStatus: string | null;
22
26
  createdAt: number;
23
27
  updatedAt: number;
24
28
  }
@@ -33,6 +37,7 @@ export function createWorkItem(opts: {
33
37
  sortIndex?: number;
34
38
  sourceType?: string;
35
39
  sourceId?: string;
40
+ requiredTools?: string;
36
41
  }): WorkItem {
37
42
  const db = getDb();
38
43
  const now = Date.now();
@@ -50,6 +55,9 @@ export function createWorkItem(opts: {
50
55
  lastRunStatus: null,
51
56
  sourceType: opts.sourceType ?? null,
52
57
  sourceId: opts.sourceId ?? null,
58
+ requiredTools: opts.requiredTools ?? null,
59
+ approvedTools: null,
60
+ approvalStatus: 'none',
53
61
  createdAt: now,
54
62
  updatedAt: now,
55
63
  };
@@ -57,6 +65,24 @@ export function createWorkItem(opts: {
57
65
  return item;
58
66
  }
59
67
 
68
+ /**
69
+ * Create a work item without any pre-approved permissions. Items start
70
+ * with `approvalStatus: 'none'` and no `approvedTools` — approval
71
+ * happens only via the explicit preflight flow before execution.
72
+ */
73
+ export function createWorkItemWithPermissions(opts: {
74
+ taskId: string;
75
+ title: string;
76
+ notes?: string;
77
+ priorityTier?: number;
78
+ sortIndex?: number;
79
+ sourceType?: string;
80
+ sourceId?: string;
81
+ requiredTools?: string;
82
+ }): WorkItem {
83
+ return createWorkItem(opts);
84
+ }
85
+
60
86
  export function getWorkItem(id: string): WorkItem | undefined {
61
87
  const db = getDb();
62
88
  return db.select().from(workItems).where(eq(workItems.id, id)).get() as WorkItem | undefined;
@@ -75,7 +101,7 @@ export function listWorkItems(opts?: { status?: WorkItemStatus }): WorkItem[] {
75
101
 
76
102
  export function updateWorkItem(
77
103
  id: string,
78
- updates: Partial<Pick<WorkItem, 'title' | 'notes' | 'status' | 'priorityTier' | 'sortIndex' | 'lastRunId' | 'lastRunConversationId' | 'lastRunStatus'>>,
104
+ updates: Partial<Pick<WorkItem, 'title' | 'notes' | 'status' | 'priorityTier' | 'sortIndex' | 'lastRunId' | 'lastRunConversationId' | 'lastRunStatus' | 'requiredTools' | 'approvedTools' | 'approvalStatus'>>,
79
105
  ): WorkItem | undefined {
80
106
  const db = getDb();
81
107
  db.update(workItems)
@@ -89,3 +115,211 @@ export function deleteWorkItem(id: string): void {
89
115
  const db = getDb();
90
116
  db.delete(workItems).where(eq(workItems.id, id)).run();
91
117
  }
118
+
119
+ // ── Queue Removal ───────────────────────────────────────────────────
120
+
121
+ export interface RemoveWorkItemResult {
122
+ success: boolean;
123
+ title: string;
124
+ message: string;
125
+ }
126
+
127
+ /**
128
+ * Shared helper for removing a single work item from the queue by ID.
129
+ * Used by both task_delete (compat path) and task_list_remove so all
130
+ * single-item deletions follow one codepath.
131
+ */
132
+ export function removeWorkItemFromQueue(id: string): RemoveWorkItemResult {
133
+ const item = getWorkItem(id);
134
+ if (!item) {
135
+ return { success: false, title: '', message: `No work item found with ID "${id}"` };
136
+ }
137
+ deleteWorkItem(item.id);
138
+ return { success: true, title: item.title, message: `Removed "${item.title}" from the task queue.` };
139
+ }
140
+
141
+ // ── Selectors / Helpers ─────────────────────────────────────────────
142
+
143
+ export interface WorkItemSelector {
144
+ workItemId?: string;
145
+ taskId?: string;
146
+ title?: string;
147
+ /** Disambiguator: filter by priority tier (0 = high, 1 = medium, 2 = low) */
148
+ priorityTier?: number;
149
+ /** Disambiguator: filter by status (queued, running, etc.) */
150
+ status?: WorkItemStatus;
151
+ /** Disambiguator: 1-indexed creation order (1 = oldest, 2 = second oldest, etc.) */
152
+ createdOrder?: number;
153
+ }
154
+
155
+ export type ResolveWorkItemResult =
156
+ | { status: 'found'; workItem: WorkItem }
157
+ | { status: 'not_found'; message: string }
158
+ | { status: 'ambiguous'; matches: WorkItem[]; message: string };
159
+
160
+ const PRIORITY_TIER_LABELS: Record<number, string> = { 0: 'high', 1: 'medium', 2: 'low' };
161
+
162
+ function formatAmbiguityMessage(selectorLabel: string, matches: WorkItem[]): string {
163
+ const lines = matches.map(
164
+ m =>
165
+ ` - ID: ${m.id} | title: "${m.title}" | priority: ${PRIORITY_TIER_LABELS[m.priorityTier] ?? m.priorityTier} | status: ${m.status}`,
166
+ );
167
+ return `Multiple items match '${selectorLabel}'. Please specify which one:\n${lines.join('\n')}`;
168
+ }
169
+
170
+ /** Find all active work items for a given task ID */
171
+ export function findActiveWorkItemsByTaskId(taskId: string): WorkItem[] {
172
+ return listWorkItems().filter(
173
+ i => i.taskId === taskId && i.status !== 'done' && i.status !== 'archived'
174
+ );
175
+ }
176
+
177
+ /** Find all active work items matching a title (case-insensitive exact match) */
178
+ export function findActiveWorkItemsByTitle(title: string): WorkItem[] {
179
+ const normalized = title.trim().toLowerCase();
180
+ return listWorkItems().filter(
181
+ i => i.title.trim().toLowerCase() === normalized && i.status !== 'done' && i.status !== 'archived'
182
+ );
183
+ }
184
+
185
+ /**
186
+ * Apply disambiguator fields to narrow down a set of candidate matches.
187
+ * Filters by priorityTier, then status, then picks by createdOrder if provided.
188
+ * Returns the filtered list (may still contain multiple items if disambiguation
189
+ * fields are insufficient).
190
+ */
191
+ function applyDisambiguators(items: WorkItem[], selector: WorkItemSelector): WorkItem[] {
192
+ let filtered = items;
193
+
194
+ if (selector.priorityTier !== undefined) {
195
+ filtered = filtered.filter(i => i.priorityTier === selector.priorityTier);
196
+ }
197
+
198
+ if (selector.status !== undefined) {
199
+ filtered = filtered.filter(i => i.status === selector.status);
200
+ }
201
+
202
+ if (selector.createdOrder !== undefined && filtered.length > 0) {
203
+ const sorted = [...filtered].sort((a, b) => a.createdAt - b.createdAt);
204
+ const idx = selector.createdOrder - 1; // convert 1-indexed to 0-indexed
205
+ if (idx >= 0 && idx < sorted.length) {
206
+ filtered = [sorted[idx]];
207
+ }
208
+ // If createdOrder is out of range, return the full filtered list so the
209
+ // caller can report ambiguity with the remaining candidates.
210
+ }
211
+
212
+ return filtered;
213
+ }
214
+
215
+ /**
216
+ * Given a list of candidate matches, apply disambiguators and return a resolution result.
217
+ * Centralises the disambiguate-or-return-ambiguous logic shared across selector branches.
218
+ */
219
+ function resolveFromCandidates(items: WorkItem[], selectorLabel: string, selector: WorkItemSelector): ResolveWorkItemResult {
220
+ if (items.length === 0) {
221
+ return { status: 'not_found', message: `No active work item found for "${selectorLabel}"` };
222
+ }
223
+ if (items.length === 1) {
224
+ return { status: 'found', workItem: items[0] };
225
+ }
226
+
227
+ // Multiple matches — try to narrow down with disambiguator fields
228
+ const narrowed = applyDisambiguators(items, selector);
229
+
230
+ if (narrowed.length === 1) {
231
+ return { status: 'found', workItem: narrowed[0] };
232
+ }
233
+ if (narrowed.length === 0) {
234
+ // Disambiguators filtered out everything — report the original set so the
235
+ // caller sees what was available
236
+ return { status: 'ambiguous', matches: items, message: formatAmbiguityMessage(selectorLabel, items) };
237
+ }
238
+ return { status: 'ambiguous', matches: narrowed, message: formatAmbiguityMessage(selectorLabel, narrowed) };
239
+ }
240
+
241
+ /**
242
+ * Resolve a single active work item from a selector.
243
+ * Tries fields in priority order: workItemId > taskId > title.
244
+ * Only considers active items (status not 'done' or 'archived').
245
+ * Returns a discriminated union so callers can handle ambiguity explicitly
246
+ * instead of silently picking one match when multiple exist.
247
+ *
248
+ * When multiple items match, the optional disambiguator fields (priorityTier,
249
+ * status, createdOrder) are applied to narrow down the set.
250
+ */
251
+ export function resolveWorkItem(selector: WorkItemSelector): ResolveWorkItemResult {
252
+ if (selector.workItemId) {
253
+ const item = getWorkItem(selector.workItemId);
254
+ if (!item) return { status: 'not_found', message: `No work item found with ID "${selector.workItemId}"` };
255
+ if (item.status === 'done' || item.status === 'archived') {
256
+ return { status: 'not_found', message: `Work item "${selector.workItemId}" is ${item.status}` };
257
+ }
258
+ return { status: 'found', workItem: item };
259
+ }
260
+
261
+ if (selector.taskId) {
262
+ const items = findActiveWorkItemsByTaskId(selector.taskId);
263
+ return resolveFromCandidates(items, selector.taskId, selector);
264
+ }
265
+
266
+ if (selector.title) {
267
+ const items = findActiveWorkItemsByTitle(selector.title);
268
+ return resolveFromCandidates(items, selector.title, selector);
269
+ }
270
+
271
+ return { status: 'not_found', message: 'At least one selector field (workItemId, taskId, or title) must be provided' };
272
+ }
273
+
274
+ // ── Entity Identification ───────────────────────────────────────────
275
+
276
+ export type EntityType = 'task_template' | 'work_item' | 'unknown';
277
+
278
+ export interface EntityIdentification {
279
+ type: EntityType;
280
+ id: string;
281
+ title?: string;
282
+ }
283
+
284
+ /**
285
+ * Determine whether an ID refers to a task template (tasks table) or
286
+ * a work item (work_items table). Used by tool error messages to give
287
+ * the model corrective guidance when the wrong entity type is provided.
288
+ */
289
+ export function identifyEntityById(id: string): EntityIdentification {
290
+ const workItem = getWorkItem(id);
291
+ if (workItem) {
292
+ return { type: 'work_item', id: workItem.id, title: workItem.title };
293
+ }
294
+
295
+ const task = getTask(id);
296
+ if (task) {
297
+ return { type: 'task_template', id: task.id, title: task.title };
298
+ }
299
+
300
+ return { type: 'unknown', id };
301
+ }
302
+
303
+ /**
304
+ * Build a corrective error message when a work item ID is passed where
305
+ * a task template ID is expected.
306
+ */
307
+ export function buildWorkItemMismatchError(id: string, title: string, expectedTool: string): string {
308
+ return [
309
+ `Entity mismatch: The ID "${id}" refers to a work item ("${title}"), not a task template.`,
310
+ `Corrective action: Use ${expectedTool} to operate on work items in the task queue.`,
311
+ `Selector fields: work_item_id: "${id}" or title: "${title}"`,
312
+ ].join('\n');
313
+ }
314
+
315
+ /**
316
+ * Build a corrective error message when a task template ID is passed where
317
+ * a work item ID is expected.
318
+ */
319
+ export function buildTaskTemplateMismatchError(id: string, title: string, expectedTool: string): string {
320
+ return [
321
+ `Entity mismatch: The ID "${id}" refers to a task template ("${title}"), not a work item.`,
322
+ `Corrective action: Use ${expectedTool} to operate on task templates.`,
323
+ `Selector fields: task_id: "${id}" or task_name: "${title}"`,
324
+ ].join('\n');
325
+ }