vellum 0.2.1 → 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 (349) 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 +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 +299 -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-twitter-config.test.ts +718 -0
  36. package/src/__tests__/intent-routing.test.ts +64 -57
  37. package/src/__tests__/ipc-roundtrip.benchmark.test.ts +237 -0
  38. package/src/__tests__/ipc-snapshot.test.ts +62 -28
  39. package/src/__tests__/llm-usage-store.test.ts +3 -8
  40. package/src/__tests__/media-generate-image.test.ts +1 -1
  41. package/src/__tests__/media-reuse-story.e2e.test.ts +7 -7
  42. package/src/__tests__/memory-retrieval.benchmark.test.ts +430 -0
  43. package/src/__tests__/parallel-tool.benchmark.test.ts +294 -0
  44. package/src/__tests__/playbook-tools.test.ts +342 -0
  45. package/src/__tests__/profile-compiler.test.ts +2 -1
  46. package/src/__tests__/provider-streaming.benchmark.test.ts +773 -0
  47. package/src/__tests__/recurrence-engine-rruleset.test.ts +78 -0
  48. package/src/__tests__/recurrence-engine.test.ts +69 -0
  49. package/src/__tests__/recurrence-types.test.ts +71 -0
  50. package/src/__tests__/registry.test.ts +5 -3
  51. package/src/__tests__/relay-server.test.ts +633 -0
  52. package/src/__tests__/reminder-store.test.ts +6 -3
  53. package/src/__tests__/reminder.test.ts +43 -77
  54. package/src/__tests__/run-orchestrator-assistant-events.test.ts +8 -4
  55. package/src/__tests__/run-orchestrator.test.ts +4 -4
  56. package/src/__tests__/runtime-attachment-metadata.test.ts +7 -6
  57. package/src/__tests__/runtime-runs-http.test.ts +4 -4
  58. package/src/__tests__/runtime-runs.test.ts +4 -4
  59. package/src/__tests__/schedule-store.test.ts +482 -0
  60. package/src/__tests__/schedule-tools.test.ts +700 -0
  61. package/src/__tests__/scheduler-recurrence.test.ts +329 -0
  62. package/src/__tests__/server-history-render.test.ts +14 -13
  63. package/src/__tests__/session-error.test.ts +28 -0
  64. package/src/__tests__/session-init.benchmark.test.ts +462 -0
  65. package/src/__tests__/session-queue.test.ts +71 -48
  66. package/src/__tests__/session-runtime-assembly.test.ts +161 -0
  67. package/src/__tests__/session-surfaces-task-progress.test.ts +104 -0
  68. package/src/__tests__/signup-e2e.test.ts +2 -1
  69. package/src/__tests__/skill-projection.benchmark.test.ts +328 -0
  70. package/src/__tests__/skill-script-runner.test.ts +159 -0
  71. package/src/__tests__/speaker-identification.test.ts +52 -0
  72. package/src/__tests__/subagent-manager-notify.test.ts +42 -10
  73. package/src/__tests__/subagent-tools.test.ts +141 -41
  74. package/src/__tests__/task-compiler.test.ts +2 -1
  75. package/src/__tests__/task-runner.test.ts +2 -1
  76. package/src/__tests__/task-scheduler.test.ts +2 -1
  77. package/src/__tests__/task-tools.test.ts +49 -56
  78. package/src/__tests__/tool-audit-listener.test.ts +1 -0
  79. package/src/__tests__/tool-domain-event-publisher.test.ts +2 -0
  80. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +500 -0
  81. package/src/__tests__/tool-executor.test.ts +13 -17
  82. package/src/__tests__/turn-commit.test.ts +218 -3
  83. package/src/__tests__/twilio-provider.test.ts +143 -0
  84. package/src/__tests__/twilio-routes.test.ts +789 -0
  85. package/src/__tests__/twitter-auth-handler.test.ts +581 -0
  86. package/src/__tests__/view-image-tool.test.ts +217 -0
  87. package/src/__tests__/workspace-git-service.test.ts +186 -0
  88. package/src/__tests__/workspace-heartbeat-service.test.ts +13 -3
  89. package/src/agent-heartbeat/agent-heartbeat-service.ts +155 -0
  90. package/src/bundler/app-bundler.ts +12 -8
  91. package/src/calls/call-bridge.ts +95 -0
  92. package/src/calls/call-constants.ts +43 -5
  93. package/src/calls/call-domain.ts +276 -0
  94. package/src/calls/call-orchestrator.ts +43 -17
  95. package/src/calls/call-recovery.ts +207 -0
  96. package/src/calls/call-state-machine.ts +68 -0
  97. package/src/calls/call-store.ts +192 -5
  98. package/src/calls/relay-server.ts +41 -4
  99. package/src/calls/speaker-identification.ts +213 -0
  100. package/src/calls/twilio-provider.ts +10 -6
  101. package/src/calls/twilio-routes.ts +90 -76
  102. package/src/calls/types.ts +1 -1
  103. package/src/cli/config-commands.ts +334 -0
  104. package/src/cli/core-commands.ts +776 -0
  105. package/src/cli/doordash.ts +251 -1
  106. package/src/cli/ipc-client.ts +82 -0
  107. package/src/cli/map.ts +246 -0
  108. package/src/cli/twitter.ts +575 -0
  109. package/src/cli.ts +7 -5
  110. package/src/commands/__tests__/cc-command-registry.test.ts +319 -0
  111. package/src/commands/cc-command-registry.ts +209 -0
  112. package/src/config/bundled-skills/contacts/SKILL.md +39 -0
  113. package/src/config/bundled-skills/contacts/TOOLS.json +122 -0
  114. package/src/config/bundled-skills/contacts/tools/contact-merge.ts +9 -0
  115. package/src/config/bundled-skills/contacts/tools/contact-search.ts +9 -0
  116. package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +9 -0
  117. package/src/config/bundled-skills/document/SKILL.md +18 -0
  118. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  119. package/src/config/bundled-skills/document/tools/document-create.ts +9 -0
  120. package/src/config/bundled-skills/document/tools/document-update.ts +9 -0
  121. package/src/config/bundled-skills/doordash/SKILL.md +82 -23
  122. package/src/config/bundled-skills/followups/SKILL.md +32 -0
  123. package/src/config/bundled-skills/followups/TOOLS.json +100 -0
  124. package/src/config/bundled-skills/followups/tools/followup-create.ts +9 -0
  125. package/src/config/bundled-skills/followups/tools/followup-list.ts +9 -0
  126. package/src/config/bundled-skills/followups/tools/followup-resolve.ts +9 -0
  127. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +1 -23
  128. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -1
  129. package/src/config/bundled-skills/playbooks/SKILL.md +31 -0
  130. package/src/config/bundled-skills/playbooks/TOOLS.json +126 -0
  131. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +9 -0
  132. package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +9 -0
  133. package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +9 -0
  134. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +9 -0
  135. package/src/config/bundled-skills/reminder/SKILL.md +20 -0
  136. package/src/config/bundled-skills/reminder/TOOLS.json +67 -0
  137. package/src/config/bundled-skills/reminder/tools/reminder-cancel.ts +9 -0
  138. package/src/config/bundled-skills/reminder/tools/reminder-create.ts +9 -0
  139. package/src/config/bundled-skills/reminder/tools/reminder-list.ts +9 -0
  140. package/src/config/bundled-skills/schedule/SKILL.md +74 -0
  141. package/src/config/bundled-skills/schedule/TOOLS.json +135 -0
  142. package/src/config/bundled-skills/schedule/tools/schedule-create.ts +9 -0
  143. package/src/config/bundled-skills/schedule/tools/schedule-delete.ts +9 -0
  144. package/src/config/bundled-skills/schedule/tools/schedule-list.ts +9 -0
  145. package/src/config/bundled-skills/schedule/tools/schedule-update.ts +9 -0
  146. package/src/config/bundled-skills/subagent/SKILL.md +25 -0
  147. package/src/config/bundled-skills/subagent/TOOLS.json +107 -0
  148. package/src/config/bundled-skills/subagent/tools/subagent-abort.ts +9 -0
  149. package/src/config/bundled-skills/subagent/tools/subagent-message.ts +9 -0
  150. package/src/config/bundled-skills/subagent/tools/subagent-read.ts +9 -0
  151. package/src/config/bundled-skills/subagent/tools/subagent-spawn.ts +9 -0
  152. package/src/config/bundled-skills/subagent/tools/subagent-status.ts +9 -0
  153. package/src/config/bundled-skills/tasks/SKILL.md +28 -0
  154. package/src/config/bundled-skills/tasks/TOOLS.json +256 -0
  155. package/src/config/bundled-skills/tasks/tools/task-delete.ts +9 -0
  156. package/src/config/bundled-skills/tasks/tools/task-list-add.ts +9 -0
  157. package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +9 -0
  158. package/src/config/bundled-skills/tasks/tools/task-list-show.ts +9 -0
  159. package/src/config/bundled-skills/tasks/tools/task-list-update.ts +9 -0
  160. package/src/config/bundled-skills/tasks/tools/task-list.ts +9 -0
  161. package/src/config/bundled-skills/tasks/tools/task-run.ts +9 -0
  162. package/src/config/bundled-skills/tasks/tools/task-save.ts +9 -0
  163. package/src/config/bundled-skills/twitter/SKILL.md +134 -0
  164. package/src/config/bundled-skills/watcher/SKILL.md +27 -0
  165. package/src/config/bundled-skills/watcher/TOOLS.json +147 -0
  166. package/src/config/bundled-skills/watcher/tools/watcher-create.ts +9 -0
  167. package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +9 -0
  168. package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +9 -0
  169. package/src/config/bundled-skills/watcher/tools/watcher-list.ts +9 -0
  170. package/src/config/bundled-skills/watcher/tools/watcher-update.ts +9 -0
  171. package/src/config/defaults.ts +33 -0
  172. package/src/config/loader.ts +4 -1
  173. package/src/config/schema.ts +161 -1
  174. package/src/config/system-prompt.ts +61 -16
  175. package/src/config/templates/IDENTITY.md +7 -0
  176. package/src/config/types.ts +4 -0
  177. package/src/contacts/contact-store.ts +4 -4
  178. package/src/daemon/assistant-attachments.ts +10 -0
  179. package/src/daemon/classifier.ts +3 -1
  180. package/src/daemon/computer-use-session.ts +3 -1
  181. package/src/daemon/date-context.ts +136 -0
  182. package/src/daemon/handlers/apps.ts +16 -1
  183. package/src/daemon/handlers/browser.ts +54 -0
  184. package/src/daemon/handlers/computer-use.ts +7 -1
  185. package/src/daemon/handlers/config.ts +163 -5
  186. package/src/daemon/handlers/diagnostics.ts +5 -1
  187. package/src/daemon/handlers/documents.ts +18 -29
  188. package/src/daemon/handlers/home-base.ts +5 -1
  189. package/src/daemon/handlers/index.ts +40 -277
  190. package/src/daemon/handlers/misc.ts +9 -1
  191. package/src/daemon/handlers/publish.ts +6 -1
  192. package/src/daemon/handlers/sessions.ts +65 -12
  193. package/src/daemon/handlers/shared.ts +36 -1
  194. package/src/daemon/handlers/signing.ts +37 -0
  195. package/src/daemon/handlers/skills.ts +20 -6
  196. package/src/daemon/handlers/subagents.ts +8 -3
  197. package/src/daemon/handlers/twitter-auth.ts +169 -0
  198. package/src/daemon/handlers/work-items.ts +384 -68
  199. package/src/daemon/ipc-contract-inventory.json +28 -4
  200. package/src/daemon/ipc-contract.ts +133 -37
  201. package/src/daemon/ipc-protocol.ts +7 -2
  202. package/src/daemon/lifecycle.ts +21 -0
  203. package/src/daemon/main.ts +10 -4
  204. package/src/daemon/ride-shotgun-handler.ts +74 -10
  205. package/src/daemon/server.ts +143 -26
  206. package/src/daemon/session-agent-loop.ts +887 -0
  207. package/src/daemon/session-attachments.ts +28 -5
  208. package/src/daemon/session-error.ts +24 -3
  209. package/src/daemon/session-lifecycle.ts +147 -0
  210. package/src/daemon/session-media-retry.ts +147 -0
  211. package/src/daemon/session-messaging.ts +145 -0
  212. package/src/daemon/session-notifiers.ts +164 -0
  213. package/src/daemon/session-process.ts +2 -2
  214. package/src/daemon/session-queue-manager.ts +1 -0
  215. package/src/daemon/session-runtime-assembly.ts +52 -0
  216. package/src/daemon/session-skill-tools.ts +124 -5
  217. package/src/daemon/session-slash.ts +3 -0
  218. package/src/daemon/session-surfaces.ts +77 -2
  219. package/src/daemon/session-tool-setup.ts +216 -2
  220. package/src/daemon/session-usage.ts +0 -2
  221. package/src/daemon/session.ts +114 -1404
  222. package/src/daemon/video-thumbnail.ts +60 -0
  223. package/src/doordash/client.ts +121 -27
  224. package/src/doordash/queries.ts +1 -2
  225. package/src/export/formatter.ts +3 -1
  226. package/src/followups/followup-store.ts +4 -2
  227. package/src/followups/types.ts +6 -0
  228. package/src/hooks/templates.ts +1 -1
  229. package/src/index.ts +32 -1153
  230. package/src/memory/attachments-store.ts +28 -83
  231. package/src/memory/channel-delivery-store.ts +7 -21
  232. package/src/memory/clarification-resolver.ts +6 -5
  233. package/src/memory/contradiction-checker.ts +3 -2
  234. package/src/memory/conversation-key-store.ts +10 -29
  235. package/src/memory/conversation-store.ts +2 -1
  236. package/src/memory/db.ts +96 -2
  237. package/src/memory/entity-extractor.ts +6 -3
  238. package/src/memory/items-extractor.ts +5 -4
  239. package/src/memory/jobs-store.ts +3 -2
  240. package/src/memory/llm-usage-store.ts +1 -2
  241. package/src/memory/runs-store.ts +1 -2
  242. package/src/memory/schema.ts +23 -2
  243. package/src/messaging/style-analyzer.ts +3 -2
  244. package/src/messaging/thread-summarizer.ts +8 -12
  245. package/src/messaging/triage-engine.ts +4 -2
  246. package/src/providers/openrouter/client.ts +20 -0
  247. package/src/providers/registry.ts +8 -0
  248. package/src/runtime/http-server.ts +108 -20
  249. package/src/runtime/routes/attachment-routes.ts +2 -3
  250. package/src/runtime/routes/call-routes.ts +140 -0
  251. package/src/runtime/routes/channel-routes.ts +5 -10
  252. package/src/runtime/routes/conversation-routes.ts +5 -5
  253. package/src/runtime/routes/run-routes.ts +2 -2
  254. package/src/runtime/run-orchestrator.ts +9 -3
  255. package/src/schedule/recurrence-engine.ts +138 -0
  256. package/src/schedule/recurrence-types.ts +67 -0
  257. package/src/schedule/schedule-store.ts +102 -57
  258. package/src/schedule/scheduler.ts +9 -6
  259. package/src/security/oauth2.ts +29 -4
  260. package/src/security/secret-allowlist.ts +46 -0
  261. package/src/skills/clawhub.ts +1 -1
  262. package/src/subagent/manager.ts +40 -8
  263. package/src/swarm/backend-claude-code.ts +64 -9
  264. package/src/swarm/worker-prompts.ts +2 -1
  265. package/src/tasks/SPEC.md +34 -28
  266. package/src/tasks/ephemeral-permissions.ts +16 -7
  267. package/src/tasks/task-compiler.ts +5 -4
  268. package/src/tasks/task-runner.ts +10 -5
  269. package/src/tasks/task-scheduler.ts +1 -1
  270. package/src/tasks/tool-sanitizer.ts +36 -0
  271. package/src/tools/assets/search.ts +4 -4
  272. package/src/tools/browser/api-map.ts +220 -0
  273. package/src/tools/browser/auto-navigate.ts +270 -0
  274. package/src/tools/browser/browser-execution.ts +2 -1
  275. package/src/tools/browser/browser-manager.ts +2 -2
  276. package/src/tools/browser/network-recorder.ts +5 -4
  277. package/src/tools/browser/x-auto-navigate.ts +207 -0
  278. package/src/tools/calls/call-end.ts +17 -67
  279. package/src/tools/calls/call-start.ts +24 -85
  280. package/src/tools/calls/call-status.ts +35 -51
  281. package/src/tools/claude-code/claude-code.ts +77 -11
  282. package/src/tools/contacts/contact-merge.ts +46 -78
  283. package/src/tools/contacts/contact-search.ts +35 -79
  284. package/src/tools/contacts/contact-upsert.ts +35 -108
  285. package/src/tools/credentials/vault.ts +20 -4
  286. package/src/tools/document/document-tool.ts +71 -144
  287. package/src/tools/executor.ts +129 -10
  288. package/src/tools/followups/followup_create.ts +46 -88
  289. package/src/tools/followups/followup_list.ts +34 -74
  290. package/src/tools/followups/followup_resolve.ts +31 -66
  291. package/src/tools/host-terminal/cli-discover.ts +2 -1
  292. package/src/tools/host-terminal/host-shell.ts +10 -0
  293. package/src/tools/memory/handlers.ts +5 -4
  294. package/src/tools/network/__tests__/web-search.test.ts +427 -0
  295. package/src/tools/network/script-proxy/__tests__/logging.test.ts +248 -0
  296. package/src/tools/network/script-proxy/__tests__/policy.test.ts +234 -0
  297. package/src/tools/network/script-proxy/__tests__/router.test.ts +76 -0
  298. package/src/tools/network/web-fetch.ts +18 -6
  299. package/src/tools/playbooks/index.ts +4 -5
  300. package/src/tools/playbooks/playbook-create.ts +3 -47
  301. package/src/tools/playbooks/playbook-delete.ts +1 -25
  302. package/src/tools/playbooks/playbook-list.ts +1 -28
  303. package/src/tools/playbooks/playbook-update.ts +3 -51
  304. package/src/tools/reminder/reminder.ts +5 -78
  305. package/src/tools/schedule/create.ts +69 -74
  306. package/src/tools/schedule/delete.ts +21 -47
  307. package/src/tools/schedule/list.ts +55 -74
  308. package/src/tools/schedule/update.ts +77 -84
  309. package/src/tools/subagent/abort.ts +29 -58
  310. package/src/tools/subagent/message.ts +30 -63
  311. package/src/tools/subagent/read.ts +53 -84
  312. package/src/tools/subagent/spawn.ts +43 -82
  313. package/src/tools/subagent/status.ts +42 -71
  314. package/src/tools/swarm/delegate.ts +2 -1
  315. package/src/tools/tasks/index.ts +8 -8
  316. package/src/tools/tasks/task-delete.ts +60 -88
  317. package/src/tools/tasks/task-list.ts +31 -52
  318. package/src/tools/tasks/task-run.ts +72 -108
  319. package/src/tools/tasks/task-save.ts +33 -65
  320. package/src/tools/tasks/work-item-enqueue.ts +183 -215
  321. package/src/tools/tasks/work-item-list.ts +33 -63
  322. package/src/tools/tasks/work-item-remove.ts +45 -97
  323. package/src/tools/tasks/work-item-update.ts +91 -163
  324. package/src/tools/terminal/backends/native.ts +3 -1
  325. package/src/tools/tool-manifest.ts +0 -62
  326. package/src/tools/types.ts +6 -0
  327. package/src/tools/ui-surface/definitions.ts +3 -1
  328. package/src/tools/watch/screen-watch.ts +3 -1
  329. package/src/tools/watcher/create.ts +52 -98
  330. package/src/tools/watcher/delete.ts +20 -46
  331. package/src/tools/watcher/digest.ts +36 -70
  332. package/src/tools/watcher/list.ts +49 -79
  333. package/src/tools/watcher/update.ts +45 -91
  334. package/src/twitter/client.ts +690 -0
  335. package/src/twitter/session.ts +91 -0
  336. package/src/usage/types.ts +0 -1
  337. package/src/util/truncate.ts +6 -0
  338. package/src/watcher/providers/slack.ts +2 -1
  339. package/src/watcher/watcher-store.ts +3 -2
  340. package/src/work-items/work-item-store.ts +27 -2
  341. package/src/workspace/commit-message-enrichment-service.ts +31 -7
  342. package/src/workspace/git-service.ts +87 -22
  343. package/src/workspace/provider-commit-message-generator.ts +242 -0
  344. package/src/workspace/turn-commit.ts +62 -3
  345. package/src/tools/contacts/index.ts +0 -4
  346. package/src/tools/document/index.ts +0 -5
  347. package/src/tools/followups/index.ts +0 -3
  348. package/src/tools/subagent/index.ts +0 -5
  349. /package/src/__tests__/{memory-context-benchmark.test.ts → memory-context-benchmark.benchmark.test.ts} +0 -0
@@ -1,110 +1,82 @@
1
- import { RiskLevel } from '../../permissions/types.js';
2
- import type { Tool, ToolContext, ToolExecutionResult } from '../types.js';
3
- import type { ToolDefinition } from '../../providers/types.js';
1
+ import type { ToolContext, ToolExecutionResult } from '../types.js';
4
2
  import { deleteTask, deleteTasks, getTask } from '../../tasks/task-store.js';
5
3
  import { removeWorkItemFromQueue } from '../../work-items/work-item-store.js';
6
4
  import { getLogger } from '../../util/logger.js';
7
5
 
8
6
  const log = getLogger('task-delete');
9
7
 
10
- const definition: ToolDefinition = {
11
- name: 'task_delete',
12
- description: 'Delete one or more task templates by ID. Also removes associated task runs and work items (Tasks).',
13
- input_schema: {
14
- type: 'object',
15
- properties: {
16
- task_ids: {
17
- type: 'array',
18
- items: { type: 'string' },
19
- description: 'One or more task IDs to delete.',
20
- },
21
- },
22
- required: ['task_ids'],
23
- },
24
- };
25
-
26
- class TaskDeleteTool implements Tool {
27
- name = 'task_delete';
28
- description = definition.description;
29
- category = 'tasks';
30
- defaultRiskLevel = RiskLevel.Medium;
31
-
32
- getDefinition(): ToolDefinition {
33
- return definition;
8
+ export async function executeTaskDelete(
9
+ input: Record<string, unknown>,
10
+ _context: ToolContext,
11
+ ): Promise<ToolExecutionResult> {
12
+ const raw = input.task_ids;
13
+ if (!Array.isArray(raw) || raw.length === 0) {
14
+ return { content: 'Error: task_ids must be a non-empty array of task ID strings', isError: true };
15
+ }
16
+ const ids = raw.filter((v): v is string => typeof v === 'string' && v.trim().length > 0);
17
+ if (ids.length === 0) {
18
+ return { content: 'Error: task_ids must contain at least one non-empty string', isError: true };
34
19
  }
35
20
 
36
- async execute(input: Record<string, unknown>, _context: ToolContext): Promise<ToolExecutionResult> {
37
- const raw = input.task_ids;
38
- if (!Array.isArray(raw) || raw.length === 0) {
39
- return { content: 'Error: task_ids must be a non-empty array of task ID strings', isError: true };
40
- }
41
- const ids = raw.filter((v): v is string => typeof v === 'string' && v.trim().length > 0);
42
- if (ids.length === 0) {
43
- return { content: 'Error: task_ids must contain at least one non-empty string', isError: true };
44
- }
45
-
46
- try {
47
- if (ids.length === 1) {
48
- const task = getTask(ids[0]);
49
- const deleted = deleteTask(ids[0]);
50
- if (!deleted) {
51
- // The LLM may pass a work item ID instead of a task template ID.
52
- // Fall back to removing from the task queue so the user's intent succeeds.
53
- const result = removeWorkItemFromQueue(ids[0]);
54
- if (result.success) {
55
- log.info({ inputId: ids[0], fallback: true, deletedCount: 1 }, 'deleted via work item fallback');
56
- return { content: result.message, isError: false };
57
- }
58
- log.warn({ inputId: ids[0] }, 'no task or work item found for deletion');
59
- return { content: `No task template or work item found with ID "${ids[0]}". Use task_list to see task templates or task_list_show to see work items in the queue.`, isError: true };
21
+ try {
22
+ if (ids.length === 1) {
23
+ const task = getTask(ids[0]);
24
+ const deleted = deleteTask(ids[0]);
25
+ if (!deleted) {
26
+ // The LLM may pass a work item ID instead of a task template ID.
27
+ // Fall back to removing from the task queue so the user's intent succeeds.
28
+ const result = removeWorkItemFromQueue(ids[0]);
29
+ if (result.success) {
30
+ log.info({ inputId: ids[0], fallback: true, deletedCount: 1 }, 'deleted via work item fallback');
31
+ return { content: result.message, isError: false };
60
32
  }
61
- log.info({ taskId: ids[0], title: task?.title, deletedCount: 1 }, 'task deleted');
62
- return { content: `Deleted task: ${task?.title ?? ids[0]}`, isError: false };
33
+ log.warn({ inputId: ids[0] }, 'no task or work item found for deletion');
34
+ return { content: `No task template or work item found with ID "${ids[0]}". Use task_list to see task templates or task_list_show to see work items in the queue.`, isError: true };
63
35
  }
36
+ log.info({ taskId: ids[0], title: task?.title, deletedCount: 1 }, 'task deleted');
37
+ return { content: `Deleted task: ${task?.title ?? ids[0]}`, isError: false };
38
+ }
64
39
 
65
- const taskIds: string[] = [];
66
- const taskTitles: string[] = [];
67
- const workItemTitles: string[] = [];
40
+ const taskIds: string[] = [];
41
+ const taskTitles: string[] = [];
42
+ const workItemTitles: string[] = [];
68
43
 
69
- for (const id of ids) {
70
- const task = getTask(id);
71
- if (task) {
72
- taskIds.push(id);
73
- taskTitles.push(task.title);
44
+ for (const id of ids) {
45
+ const task = getTask(id);
46
+ if (task) {
47
+ taskIds.push(id);
48
+ taskTitles.push(task.title);
49
+ } else {
50
+ const result = removeWorkItemFromQueue(id);
51
+ if (result.success) {
52
+ log.info({ inputId: id, fallback: true }, 'deleted work item in batch (fallback)');
53
+ workItemTitles.push(result.title);
74
54
  } else {
75
- const result = removeWorkItemFromQueue(id);
76
- if (result.success) {
77
- log.info({ inputId: id, fallback: true }, 'deleted work item in batch (fallback)');
78
- workItemTitles.push(result.title);
79
- } else {
80
- log.warn({ inputId: id }, 'batch delete: no task or work item found');
81
- }
55
+ log.warn({ inputId: id }, 'batch delete: no task or work item found');
82
56
  }
83
57
  }
58
+ }
84
59
 
85
- const taskCount = taskIds.length > 0 ? deleteTasks(taskIds) : 0;
60
+ const taskCount = taskIds.length > 0 ? deleteTasks(taskIds) : 0;
86
61
 
87
- if (taskCount === 0 && workItemTitles.length === 0) {
88
- log.warn({ inputIds: ids }, 'no matching tasks found to delete');
89
- return { content: 'No matching tasks found to delete.', isError: true };
90
- }
62
+ if (taskCount === 0 && workItemTitles.length === 0) {
63
+ log.warn({ inputIds: ids }, 'no matching tasks found to delete');
64
+ return { content: 'No matching tasks found to delete.', isError: true };
65
+ }
91
66
 
92
- log.info({ deletedTasks: taskCount, deletedWorkItems: workItemTitles.length, totalInput: ids.length }, 'batch delete completed');
67
+ log.info({ deletedTasks: taskCount, deletedWorkItems: workItemTitles.length, totalInput: ids.length }, 'batch delete completed');
93
68
 
94
- const lines: string[] = [];
95
- if (taskCount > 0) {
96
- lines.push(`Deleted ${taskCount} task(s):`, ...taskTitles.map((t) => `- ${t}`));
97
- }
98
- if (workItemTitles.length > 0) {
99
- lines.push(`Removed ${workItemTitles.length} item(s) from the task queue:`, ...workItemTitles.map((t) => `- ${t}`));
100
- }
101
- return { content: lines.join('\n'), isError: false };
102
- } catch (err) {
103
- const msg = err instanceof Error ? err.message : String(err);
104
- log.error({ inputIds: ids, error: msg }, 'delete failed');
105
- return { content: `Error: ${msg}`, isError: true };
69
+ const lines: string[] = [];
70
+ if (taskCount > 0) {
71
+ lines.push(`Deleted ${taskCount} task(s):`, ...taskTitles.map((t) => `- ${t}`));
72
+ }
73
+ if (workItemTitles.length > 0) {
74
+ lines.push(`Removed ${workItemTitles.length} item(s) from the task queue:`, ...workItemTitles.map((t) => `- ${t}`));
106
75
  }
76
+ return { content: lines.join('\n'), isError: false };
77
+ } catch (err) {
78
+ const msg = err instanceof Error ? err.message : String(err);
79
+ log.error({ inputIds: ids, error: msg }, 'delete failed');
80
+ return { content: `Error: ${msg}`, isError: true };
107
81
  }
108
82
  }
109
-
110
- export const taskDeleteTool = new TaskDeleteTool();
@@ -1,65 +1,44 @@
1
- import { RiskLevel } from '../../permissions/types.js';
2
- import type { Tool, ToolContext, ToolExecutionResult } from '../types.js';
3
- import type { ToolDefinition } from '../../providers/types.js';
1
+ import type { ToolContext, ToolExecutionResult } from '../types.js';
4
2
  import { listTasks } from '../../tasks/task-store.js';
5
3
 
6
- const definition: ToolDefinition = {
7
- name: 'task_list',
8
- description: 'List saved task templates (reusable definitions). To see your active Tasks (work items), use task_list_show instead.',
9
- input_schema: {
10
- type: 'object',
11
- properties: {},
12
- },
13
- };
4
+ export async function executeTaskList(
5
+ _input: Record<string, unknown>,
6
+ _context: ToolContext,
7
+ ): Promise<ToolExecutionResult> {
8
+ try {
9
+ const tasks = listTasks();
14
10
 
15
- class TaskListTool implements Tool {
16
- name = 'task_list';
17
- description = definition.description;
18
- category = 'tasks';
19
- defaultRiskLevel = RiskLevel.Low;
11
+ if (tasks.length === 0) {
12
+ return { content: 'No task templates found. Use task_save to create one from a conversation.\n\nTip: To see your active Tasks (work items in the queue), use the task_list_show tool.', isError: false };
13
+ }
20
14
 
21
- getDefinition(): ToolDefinition {
22
- return definition;
23
- }
15
+ const lines = [`Found ${tasks.length} task template(s):`, ''];
24
16
 
25
- async execute(_input: Record<string, unknown>, _context: ToolContext): Promise<ToolExecutionResult> {
26
- try {
27
- const tasks = listTasks();
17
+ for (const task of tasks) {
18
+ const requiredTools: string[] = task.requiredTools ? JSON.parse(task.requiredTools) : [];
19
+ const createdAt = new Date(task.createdAt).toISOString();
28
20
 
29
- if (tasks.length === 0) {
30
- return { content: 'No task templates found. Use task_save to create one from a conversation.\n\nTip: To see your active Tasks (work items in the queue), use the task_list_show tool.', isError: false };
21
+ lines.push(`- ${task.title}`);
22
+ lines.push(` ID: ${task.id}`);
23
+ lines.push(` Status: ${task.status}`);
24
+ lines.push(` Created: ${createdAt}`);
25
+ if (requiredTools.length > 0) {
26
+ lines.push(` Required tools: ${requiredTools.join(', ')}`);
31
27
  }
32
-
33
- const lines = [`Found ${tasks.length} task template(s):`, ''];
34
-
35
- for (const task of tasks) {
36
- const requiredTools: string[] = task.requiredTools ? JSON.parse(task.requiredTools) : [];
37
- const createdAt = new Date(task.createdAt).toISOString();
38
-
39
- lines.push(`- ${task.title}`);
40
- lines.push(` ID: ${task.id}`);
41
- lines.push(` Status: ${task.status}`);
42
- lines.push(` Created: ${createdAt}`);
43
- if (requiredTools.length > 0) {
44
- lines.push(` Required tools: ${requiredTools.join(', ')}`);
28
+ if (task.inputSchema) {
29
+ const schema = JSON.parse(task.inputSchema) as { properties?: Record<string, unknown> };
30
+ if (schema.properties) {
31
+ lines.push(` Inputs: ${Object.keys(schema.properties).join(', ')}`);
45
32
  }
46
- if (task.inputSchema) {
47
- const schema = JSON.parse(task.inputSchema) as { properties?: Record<string, unknown> };
48
- if (schema.properties) {
49
- lines.push(` Inputs: ${Object.keys(schema.properties).join(', ')}`);
50
- }
51
- }
52
- lines.push('');
53
33
  }
34
+ lines.push('');
35
+ }
54
36
 
55
- lines.push('Tip: To see your active Tasks (work items in the queue), use the task_list_show tool.');
37
+ lines.push('Tip: To see your active Tasks (work items in the queue), use the task_list_show tool.');
56
38
 
57
- return { content: lines.join('\n'), isError: false };
58
- } catch (err) {
59
- const msg = err instanceof Error ? err.message : String(err);
60
- return { content: `Error: ${msg}`, isError: true };
61
- }
39
+ return { content: lines.join('\n'), isError: false };
40
+ } catch (err) {
41
+ const msg = err instanceof Error ? err.message : String(err);
42
+ return { content: `Error: ${msg}`, isError: true };
62
43
  }
63
44
  }
64
-
65
- export const taskListTool = new TaskListTool();
@@ -1,133 +1,97 @@
1
- import { RiskLevel } from '../../permissions/types.js';
2
- import type { Tool, ToolContext, ToolExecutionResult } from '../types.js';
3
- import type { ToolDefinition } from '../../providers/types.js';
1
+ import type { ToolContext, ToolExecutionResult } from '../types.js';
4
2
  import { getTask, listTasks } from '../../tasks/task-store.js';
5
3
  import { renderTemplate } from '../../tasks/task-runner.js';
6
4
  import { identifyEntityById, buildWorkItemMismatchError } from '../../work-items/work-item-store.js';
7
5
 
8
- const definition: ToolDefinition = {
9
- name: 'task_run',
10
- description:
11
- 'Run a task template. Resolves the template by name (fuzzy match) or ID, renders it with the provided inputs, and returns the rendered prompt for execution as a Task (work item).',
12
- input_schema: {
13
- type: 'object',
14
- properties: {
15
- task_name: {
16
- type: 'string',
17
- description: 'Fuzzy match a task template by name (case-insensitive substring match)',
18
- },
19
- task_id: {
20
- type: 'string',
21
- description: 'Exact match a task template by ID',
22
- },
23
- inputs: {
24
- type: 'object',
25
- description: 'Values for template placeholders (e.g. {"file_path": "/tmp/foo.txt", "url": "https://example.com"})',
26
- additionalProperties: { type: 'string' },
27
- },
28
- },
29
- },
30
- };
31
-
32
- class TaskRunTool implements Tool {
33
- name = 'task_run';
34
- description = definition.description;
35
- category = 'tasks';
36
- defaultRiskLevel = RiskLevel.Low;
37
-
38
- getDefinition(): ToolDefinition {
39
- return definition;
6
+ export async function executeTaskRun(
7
+ input: Record<string, unknown>,
8
+ _context: ToolContext,
9
+ ): Promise<ToolExecutionResult> {
10
+ const taskName = input.task_name as string | undefined;
11
+ const taskId = input.task_id as string | undefined;
12
+ const inputs = (input.inputs as Record<string, string> | undefined) ?? {};
13
+
14
+ if (!taskName && !taskId) {
15
+ return {
16
+ content: 'Error: At least one of task_name or task_id must be provided',
17
+ isError: true,
18
+ };
40
19
  }
41
20
 
42
- async execute(input: Record<string, unknown>, _context: ToolContext): Promise<ToolExecutionResult> {
43
- const taskName = input.task_name as string | undefined;
44
- const taskId = input.task_id as string | undefined;
45
- const inputs = (input.inputs as Record<string, string> | undefined) ?? {};
46
-
47
- if (!taskName && !taskId) {
48
- return {
49
- content: 'Error: At least one of task_name or task_id must be provided',
50
- isError: true,
51
- };
52
- }
53
-
54
- try {
55
- // Resolve the task
56
- let task;
21
+ try {
22
+ // Resolve the task
23
+ let task;
57
24
 
58
- if (taskId) {
59
- task = getTask(taskId);
60
- if (!task) {
61
- const entity = identifyEntityById(taskId);
62
- if (entity.type === 'work_item') {
63
- return {
64
- content: `Error: ${buildWorkItemMismatchError(taskId, entity.title!, 'task_list_show to view work items, or task_list_update to modify them')}`,
65
- isError: true,
66
- };
67
- }
68
- return { content: `Error: No task template found with ID "${taskId}". Use task_list to see available templates.`, isError: true };
69
- }
70
- } else if (taskName) {
71
- const allTasks = listTasks();
72
- const needle = taskName.toLowerCase();
73
-
74
- // Case-insensitive substring match
75
- task = allTasks.find((t) => t.title.toLowerCase().includes(needle));
76
-
77
- if (!task) {
78
- if (allTasks.length === 0) {
79
- return { content: 'Error: No task templates found. Use task_save to create one first.', isError: true };
80
- }
81
- const available = allTasks.map((t) => ` - "${t.title}" (${t.id})`).join('\n');
25
+ if (taskId) {
26
+ task = getTask(taskId);
27
+ if (!task) {
28
+ const entity = identifyEntityById(taskId);
29
+ if (entity.type === 'work_item') {
82
30
  return {
83
- content: `Error: No task template matching "${taskName}" found. Available templates:\n${available}`,
31
+ content: `Error: ${buildWorkItemMismatchError(taskId, entity.title!, 'task_list_show to view work items, or task_list_update to modify them')}`,
84
32
  isError: true,
85
33
  };
86
34
  }
35
+ return { content: `Error: No task template found with ID "${taskId}". Use task_list to see available templates.`, isError: true };
87
36
  }
37
+ } else if (taskName) {
38
+ const allTasks = listTasks();
39
+ const needle = taskName.toLowerCase();
40
+
41
+ // Case-insensitive substring match
42
+ task = allTasks.find((t) => t.title.toLowerCase().includes(needle));
88
43
 
89
44
  if (!task) {
90
- return { content: 'Error: Could not resolve task template', isError: true };
45
+ if (allTasks.length === 0) {
46
+ return { content: 'Error: No task templates found. Use task_save to create one first.', isError: true };
47
+ }
48
+ const available = allTasks.map((t) => ` - "${t.title}" (${t.id})`).join('\n');
49
+ return {
50
+ content: `Error: No task template matching "${taskName}" found. Available templates:\n${available}`,
51
+ isError: true,
52
+ };
91
53
  }
54
+ }
55
+
56
+ if (!task) {
57
+ return { content: 'Error: Could not resolve task template', isError: true };
58
+ }
92
59
 
93
- // Check if required inputs are provided
94
- if (task.inputSchema) {
95
- const schema = JSON.parse(task.inputSchema) as { properties?: Record<string, unknown> };
96
- if (schema.properties) {
97
- const requiredKeys = Object.keys(schema.properties);
98
- const missingKeys = requiredKeys.filter((k) => !(k in inputs));
99
- if (missingKeys.length > 0) {
100
- return {
101
- content: `Error: Missing required inputs: ${missingKeys.join(', ')}. Provide them in the "inputs" parameter.`,
102
- isError: true,
103
- };
104
- }
60
+ // Check if required inputs are provided
61
+ if (task.inputSchema) {
62
+ const schema = JSON.parse(task.inputSchema) as { properties?: Record<string, unknown> };
63
+ if (schema.properties) {
64
+ const requiredKeys = Object.keys(schema.properties);
65
+ const missingKeys = requiredKeys.filter((k) => !(k in inputs));
66
+ if (missingKeys.length > 0) {
67
+ return {
68
+ content: `Error: Missing required inputs: ${missingKeys.join(', ')}. Provide them in the "inputs" parameter.`,
69
+ isError: true,
70
+ };
105
71
  }
106
72
  }
73
+ }
107
74
 
108
- // Render the template
109
- const rendered = renderTemplate(task.template, inputs);
110
-
111
- const requiredTools: string[] = task.requiredTools ? JSON.parse(task.requiredTools) : [];
75
+ // Render the template
76
+ const rendered = renderTemplate(task.template, inputs);
112
77
 
113
- const lines = [
114
- `Template "${task.title}" resolved and rendered.`,
115
- ``,
116
- `I'll now execute the following:`,
117
- ``,
118
- rendered,
119
- ];
78
+ const requiredTools: string[] = task.requiredTools ? JSON.parse(task.requiredTools) : [];
120
79
 
121
- if (requiredTools.length > 0) {
122
- lines.push('', `Required tools: ${requiredTools.join(', ')}`);
123
- }
80
+ const lines = [
81
+ `Template "${task.title}" resolved and rendered.`,
82
+ ``,
83
+ `I'll now execute the following:`,
84
+ ``,
85
+ rendered,
86
+ ];
124
87
 
125
- return { content: lines.join('\n'), isError: false };
126
- } catch (err) {
127
- const msg = err instanceof Error ? err.message : String(err);
128
- return { content: `Error: ${msg}`, isError: true };
88
+ if (requiredTools.length > 0) {
89
+ lines.push('', `Required tools: ${requiredTools.join(', ')}`);
129
90
  }
91
+
92
+ return { content: lines.join('\n'), isError: false };
93
+ } catch (err) {
94
+ const msg = err instanceof Error ? err.message : String(err);
95
+ return { content: `Error: ${msg}`, isError: true };
130
96
  }
131
97
  }
132
-
133
- export const taskRunTool = new TaskRunTool();
@@ -1,79 +1,47 @@
1
- import { RiskLevel } from '../../permissions/types.js';
2
- import type { Tool, ToolContext, ToolExecutionResult } from '../types.js';
3
- import type { ToolDefinition } from '../../providers/types.js';
1
+ import type { ToolContext, ToolExecutionResult } from '../types.js';
4
2
  import { compileTaskFromConversation, saveCompiledTask } from '../../tasks/task-compiler.js';
5
3
 
6
- const definition: ToolDefinition = {
7
- name: 'task_save',
8
- description:
9
- 'Save the current conversation as a reusable task template (definition). This is NOT for adding items to the user\'s task queue — use task_list_add for that. task_save extracts the conversation pattern into a reusable definition with placeholders that can be run later with different inputs.',
10
- input_schema: {
11
- type: 'object',
12
- properties: {
13
- conversation_id: {
14
- type: 'string',
15
- description: 'The conversation to capture as a task template. If omitted, uses the current conversation.',
16
- },
17
- title: {
18
- type: 'string',
19
- description: 'Optional override for the auto-generated task title',
20
- },
21
- },
22
- required: [],
23
- },
24
- };
25
-
26
- class TaskSaveTool implements Tool {
27
- name = 'task_save';
28
- description = definition.description;
29
- category = 'tasks';
30
- defaultRiskLevel = RiskLevel.Low;
31
-
32
- getDefinition(): ToolDefinition {
33
- return definition;
4
+ export async function executeTaskSave(
5
+ input: Record<string, unknown>,
6
+ context: ToolContext,
7
+ ): Promise<ToolExecutionResult> {
8
+ const conversationId = (input.conversation_id as string | undefined) || context.conversationId;
9
+ if (!conversationId || typeof conversationId !== 'string' || conversationId.trim().length === 0) {
10
+ return { content: 'Error: conversation_id is required and must be a non-empty string', isError: true };
34
11
  }
35
12
 
36
- async execute(input: Record<string, unknown>, context: ToolContext): Promise<ToolExecutionResult> {
37
- const conversationId = (input.conversation_id as string | undefined) || context.conversationId;
38
- if (!conversationId || typeof conversationId !== 'string' || conversationId.trim().length === 0) {
39
- return { content: 'Error: conversation_id is required and must be a non-empty string', isError: true };
40
- }
41
-
42
- const titleOverride = input.title as string | undefined;
13
+ const titleOverride = input.title as string | undefined;
43
14
 
44
- try {
45
- const compiled = compileTaskFromConversation(conversationId);
15
+ try {
16
+ const compiled = compileTaskFromConversation(conversationId);
46
17
 
47
- if (titleOverride && typeof titleOverride === 'string' && titleOverride.trim().length > 0) {
48
- compiled.title = titleOverride.trim();
49
- }
18
+ if (titleOverride && typeof titleOverride === 'string' && titleOverride.trim().length > 0) {
19
+ compiled.title = titleOverride.trim();
20
+ }
50
21
 
51
- const task = saveCompiledTask(compiled, conversationId);
22
+ const task = saveCompiledTask(compiled, conversationId);
52
23
 
53
- const lines = [
54
- `Task saved successfully.`,
55
- ` ID: ${task.id}`,
56
- ` Title: ${task.title}`,
57
- ` Template: ${task.template}`,
58
- ];
24
+ const lines = [
25
+ `Task saved successfully.`,
26
+ ` ID: ${task.id}`,
27
+ ` Title: ${task.title}`,
28
+ ` Template: ${task.template}`,
29
+ ];
59
30
 
60
- if (compiled.requiredTools.length > 0) {
61
- lines.push(` Required tools: ${compiled.requiredTools.join(', ')}`);
62
- }
31
+ if (compiled.requiredTools.length > 0) {
32
+ lines.push(` Required tools: ${compiled.requiredTools.join(', ')}`);
33
+ }
63
34
 
64
- if (compiled.inputSchema) {
65
- const props = (compiled.inputSchema as Record<string, unknown>).properties as Record<string, unknown> | undefined;
66
- if (props) {
67
- lines.push(` Input placeholders: ${Object.keys(props).join(', ')}`);
68
- }
35
+ if (compiled.inputSchema) {
36
+ const props = (compiled.inputSchema as Record<string, unknown>).properties as Record<string, unknown> | undefined;
37
+ if (props) {
38
+ lines.push(` Input placeholders: ${Object.keys(props).join(', ')}`);
69
39
  }
70
-
71
- return { content: lines.join('\n'), isError: false };
72
- } catch (err) {
73
- const msg = err instanceof Error ? err.message : String(err);
74
- return { content: `Error: ${msg}`, isError: true };
75
40
  }
41
+
42
+ return { content: lines.join('\n'), isError: false };
43
+ } catch (err) {
44
+ const msg = err instanceof Error ? err.message : String(err);
45
+ return { content: `Error: ${msg}`, isError: true };
76
46
  }
77
47
  }
78
-
79
- export const taskSaveTool = new TaskSaveTool();