@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea

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 (570) hide show
  1. package/Dockerfile +20 -4
  2. package/bun.lock +2 -2
  3. package/docker-entrypoint.sh +4 -2
  4. package/docker-init-apt-root.sh +3 -1
  5. package/docker-kata-apt-env.sh +3 -1
  6. package/docker-kata-runtime-family.sh +12 -0
  7. package/docs/architecture/memory.md +1 -1
  8. package/examples/plugins/echo/README.md +61 -66
  9. package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
  10. package/examples/plugins/echo/hooks/stop.ts +16 -0
  11. package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
  12. package/examples/plugins/echo/package.json +1 -2
  13. package/examples/plugins/echo/src/emit.ts +19 -0
  14. package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
  15. package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
  16. package/openapi.yaml +3378 -335
  17. package/package.json +2 -2
  18. package/scripts/generate-openapi.ts +68 -41
  19. package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
  20. package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
  21. package/src/__tests__/agent-loop.test.ts +37 -87
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
  23. package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
  24. package/src/__tests__/annotate-risk-options.test.ts +2 -3
  25. package/src/__tests__/anthropic-provider.test.ts +95 -2
  26. package/src/__tests__/app-control-flow.test.ts +1 -1
  27. package/src/__tests__/app-dir-path-guard.test.ts +1 -0
  28. package/src/__tests__/approval-routes-http.test.ts +4 -1
  29. package/src/__tests__/assistant-event-hub.test.ts +25 -0
  30. package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
  31. package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
  32. package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
  33. package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
  34. package/src/__tests__/btw-routes.test.ts +62 -3
  35. package/src/__tests__/build-persisted-content.test.ts +184 -0
  36. package/src/__tests__/catalog-files.test.ts +1 -1
  37. package/src/__tests__/channel-approval-routes.test.ts +1 -1
  38. package/src/__tests__/channel-approvals.test.ts +1 -1
  39. package/src/__tests__/clawhub-files.test.ts +1 -1
  40. package/src/__tests__/compaction-circuit.test.ts +258 -0
  41. package/src/__tests__/compaction-direct.test.ts +132 -0
  42. package/src/__tests__/compaction.benchmark.test.ts +0 -30
  43. package/src/__tests__/config-watcher.test.ts +1 -1
  44. package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
  45. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
  46. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
  47. package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
  48. package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
  49. package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
  50. package/src/__tests__/conversation-clean-command.test.ts +5 -2
  51. package/src/__tests__/conversation-history-web-search.test.ts +11 -1
  52. package/src/__tests__/conversation-pairing.test.ts +4 -31
  53. package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
  54. package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
  55. package/src/__tests__/conversation-queue.test.ts +2 -0
  56. package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
  57. package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
  58. package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
  59. package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
  60. package/src/__tests__/conversation-slash-commands.test.ts +8 -42
  61. package/src/__tests__/conversation-slash-queue.test.ts +6 -1
  62. package/src/__tests__/conversation-starter-routes.test.ts +14 -6
  63. package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
  64. package/src/__tests__/conversation-sync-tags.test.ts +27 -15
  65. package/src/__tests__/conversation-title-service.test.ts +135 -2
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
  67. package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
  69. package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
  70. package/src/__tests__/cross-provider-web-search.test.ts +214 -1
  71. package/src/__tests__/db-acp-history.test.ts +101 -0
  72. package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
  73. package/src/__tests__/dm-persistence.test.ts +5 -1
  74. package/src/__tests__/dynamic-page-surface.test.ts +31 -0
  75. package/src/__tests__/empty-response-hook.test.ts +304 -0
  76. package/src/__tests__/feature-flag-test-helpers.ts +2 -2
  77. package/src/__tests__/file-write-tool.test.ts +63 -0
  78. package/src/__tests__/gateway-only-guard.test.ts +12 -2
  79. package/src/__tests__/gemini-image-service.test.ts +13 -0
  80. package/src/__tests__/guardian-grant-minting.test.ts +1 -1
  81. package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
  82. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  83. package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
  84. package/src/__tests__/heartbeat-service.test.ts +1 -0
  85. package/src/__tests__/helpers/mock-provider.ts +110 -0
  86. package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
  87. package/src/__tests__/history-repair-hook.test.ts +1 -0
  88. package/src/__tests__/host-app-control-routes.test.ts +1 -1
  89. package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
  90. package/src/__tests__/identity-intro-cache.test.ts +12 -100
  91. package/src/__tests__/identity-routes.test.ts +248 -7
  92. package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
  93. package/src/__tests__/injector-background-turn.test.ts +3 -9
  94. package/src/__tests__/injector-chain.test.ts +139 -275
  95. package/src/__tests__/injector-disk-pressure.test.ts +75 -41
  96. package/src/__tests__/injector-document-comments.test.ts +3 -3
  97. package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
  98. package/src/__tests__/injector-v3-suppression.test.ts +31 -37
  99. package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
  100. package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
  101. package/src/__tests__/list-messages-page-latest.test.ts +60 -0
  102. package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
  103. package/src/__tests__/llm-usage-store.test.ts +223 -1
  104. package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
  105. package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
  106. package/src/__tests__/native-web-search.test.ts +191 -0
  107. package/src/__tests__/onboarding-template-contract.test.ts +2 -0
  108. package/src/__tests__/openai-image-service.test.ts +17 -0
  109. package/src/__tests__/openai-provider.test.ts +31 -1
  110. package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
  111. package/src/__tests__/persist-unsendable-image.test.ts +215 -0
  112. package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
  113. package/src/__tests__/pkb-autoinject.test.ts +2 -5
  114. package/src/__tests__/plugin-api-shim.test.ts +3 -6
  115. package/src/__tests__/plugin-bootstrap.test.ts +14 -40
  116. package/src/__tests__/plugin-registry.test.ts +3 -76
  117. package/src/__tests__/plugin-types.test.ts +0 -193
  118. package/src/__tests__/process-message-display-content.test.ts +6 -2
  119. package/src/__tests__/reaction-persistence.test.ts +1 -1
  120. package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
  121. package/src/__tests__/resolve-trust-class.test.ts +4 -4
  122. package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
  123. package/src/__tests__/schedule-routes.test.ts +603 -2
  124. package/src/__tests__/schedule-store.test.ts +41 -0
  125. package/src/__tests__/schedule-tools.test.ts +35 -0
  126. package/src/__tests__/send-endpoint-busy.test.ts +4 -1
  127. package/src/__tests__/server-history-render.test.ts +314 -1
  128. package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
  129. package/src/__tests__/skillssh-files.test.ts +1 -1
  130. package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
  131. package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
  132. package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
  133. package/src/__tests__/subagent-manager-notify.test.ts +1 -3
  134. package/src/__tests__/subagent-notify-parent.test.ts +1 -3
  135. package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
  136. package/src/__tests__/system-prompt.test.ts +20 -0
  137. package/src/__tests__/task-scheduler.test.ts +162 -1
  138. package/src/__tests__/terminal-tools.test.ts +6 -1
  139. package/src/__tests__/title-generate-hook.test.ts +319 -0
  140. package/src/__tests__/tool-error-hook.test.ts +278 -0
  141. package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
  142. package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
  143. package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
  144. package/src/__tests__/tool-result-truncation.test.ts +0 -2
  145. package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
  146. package/src/__tests__/ui-work-result-surface.test.ts +159 -0
  147. package/src/__tests__/usage-routes.test.ts +285 -1
  148. package/src/__tests__/user-plugin-loader.test.ts +54 -286
  149. package/src/__tests__/voice-session-bridge.test.ts +6 -3
  150. package/src/__tests__/web-search-backend-failure.test.ts +166 -0
  151. package/src/acp/__tests__/agent-process.test.ts +161 -0
  152. package/src/acp/__tests__/client-handler.test.ts +40 -0
  153. package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
  154. package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
  155. package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
  156. package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
  157. package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
  158. package/src/acp/agent-process.ts +61 -1
  159. package/src/acp/auto-install.test.ts +196 -0
  160. package/src/acp/auto-install.ts +177 -0
  161. package/src/acp/client-handler.ts +31 -0
  162. package/src/acp/feature-gate.test.ts +48 -0
  163. package/src/acp/feature-gate.ts +34 -0
  164. package/src/acp/prepare-agent-env.ts +83 -29
  165. package/src/acp/resolve-agent.test.ts +320 -7
  166. package/src/acp/resolve-agent.ts +182 -18
  167. package/src/acp/resume-hint.ts +25 -0
  168. package/src/acp/session-manager.ts +495 -73
  169. package/src/acp/types.ts +8 -0
  170. package/src/agent/compaction-circuit.ts +60 -102
  171. package/src/agent/loop.ts +362 -485
  172. package/src/api/events/assistant-thinking-delta.ts +33 -0
  173. package/src/api/events/tool-output-chunk.ts +45 -0
  174. package/src/api/events/tool-use-preview-start.ts +32 -0
  175. package/src/api/events/trace-event.ts +69 -0
  176. package/src/api/index.ts +48 -13
  177. package/src/api/responses/conversation-message.ts +374 -0
  178. package/src/approvals/guardian-request-resolvers.ts +1 -1
  179. package/src/avatar/__tests__/avatar-store.test.ts +34 -29
  180. package/src/background-wake/next-wake.ts +1 -0
  181. package/src/cli/commands/__tests__/notifications.test.ts +58 -14
  182. package/src/cli/commands/notifications.ts +112 -60
  183. package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
  184. package/src/config/acp-defaults.test.ts +10 -0
  185. package/src/config/acp-defaults.ts +6 -0
  186. package/src/config/assistant-feature-flags.ts +22 -11
  187. package/src/config/bundled-skills/acp/SKILL.md +83 -31
  188. package/src/config/bundled-skills/acp/TOOLS.json +4 -4
  189. package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
  190. package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
  191. package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
  192. package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
  193. package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
  194. package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
  195. package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
  196. package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
  197. package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
  198. package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
  199. package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
  200. package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
  201. package/src/config/bundled-skills/messaging/SKILL.md +0 -7
  202. package/src/config/bundled-tool-registry.ts +2 -0
  203. package/src/config/feature-flag-cache.ts +3 -3
  204. package/src/config/feature-flag-registry.json +48 -7
  205. package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
  206. package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
  207. package/src/config/schemas/heartbeat.ts +9 -0
  208. package/src/config/schemas/llm.ts +1 -0
  209. package/src/config/schemas/memory-v2.ts +8 -0
  210. package/src/config/schemas/memory-v3.ts +8 -0
  211. package/src/config/schemas/platform.ts +8 -0
  212. package/src/config/seed-inference-profiles.ts +2 -2
  213. package/src/config/skills.ts +13 -0
  214. package/src/context/compactor.ts +1 -1
  215. package/src/context/strip-injections.ts +128 -0
  216. package/src/context/token-estimator.ts +23 -0
  217. package/src/context/tool-result-truncation.ts +0 -23
  218. package/src/context/window-manager.ts +5 -7
  219. package/src/credential-execution/executable-discovery.ts +16 -0
  220. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
  221. package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
  222. package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
  223. package/src/daemon/assistant-attachments.ts +1 -1
  224. package/src/daemon/config-watcher.ts +2 -2
  225. package/src/daemon/context-overflow-reducer.ts +0 -1
  226. package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
  227. package/src/daemon/conversation-agent-loop.ts +301 -997
  228. package/src/daemon/conversation-history.ts +5 -4
  229. package/src/daemon/conversation-lifecycle.ts +3 -4
  230. package/src/daemon/conversation-messaging.ts +7 -6
  231. package/src/daemon/conversation-process.ts +11 -16
  232. package/src/daemon/conversation-registry.ts +159 -0
  233. package/src/daemon/conversation-runtime-assembly.ts +218 -398
  234. package/src/daemon/conversation-slash.ts +6 -25
  235. package/src/daemon/conversation-store.ts +9 -90
  236. package/src/daemon/conversation-surfaces.ts +222 -4
  237. package/src/daemon/conversation-tool-setup.ts +2 -29
  238. package/src/daemon/conversation-workspace.ts +17 -0
  239. package/src/daemon/conversation.ts +32 -20
  240. package/src/daemon/external-plugins-bootstrap.ts +17 -18
  241. package/src/daemon/handlers/config-a2a.ts +51 -36
  242. package/src/daemon/handlers/config-slack-channel.ts +20 -14
  243. package/src/daemon/handlers/config-telegram.ts +16 -2
  244. package/src/daemon/handlers/conversations.ts +3 -1
  245. package/src/daemon/handlers/shared.ts +156 -84
  246. package/src/daemon/handlers/skills.ts +42 -10
  247. package/src/daemon/lifecycle.ts +25 -0
  248. package/src/daemon/message-types/apps.ts +1 -29
  249. package/src/daemon/message-types/messages.ts +9 -57
  250. package/src/daemon/message-types/skills.ts +2 -0
  251. package/src/daemon/message-types/surfaces.ts +136 -3
  252. package/src/daemon/now-scratchpad.ts +21 -0
  253. package/src/daemon/orphan-reaper.test.ts +210 -0
  254. package/src/daemon/orphan-reaper.ts +240 -0
  255. package/src/daemon/overflow-reduction-loop.ts +230 -0
  256. package/src/daemon/persist-unsendable-image.ts +117 -0
  257. package/src/daemon/process-message.ts +1 -3
  258. package/src/daemon/server.ts +2 -0
  259. package/src/daemon/trace-emitter.ts +6 -4
  260. package/src/daemon/trust-context.ts +19 -0
  261. package/src/daemon/wake-target-adapter.ts +3 -1
  262. package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
  263. package/src/heartbeat/heartbeat-run-store.ts +23 -1
  264. package/src/heartbeat/heartbeat-service.ts +26 -0
  265. package/src/home/home-greeting-cache.ts +24 -1
  266. package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
  267. package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
  268. package/src/ipc/gateway-client.test.ts +2 -2
  269. package/src/ipc/gateway-client.ts +3 -3
  270. package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
  271. package/src/ipc/skill-routes/memory.ts +4 -2
  272. package/src/media/gemini-image-service.ts +15 -0
  273. package/src/media/openai-image-service.ts +14 -0
  274. package/src/media/types.ts +34 -0
  275. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
  276. package/src/memory/auth-fallback-events-store.ts +94 -0
  277. package/src/memory/conversation-starter-checkpoints.ts +1 -0
  278. package/src/memory/conversation-title-service.ts +65 -41
  279. package/src/memory/db-init.ts +6 -0
  280. package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
  281. package/src/memory/graph/conversation-graph-memory.ts +65 -0
  282. package/src/memory/job-handlers/conversation-starters.ts +13 -2
  283. package/src/memory/jobs-store.ts +33 -0
  284. package/src/memory/jobs-worker.ts +32 -5
  285. package/src/memory/llm-usage-store.ts +224 -50
  286. package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
  287. package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
  288. package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
  289. package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
  290. package/src/memory/migrations/index.ts +3 -0
  291. package/src/memory/pkb/autoinject.ts +61 -0
  292. package/src/memory/pkb/context.ts +50 -0
  293. package/src/memory/pkb/types.ts +14 -0
  294. package/src/memory/schedule-attribution-sql.ts +104 -0
  295. package/src/memory/schema/acp.ts +4 -0
  296. package/src/memory/schema/infrastructure.ts +16 -0
  297. package/src/memory/usage-grouped-buckets.ts +6 -1
  298. package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
  299. package/src/memory/v2/consolidation-job.ts +14 -5
  300. package/src/notifications/conversation-pairing.ts +8 -15
  301. package/src/notifications/decision-engine.ts +6 -3
  302. package/src/notifications/home-feed-side-effect.ts +12 -1
  303. package/src/permissions/prompter.ts +4 -0
  304. package/src/plugin-api/constants.ts +4 -0
  305. package/src/plugin-api/index.ts +7 -5
  306. package/src/plugin-api/types.ts +151 -1
  307. package/src/plugins/defaults/compaction/compact.ts +59 -0
  308. package/src/plugins/defaults/compaction/package.json +1 -1
  309. package/src/plugins/defaults/compaction/register.ts +8 -19
  310. package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
  311. package/src/plugins/defaults/empty-response/register.ts +8 -13
  312. package/src/plugins/defaults/index.ts +2 -18
  313. package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
  314. package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
  315. package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
  316. package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
  317. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
  318. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
  319. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
  320. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
  321. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
  322. package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
  323. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
  324. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
  325. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
  326. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
  327. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
  328. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
  329. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
  330. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
  331. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
  332. package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
  333. package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
  334. package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
  335. package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
  336. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
  337. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
  338. package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
  339. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
  340. package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
  341. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
  342. package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
  343. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
  344. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
  345. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
  346. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
  347. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
  348. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
  349. package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
  350. package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
  351. package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
  352. package/src/plugins/defaults/title-generate/package.json +1 -1
  353. package/src/plugins/defaults/title-generate/register.ts +18 -18
  354. package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
  355. package/src/plugins/defaults/tool-error/package.json +1 -1
  356. package/src/plugins/defaults/tool-error/register.ts +9 -21
  357. package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
  358. package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
  359. package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
  360. package/src/plugins/external-api.ts +2 -2
  361. package/src/plugins/pipeline.ts +6 -305
  362. package/src/plugins/registry.ts +10 -55
  363. package/src/plugins/types.ts +62 -797
  364. package/src/plugins/user-loader.ts +30 -127
  365. package/src/proactive-artifact/aux-message-injector.ts +4 -4
  366. package/src/proactive-artifact/job.test.ts +8 -13
  367. package/src/prompts/__tests__/system-prompt.test.ts +42 -0
  368. package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
  369. package/src/prompts/templates/BOOTSTRAP.md +2 -2
  370. package/src/prompts/templates/system-sections.ts +15 -0
  371. package/src/providers/anthropic/client.ts +37 -29
  372. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
  373. package/src/providers/openai/chat-completions-provider.ts +44 -0
  374. package/src/providers/openrouter/client.ts +1 -0
  375. package/src/providers/placeholder-sentinels.ts +35 -0
  376. package/src/runtime/__tests__/agent-wake.test.ts +10 -6
  377. package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
  378. package/src/runtime/agent-wake.ts +2 -5
  379. package/src/runtime/assistant-event-hub.ts +37 -7
  380. package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
  381. package/src/runtime/channel-approvals.ts +1 -1
  382. package/src/runtime/http-router.ts +16 -21
  383. package/src/runtime/http-types.ts +16 -70
  384. package/src/runtime/interactive-ui.ts +1 -1
  385. package/src/runtime/pending-interactions.ts +1 -0
  386. package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
  387. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
  388. package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
  389. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
  390. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
  391. package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
  392. package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
  393. package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
  394. package/src/runtime/routes/acp-routes.test.ts +89 -25
  395. package/src/runtime/routes/acp-routes.ts +81 -29
  396. package/src/runtime/routes/app-management-routes.ts +6 -117
  397. package/src/runtime/routes/app-routes.ts +13 -15
  398. package/src/runtime/routes/approval-routes.ts +1 -1
  399. package/src/runtime/routes/attachment-routes.ts +26 -15
  400. package/src/runtime/routes/avatar-routes.ts +26 -0
  401. package/src/runtime/routes/browser-routes.ts +1 -1
  402. package/src/runtime/routes/browser-tabs-routes.ts +6 -10
  403. package/src/runtime/routes/btw-routes.ts +29 -23
  404. package/src/runtime/routes/consolidation-routes.ts +120 -20
  405. package/src/runtime/routes/conversation-cli-routes.ts +1 -1
  406. package/src/runtime/routes/conversation-list-routes.ts +1 -1
  407. package/src/runtime/routes/conversation-query-routes.ts +3 -1
  408. package/src/runtime/routes/conversation-routes.ts +372 -185
  409. package/src/runtime/routes/conversation-starter-routes.ts +13 -7
  410. package/src/runtime/routes/conversations-import-routes.ts +24 -7
  411. package/src/runtime/routes/documents-routes.ts +4 -0
  412. package/src/runtime/routes/domain-routes.ts +51 -37
  413. package/src/runtime/routes/epoch-millis-range.ts +34 -0
  414. package/src/runtime/routes/events-routes.ts +28 -34
  415. package/src/runtime/routes/gateway-log-routes.ts +26 -4
  416. package/src/runtime/routes/heartbeat-routes.ts +32 -12
  417. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  418. package/src/runtime/routes/host-cu-routes.ts +1 -1
  419. package/src/runtime/routes/identity-intro-cache.ts +11 -34
  420. package/src/runtime/routes/identity-routes.ts +224 -18
  421. package/src/runtime/routes/image-generation-routes.ts +40 -2
  422. package/src/runtime/routes/inbound-message-handler.ts +1 -1
  423. package/src/runtime/routes/index.ts +2 -0
  424. package/src/runtime/routes/integrations/a2a.ts +12 -10
  425. package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
  426. package/src/runtime/routes/integrations/slack/channel.ts +4 -0
  427. package/src/runtime/routes/integrations/slack/share.ts +27 -6
  428. package/src/runtime/routes/integrations/telegram.ts +6 -0
  429. package/src/runtime/routes/integrations/twilio.ts +42 -0
  430. package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
  431. package/src/runtime/routes/log-export-routes.ts +8 -0
  432. package/src/runtime/routes/memory-v2-routes.ts +15 -8
  433. package/src/runtime/routes/memory-v3-routes.ts +66 -34
  434. package/src/runtime/routes/oauth-apps.ts +66 -12
  435. package/src/runtime/routes/oauth-providers.ts +44 -5
  436. package/src/runtime/routes/platform-routes.ts +81 -5
  437. package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
  438. package/src/runtime/routes/playground/force-compact.ts +1 -1
  439. package/src/runtime/routes/playground/helpers.ts +1 -1
  440. package/src/runtime/routes/rename-conversation-routes.ts +5 -0
  441. package/src/runtime/routes/schedule-routes.ts +152 -42
  442. package/src/runtime/routes/secret-routes.ts +14 -2
  443. package/src/runtime/routes/skills-routes.ts +43 -14
  444. package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
  445. package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
  446. package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
  447. package/src/runtime/routes/trust-rules-routes.ts +26 -2
  448. package/src/runtime/routes/tts-routes.ts +35 -0
  449. package/src/runtime/routes/types.ts +66 -8
  450. package/src/runtime/routes/usage-routes.ts +47 -39
  451. package/src/runtime/routes/webhook-routes.ts +41 -2
  452. package/src/runtime/routes/work-items-routes.ts +2 -4
  453. package/src/runtime/routes/workspace-routes.ts +4 -0
  454. package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
  455. package/src/runtime/services/analyze-conversation.ts +2 -2
  456. package/src/runtime/services/conversation-serializer.ts +1 -1
  457. package/src/schedule/schedule-store.ts +20 -1
  458. package/src/schedule/schedule-usage-store.ts +83 -0
  459. package/src/schedule/scheduler.ts +12 -5
  460. package/src/signals/cancel.ts +2 -4
  461. package/src/skills/catalog-files.ts +2 -2
  462. package/src/skills/catalog-install.ts +3 -0
  463. package/src/skills/categories-cache.ts +118 -0
  464. package/src/skills/clawhub-files.ts +1 -2
  465. package/src/skills/skillssh-files.ts +1 -2
  466. package/src/subagent/manager.ts +17 -5
  467. package/src/telemetry/types.ts +29 -1
  468. package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
  469. package/src/telemetry/usage-telemetry-reporter.ts +57 -2
  470. package/src/tools/acp/context.ts +20 -0
  471. package/src/tools/acp/list-agents.test.ts +7 -1
  472. package/src/tools/acp/spawn.test.ts +158 -55
  473. package/src/tools/acp/spawn.ts +47 -72
  474. package/src/tools/acp/steer.test.ts +105 -8
  475. package/src/tools/acp/steer.ts +48 -17
  476. package/src/tools/apps/executors.ts +13 -8
  477. package/src/tools/executor.ts +1 -53
  478. package/src/tools/filesystem/write.ts +34 -0
  479. package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
  480. package/src/tools/network/__tests__/web-search.test.ts +11 -3
  481. package/src/tools/network/web-search-error.test.ts +248 -0
  482. package/src/tools/network/web-search-error.ts +267 -0
  483. package/src/tools/network/web-search.ts +207 -48
  484. package/src/tools/schedule/create.ts +2 -0
  485. package/src/tools/subagent/spawn.ts +2 -4
  486. package/src/tools/terminal/safe-env.ts +10 -1
  487. package/src/tools/ui-surface/definitions.ts +34 -5
  488. package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
  489. package/src/tts/provider-catalog.ts +76 -1
  490. package/src/util/mutex.ts +47 -0
  491. package/src/workspace/git-service.ts +1 -42
  492. package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
  493. package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
  494. package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
  495. package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
  496. package/src/workspace/migrations/registry.ts +6 -0
  497. package/docs/plugins.md +0 -836
  498. package/examples/plugins/echo/register.ts +0 -184
  499. package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
  500. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
  501. package/src/__tests__/compaction-pipeline.test.ts +0 -210
  502. package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
  503. package/src/__tests__/empty-response-pipeline.test.ts +0 -423
  504. package/src/__tests__/llm-call-pipeline.test.ts +0 -287
  505. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
  506. package/src/__tests__/persistence-pipeline.test.ts +0 -503
  507. package/src/__tests__/pipeline-runner.test.ts +0 -564
  508. package/src/__tests__/title-generate-pipeline.test.ts +0 -211
  509. package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
  510. package/src/__tests__/tool-error-pipeline.test.ts +0 -241
  511. package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
  512. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
  513. package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
  514. package/src/gallery/default-gallery.ts +0 -1359
  515. package/src/gallery/gallery-manifest.ts +0 -28
  516. package/src/home/feature-gate.ts +0 -22
  517. package/src/memory/v3/provider-blocks.ts +0 -16
  518. package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
  519. package/src/plugins/defaults/circuit-breaker/package.json +0 -15
  520. package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
  521. package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
  522. package/src/plugins/defaults/compaction/terminal.ts +0 -73
  523. package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
  524. package/src/plugins/defaults/empty-response/terminal.ts +0 -106
  525. package/src/plugins/defaults/injectors/package.json +0 -15
  526. package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
  527. package/src/plugins/defaults/llm-call/register.ts +0 -45
  528. package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
  529. package/src/plugins/defaults/memory-retrieval/package.json +0 -15
  530. package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
  531. package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
  532. package/src/plugins/defaults/overflow-reduce/package.json +0 -15
  533. package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
  534. package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
  535. package/src/plugins/defaults/persistence/package.json +0 -15
  536. package/src/plugins/defaults/persistence/register.ts +0 -38
  537. package/src/plugins/defaults/persistence/terminal.ts +0 -83
  538. package/src/plugins/defaults/title-generate/terminal.ts +0 -31
  539. package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
  540. package/src/plugins/defaults/token-estimate/package.json +0 -15
  541. package/src/plugins/defaults/token-estimate/register.ts +0 -34
  542. package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
  543. package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
  544. package/src/plugins/defaults/tool-error/terminal.ts +0 -47
  545. package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
  546. package/src/plugins/defaults/tool-execute/package.json +0 -15
  547. package/src/plugins/defaults/tool-execute/register.ts +0 -49
  548. package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
  549. package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
  550. package/src/skills/category-inference.ts +0 -111
  551. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
  552. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
  553. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
  554. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
  555. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
  556. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
  557. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
  558. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
  559. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
  560. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
  561. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
  562. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
  563. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
  564. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
  565. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
  566. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
  567. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
  568. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
  569. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
  570. /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
@@ -0,0 +1,490 @@
1
+ # Example — Habit Tracker
2
+
3
+ A 7-day habit grid: add habits, toggle a checkmark per day, delete habits. This is the
4
+ canonical **CRUD over a custom route** example — full create / read / update / delete
5
+ backed by a JSON file on disk, called from a multi-file TSX (`formatVersion: 2`) app via
6
+ `window.vellum.fetch`.
7
+
8
+ **What it demonstrates**
9
+
10
+ - A `routes/habits.ts` handler exporting `GET` / `POST` / `PATCH` / `DELETE`.
11
+ - Mutations addressed by `id` via a **query param** (`/v1/x/habits?id=…`) — route files
12
+ cannot use `[id].ts` path segments (see [CUSTOM_ROUTES.md](../CUSTOM_ROUTES.md)).
13
+ - A frontend that always checks `res.ok`, surfaces errors, and re-reads after every write
14
+ so the UI reflects persisted state rather than optimistic guesses.
15
+
16
+ ## File tree
17
+
18
+ ```
19
+ src/index.html
20
+ src/main.tsx
21
+ src/components/HabitTracker.tsx
22
+ src/components/HabitRow.tsx
23
+ src/styles.css
24
+ routes/habits.ts
25
+ ```
26
+
27
+ ## Route handler
28
+
29
+ ```typescript
30
+ // routes/habits.ts — Habit CRUD, persisted as a JSON file in the app workspace.
31
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
32
+ import { join } from "node:path";
33
+
34
+ export const description = "Habit CRUD — stores habits as a JSON file";
35
+
36
+ interface Habit {
37
+ id: string;
38
+ name: string;
39
+ completedDates: string[];
40
+ createdAt: string;
41
+ }
42
+
43
+ const DATA_DIR = join(process.env.VELLUM_WORKSPACE_DIR!, "data");
44
+ const DATA_FILE = join(DATA_DIR, "habits.json");
45
+
46
+ function loadHabits(): Habit[] {
47
+ mkdirSync(DATA_DIR, { recursive: true });
48
+ if (!existsSync(DATA_FILE)) return [];
49
+ return JSON.parse(readFileSync(DATA_FILE, "utf-8")) as Habit[];
50
+ }
51
+
52
+ function saveHabits(habits: Habit[]): void {
53
+ mkdirSync(DATA_DIR, { recursive: true });
54
+ writeFileSync(DATA_FILE, JSON.stringify(habits, null, 2));
55
+ }
56
+
57
+ export function GET(): Response {
58
+ return Response.json(loadHabits());
59
+ }
60
+
61
+ export async function POST(request: Request): Promise<Response> {
62
+ const body = (await request.json()) as { name?: unknown };
63
+ const name = typeof body.name === "string" ? body.name.trim() : "";
64
+ if (!name)
65
+ return Response.json({ error: "name is required" }, { status: 400 });
66
+
67
+ const habit: Habit = {
68
+ id: crypto.randomUUID(),
69
+ name,
70
+ completedDates: [],
71
+ createdAt: new Date().toISOString(),
72
+ };
73
+ const habits = loadHabits();
74
+ habits.push(habit);
75
+ saveHabits(habits);
76
+ return Response.json(habit, { status: 201 });
77
+ }
78
+
79
+ export async function PATCH(request: Request): Promise<Response> {
80
+ const id = new URL(request.url).searchParams.get("id");
81
+ if (!id) return Response.json({ error: "id is required" }, { status: 400 });
82
+
83
+ const body = (await request.json()) as { completedDates?: unknown };
84
+ if (!Array.isArray(body.completedDates)) {
85
+ return Response.json(
86
+ { error: "completedDates must be an array" },
87
+ { status: 400 },
88
+ );
89
+ }
90
+
91
+ const habits = loadHabits();
92
+ const habit = habits.find((h) => h.id === id);
93
+ if (!habit) return Response.json({ error: "not found" }, { status: 404 });
94
+
95
+ habit.completedDates = body.completedDates.filter(
96
+ (d): d is string => typeof d === "string",
97
+ );
98
+ saveHabits(habits);
99
+ return Response.json(habit);
100
+ }
101
+
102
+ export function DELETE(request: Request): Response {
103
+ const id = new URL(request.url).searchParams.get("id");
104
+ if (!id) return Response.json({ error: "id is required" }, { status: 400 });
105
+
106
+ const habits = loadHabits();
107
+ const next = habits.filter((h) => h.id !== id);
108
+ if (next.length === habits.length) {
109
+ return Response.json({ error: "not found" }, { status: 404 });
110
+ }
111
+ saveHabits(next);
112
+ return Response.json({ ok: true });
113
+ }
114
+ ```
115
+
116
+ ## Frontend
117
+
118
+ ```html
119
+ <!-- src/index.html -->
120
+ <!DOCTYPE html>
121
+ <html lang="en">
122
+ <head>
123
+ <meta charset="UTF-8" />
124
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
125
+ <title>Habit Tracker</title>
126
+ </head>
127
+ <body>
128
+ <div id="app"></div>
129
+ </body>
130
+ </html>
131
+ ```
132
+
133
+ ```tsx
134
+ // src/main.tsx
135
+ import { render } from "preact";
136
+ import { HabitTracker } from "./components/HabitTracker.js";
137
+ import "./styles.css";
138
+
139
+ render(<HabitTracker />, document.getElementById("app")!);
140
+ ```
141
+
142
+ ```tsx
143
+ // src/components/HabitTracker.tsx
144
+ import { useCallback, useEffect, useState } from "preact/hooks";
145
+ import { HabitRow } from "./HabitRow.js";
146
+
147
+ interface Habit {
148
+ id: string;
149
+ name: string;
150
+ completedDates: string[];
151
+ }
152
+
153
+ function getDates(): string[] {
154
+ const dates: string[] = [];
155
+ for (let i = 6; i >= 0; i--) {
156
+ const d = new Date();
157
+ d.setDate(d.getDate() - i);
158
+ dates.push(d.toISOString().slice(0, 10));
159
+ }
160
+ return dates;
161
+ }
162
+
163
+ function getDayNames(dates: string[]): string[] {
164
+ const names = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
165
+ return dates.map((d) => names[new Date(d + "T12:00:00").getDay()]);
166
+ }
167
+
168
+ export function HabitTracker() {
169
+ const [habits, setHabits] = useState<Habit[]>([]);
170
+ const [input, setInput] = useState("");
171
+ const [error, setError] = useState<string | null>(null);
172
+ const dates = getDates();
173
+ const dayNames = getDayNames(dates);
174
+
175
+ const loadHabits = useCallback(async () => {
176
+ try {
177
+ const res = await window.vellum.fetch("/v1/x/habits");
178
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
179
+ setHabits(await res.json());
180
+ setError(null);
181
+ } catch (e) {
182
+ setError("Couldn't load habits. Try again.");
183
+ console.error(e);
184
+ }
185
+ }, []);
186
+
187
+ useEffect(() => {
188
+ loadHabits();
189
+ }, [loadHabits]);
190
+
191
+ const addHabit = async () => {
192
+ const name = input.trim();
193
+ if (!name) return;
194
+ setInput("");
195
+ try {
196
+ const res = await window.vellum.fetch("/v1/x/habits", {
197
+ method: "POST",
198
+ headers: { "Content-Type": "application/json" },
199
+ body: JSON.stringify({ name }),
200
+ });
201
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
202
+ await loadHabits();
203
+ } catch (e) {
204
+ setError("Couldn't add habit. Try again.");
205
+ console.error(e);
206
+ }
207
+ };
208
+
209
+ const toggleDate = async (id: string, date: string) => {
210
+ const habit = habits.find((h) => h.id === id);
211
+ if (!habit) return;
212
+ const completed = habit.completedDates.includes(date)
213
+ ? habit.completedDates.filter((d) => d !== date)
214
+ : [...habit.completedDates, date];
215
+ try {
216
+ const res = await window.vellum.fetch(`/v1/x/habits?id=${id}`, {
217
+ method: "PATCH",
218
+ headers: { "Content-Type": "application/json" },
219
+ body: JSON.stringify({ completedDates: completed }),
220
+ });
221
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
222
+ await loadHabits();
223
+ } catch (e) {
224
+ setError("Couldn't update habit. Try again.");
225
+ console.error(e);
226
+ }
227
+ };
228
+
229
+ const deleteHabit = async (id: string) => {
230
+ try {
231
+ const res = await window.vellum.fetch(`/v1/x/habits?id=${id}`, {
232
+ method: "DELETE",
233
+ });
234
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
235
+ await loadHabits();
236
+ } catch (e) {
237
+ setError("Couldn't delete habit. Try again.");
238
+ console.error(e);
239
+ }
240
+ };
241
+
242
+ return (
243
+ <div>
244
+ <div class="header">
245
+ <h1>Habit Tracker</h1>
246
+ </div>
247
+ {error && <div class="error-banner">{error}</div>}
248
+ <div class="add-form">
249
+ <input
250
+ type="text"
251
+ value={input}
252
+ onInput={(e) => setInput((e.target as HTMLInputElement).value)}
253
+ onKeyDown={(e) => e.key === "Enter" && addHabit()}
254
+ placeholder="Add a new habit..."
255
+ />
256
+ <button class="btn-primary" onClick={addHabit}>
257
+ Add
258
+ </button>
259
+ </div>
260
+ <div class="days-header">
261
+ <div />
262
+ {dayNames.map((name, i) => (
263
+ <div key={i} class="day-label">
264
+ {name}
265
+ </div>
266
+ ))}
267
+ </div>
268
+ <div>
269
+ {habits.length === 0 ? (
270
+ <div class="empty-state">No habits yet. Add one above!</div>
271
+ ) : (
272
+ habits.map((habit) => (
273
+ <HabitRow
274
+ key={habit.id}
275
+ habit={habit}
276
+ dates={dates}
277
+ onToggle={toggleDate}
278
+ onDelete={deleteHabit}
279
+ />
280
+ ))
281
+ )}
282
+ </div>
283
+ </div>
284
+ );
285
+ }
286
+ ```
287
+
288
+ ```tsx
289
+ // src/components/HabitRow.tsx
290
+ interface Habit {
291
+ id: string;
292
+ name: string;
293
+ completedDates: string[];
294
+ }
295
+
296
+ interface HabitRowProps {
297
+ habit: Habit;
298
+ dates: string[];
299
+ onToggle: (id: string, date: string) => void;
300
+ onDelete: (id: string) => void;
301
+ }
302
+
303
+ export function HabitRow({ habit, dates, onToggle, onDelete }: HabitRowProps) {
304
+ return (
305
+ <div class="habit-row">
306
+ <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
307
+ <span class="habit-name">{habit.name}</span>
308
+ <button class="delete-btn" onClick={() => onDelete(habit.id)}>
309
+ x
310
+ </button>
311
+ </div>
312
+ {dates.map((date) => {
313
+ const checked = habit.completedDates.includes(date);
314
+ return (
315
+ <div key={date} class="check-cell">
316
+ <button
317
+ class={`check-btn${checked ? " checked" : ""}`}
318
+ onClick={() => onToggle(habit.id, date)}
319
+ >
320
+ {checked ? "\u2713" : ""}
321
+ </button>
322
+ </div>
323
+ );
324
+ })}
325
+ </div>
326
+ );
327
+ }
328
+ ```
329
+
330
+ ```css
331
+ /* src/styles.css */
332
+ :root {
333
+ --bg: #0f172a;
334
+ --surface: #1e293b;
335
+ --primary: #6366f1;
336
+ --primary-hover: #5558e6;
337
+ --success: #22c55e;
338
+ --text: #f1f5f9;
339
+ --text-secondary: #94a3b8;
340
+ --border: #334155;
341
+ --radius: 10px;
342
+ }
343
+ * {
344
+ margin: 0;
345
+ padding: 0;
346
+ box-sizing: border-box;
347
+ }
348
+ body {
349
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", sans-serif;
350
+ background: var(--bg);
351
+ color: var(--text);
352
+ padding: 24px;
353
+ min-height: 100vh;
354
+ }
355
+ .header {
356
+ display: flex;
357
+ justify-content: space-between;
358
+ align-items: center;
359
+ margin-bottom: 24px;
360
+ }
361
+ h1 {
362
+ font-size: 1.4rem;
363
+ font-weight: 600;
364
+ }
365
+ .error-banner {
366
+ background: rgba(239, 68, 68, 0.12);
367
+ color: #fca5a5;
368
+ padding: 10px 14px;
369
+ border-radius: var(--radius);
370
+ margin-bottom: 16px;
371
+ font-size: 0.85rem;
372
+ }
373
+ .add-form {
374
+ display: flex;
375
+ gap: 8px;
376
+ margin-bottom: 24px;
377
+ }
378
+ .add-form input {
379
+ flex: 1;
380
+ padding: 10px 14px;
381
+ background: var(--surface);
382
+ border: 1px solid var(--border);
383
+ border-radius: var(--radius);
384
+ color: var(--text);
385
+ font-family: inherit;
386
+ font-size: 0.9rem;
387
+ outline: none;
388
+ }
389
+ .add-form input:focus {
390
+ border-color: var(--primary);
391
+ }
392
+ .add-form input::placeholder {
393
+ color: var(--text-secondary);
394
+ }
395
+ button {
396
+ font-family: inherit;
397
+ font-size: 0.85rem;
398
+ font-weight: 500;
399
+ padding: 10px 18px;
400
+ border: none;
401
+ border-radius: var(--radius);
402
+ cursor: pointer;
403
+ transition: background 0.2s;
404
+ }
405
+ .btn-primary {
406
+ background: var(--primary);
407
+ color: white;
408
+ }
409
+ .btn-primary:hover {
410
+ background: var(--primary-hover);
411
+ }
412
+ .days-header {
413
+ display: grid;
414
+ grid-template-columns: 1fr repeat(7, 40px);
415
+ gap: 4px;
416
+ margin-bottom: 8px;
417
+ padding: 0 4px;
418
+ }
419
+ .day-label {
420
+ text-align: center;
421
+ font-size: 0.7rem;
422
+ color: var(--text-secondary);
423
+ text-transform: uppercase;
424
+ }
425
+ .habit-row {
426
+ display: grid;
427
+ grid-template-columns: 1fr repeat(7, 40px);
428
+ gap: 4px;
429
+ padding: 10px 4px;
430
+ border-radius: var(--radius);
431
+ margin-bottom: 4px;
432
+ align-items: center;
433
+ }
434
+ .habit-row:hover {
435
+ background: var(--surface);
436
+ }
437
+ .habit-name {
438
+ font-size: 0.9rem;
439
+ font-weight: 500;
440
+ overflow: hidden;
441
+ text-overflow: ellipsis;
442
+ white-space: nowrap;
443
+ }
444
+ .check-cell {
445
+ display: flex;
446
+ justify-content: center;
447
+ align-items: center;
448
+ }
449
+ .check-btn {
450
+ width: 28px;
451
+ height: 28px;
452
+ border-radius: 6px;
453
+ border: 2px solid var(--border);
454
+ background: transparent;
455
+ cursor: pointer;
456
+ padding: 0;
457
+ display: flex;
458
+ align-items: center;
459
+ justify-content: center;
460
+ transition: all 0.15s;
461
+ color: transparent;
462
+ font-size: 14px;
463
+ }
464
+ .check-btn.checked {
465
+ background: var(--success);
466
+ border-color: var(--success);
467
+ color: white;
468
+ }
469
+ .check-btn:hover {
470
+ border-color: var(--success);
471
+ }
472
+ .delete-btn {
473
+ background: transparent;
474
+ color: var(--text-secondary);
475
+ border: none;
476
+ padding: 4px 8px;
477
+ font-size: 0.8rem;
478
+ cursor: pointer;
479
+ border-radius: 4px;
480
+ }
481
+ .delete-btn:hover {
482
+ color: #ef4444;
483
+ background: rgba(239, 68, 68, 0.1);
484
+ }
485
+ .empty-state {
486
+ text-align: center;
487
+ padding: 48px 0;
488
+ color: var(--text-secondary);
489
+ }
490
+ ```
@@ -0,0 +1,62 @@
1
+ import * as appStore from "../../../../memory/app-store.js";
2
+ import type {
3
+ ToolContext,
4
+ ToolExecutionResult,
5
+ } from "../../../../tools/types.js";
6
+
7
+ interface AppListEntry {
8
+ app_id: string;
9
+ name: string;
10
+ description?: string;
11
+ updated_at: number;
12
+ created_at: number;
13
+ }
14
+
15
+ function toEntry(app: appStore.AppDefinition): AppListEntry {
16
+ return {
17
+ app_id: app.id,
18
+ name: app.name,
19
+ ...(app.description ? { description: app.description } : {}),
20
+ updated_at: app.updatedAt,
21
+ created_at: app.createdAt,
22
+ };
23
+ }
24
+
25
+ export async function run(
26
+ input: Record<string, unknown>,
27
+ _context: ToolContext,
28
+ ): Promise<ToolExecutionResult> {
29
+ const apps = appStore.listApps();
30
+ const entries = apps.map(toEntry);
31
+
32
+ const query =
33
+ typeof input.query === "string" ? input.query.trim().toLowerCase() : "";
34
+
35
+ if (!query) {
36
+ return {
37
+ content: JSON.stringify({ count: entries.length, apps: entries }),
38
+ isError: false,
39
+ };
40
+ }
41
+
42
+ // Resolve the app the user mentioned by name. Prefer an exact (case-insensitive)
43
+ // name match, then fall back to substring matches in either direction so
44
+ // "habit tracker" resolves "Habit Tracker" and "my budget" resolves "Budget".
45
+ const exact = entries.filter((e) => e.name.toLowerCase() === query);
46
+ const matches =
47
+ exact.length > 0
48
+ ? exact
49
+ : entries.filter((e) => {
50
+ const name = e.name.toLowerCase();
51
+ return name.includes(query) || query.includes(name);
52
+ });
53
+
54
+ return {
55
+ content: JSON.stringify({
56
+ query: input.query,
57
+ match_count: matches.length,
58
+ matches,
59
+ }),
60
+ isError: false,
61
+ };
62
+ }
@@ -1,44 +1,37 @@
1
1
  ---
2
2
  name: document-editor
3
- description: Rich text document editor with collaborative editing tools create, read, update, and annotate documents
3
+ description: Use whenever the user wants to write or draft an article, blog post, essay, report, or any long-form content. Creates the content in a rich text editor instead of dumping it in chat, so it can be streamed, reviewed, edited, and exported.
4
4
  compatibility: "Designed for Vellum personal assistants"
5
5
  metadata:
6
- emoji: "📄"
6
+ emoji: "✍️"
7
7
  vellum:
8
- display-name: "Document Editor"
8
+ display-name: "Document Writer"
9
9
  activation-hints:
10
- - "User asks to write, draft, or collaborate on long-form content — use the document editor for a better editing experience"
11
- - "When content will be iterated on, reviewed, or exported, prefer the document editor over inline markdown"
12
- - "When a file attachment contains a draft or document the user wants to iterate on, open it in the editor"
10
+ - "User asks to write, draft, or compose an article, blog post, essay, report, story, or any long-form content — always create it in the document editor, not as a chat reply"
11
+ - "User wants written content they will iterate on, review, or export use the editor instead of inline markdown"
12
+ - "A file attachment contains a draft or document the user wants to revise open it in the editor"
13
+ avoid-when:
14
+ - "The user wants an interactive app, dashboard, calculator, game, or anything with state or data — use app-builder instead"
15
+ - "A one or two sentence answer is enough — just reply in chat"
13
16
  ---
14
17
 
15
- Create and edit long-form documents using the built-in rich text editor. Documents open in workspace mode with chat docked to the side.
18
+ Write and edit long-form documents using the built-in rich text editor. Documents open in workspace mode with chat docked to the side. When a request is about writing prose (an article, blog post, report, essay, story, or similar), create it here rather than writing it into the chat response.
16
19
 
17
20
  ## Tools
18
21
 
19
- - **document_open** - Opens an existing document in the editor panel by `surface_id`. Use this when a document exists but isn't visible in the editor — for example after the user switches devices, refreshes the page, or when the editor panel was closed. Fetches the document from storage and sends it to the client.
20
- - **document_create** - Opens a new document editor with an optional title and initial Markdown content. Returns a `surface_id` for subsequent updates.
22
+ - **document_create** - Opens a new document editor with an optional title and initial Markdown content. Returns a `surface_id` for subsequent updates. This is the entry point for any new piece of writing.
21
23
  - **document_update** - Updates content in an open document editor by `surface_id`. Supports `replace` (overwrite) and `append` (add to end) modes.
22
24
  - **document_read** - Reads the current content of a document by `surface_id` when it belongs to the current conversation, or when the current actor is the guardian/local user. Use to verify content before editing.
23
- - **document_list** - Lists documents. Without `query`, lists the current conversation's documents. With `query`, searches by title; guardian/local users can search across conversations, while other actors are scoped to the current conversation.
24
25
  - **document_find** - Searches a document for text or regex patterns. Returns matching lines with line numbers, match positions, and matched text.
25
26
  - **document_replace_text** - Targeted find-and-replace within a document. Supports literal and regex patterns (with backreferences). Optionally limit the number of replacements.
27
+ - **document_list** - Lists documents. Without `query`, lists the current conversation's documents. With `query`, searches by title; guardian/local users can search across conversations, while other actors are scoped to the current conversation.
28
+ - **document_open** - Opens an existing document in the editor panel by `surface_id`. Use this when a document exists but isn't visible in the editor — for example after the user switches devices, refreshes the page, or when the editor panel was closed. Fetches the document from storage and sends it to the client.
26
29
  - **document_delete** - Deletes a document by `surface_id`. Use to clean up unwanted documents.
27
30
 
28
- ## Retrieving existing documents
29
-
30
- When the user asks to see, open, or pull up a document:
31
-
32
- 1. Check the `<active_documents>` block in your context — it lists all documents in this conversation with their `surface_id` and title.
33
- 2. If the document is NOT in `<active_documents>`, call `document_list` with a `query` matching the document title. For guardian/local users, this searches across previous conversations and sessions.
34
- 3. Once you have the `surface_id`, call `document_open` to open the editor panel. This both surfaces the editor on the client and returns the document content. If the user only needs the text (not the editor), use `document_read` instead.
35
-
36
- **Never** search the filesystem, conversation history, or archives to find a document. Always use `document_list` with a `query`.
37
-
38
- **If the user says they can't see a document you know exists** (e.g. after switching from macOS to web, or after a page refresh), call `document_open` with the `surface_id` to re-surface the editor panel on their current client.
39
-
40
31
  ## Creating a new document
41
32
 
33
+ This is the default path when the user asks you to write something.
34
+
42
35
  1. **Create the document**: Call `document_create` with a title (inferred from the request). Call the tool immediately, not after conversational preamble. Capture the `surface_id` from the response — every subsequent `document_update` call must reference it.
43
36
  2. **Write content in Markdown**: Use proper structure (`#` for titles, `##` for sections), **bold**, _italic_, code blocks, tables, lists, blockquotes as appropriate.
44
37
  3. **CRITICAL - Stream content in chunks**: Call `document_update` MULTIPLE times, not just once. Break content into logical chunks (paragraphs, sections, or every 200-300 words). Call `document_update` with `mode: "append"` for EACH chunk separately. The user experiences real-time content appearing as you write.
@@ -59,6 +52,18 @@ When the user requests changes to a document:
59
52
  - `document_find` + `document_replace_text` — **for everything else**. Fixing typos, renaming terms, swapping sections, reordering content, adjusting formatting, or any edit that touches only part of the document. This is the default choice for edits. It avoids rewriting the entire document and eliminates the risk of accidentally dropping content.
60
53
  4. **Do NOT use `document_update` with `mode: "replace"` for targeted edits.** Rewriting the entire document to change a few words or rearrange sections is wasteful and error-prone.
61
54
 
55
+ ## Retrieving existing documents
56
+
57
+ When the user asks to see, open, or pull up a document:
58
+
59
+ 1. Check the `<active_documents>` block in your context — it lists all documents in this conversation with their `surface_id` and title.
60
+ 2. If the document is NOT in `<active_documents>`, call `document_list` with a `query` matching the document title. For guardian/local users, this searches across previous conversations and sessions.
61
+ 3. Once you have the `surface_id`, call `document_open` to open the editor panel. This surfaces the editor on the client and returns document metadata (`surface_id`, `title`, `word_count`) — not the full content. If you need the actual document text, follow up with `document_read`.
62
+
63
+ **Never** search the filesystem, conversation history, or archives to find a document. Always use `document_list` with a `query`.
64
+
65
+ **If the user says they can't see a document you know exists** (e.g. after switching from macOS to web, or after a page refresh), call `document_open` with the `surface_id` to re-surface the editor panel on their current client.
66
+
62
67
  ## Find & Replace
63
68
 
64
69
  Use `document_find` and `document_replace_text` for surgical edits that target specific text patterns without rewriting the entire document.
@@ -117,7 +122,7 @@ Users can leave inline comments on documents. Open comments are surfaced in a `<
117
122
  ## Anti-Patterns
118
123
 
119
124
  - **Don't use `app_create` for blog posts, articles, or written content.** Use `document_create` — apps are for interactive content with state/data.
120
- - **Don't output the full content in chat.** The content goes in the document editor, not in the chat response. Acknowledge what you're doing and stream to the editor.
125
+ - **Don't write the article into the chat response.** Long-form prose goes in the document editor via `document_create`, not in chat and not into a `.md` file in the workspace. Acknowledge what you're doing and stream to the editor.
121
126
  - **Don't wait to generate everything before sending.** Stream content in chunks via `document_update` with `mode: "append"` so users see progress in real time.
122
127
 
123
128
  ## Usage Notes
@@ -42,7 +42,7 @@
42
42
  },
43
43
  {
44
44
  "name": "document_update",
45
- "description": "Update content in an open document editor. Use this to stream generated content or apply edits.",
45
+ "description": "Append to or fully replace a document's content. Takes the full Markdown in `content` plus a `mode`: \"append\" adds it to the end (use this to stream generated content in chunks), \"replace\" overwrites the entire document. This tool does NOT do targeted edits — for changing specific words, phrases, or sections, use document_replace_text instead. There is no old/new or find/replace parameter here.",
46
46
  "category": "document-editor",
47
47
  "risk": "low",
48
48
  "input_schema": {
@@ -27,13 +27,6 @@ When the user mentions "email" - sending, reading, checking, decluttering, draft
27
27
 
28
28
  Do not offer the assistant's own email as an option unless the user specifically asks. If Gmail and Outlook are not connected, guide them through setup.
29
29
 
30
- ## Communication Style
31
-
32
- - **Be action-oriented.** When the user asks to do something ("declutter", "check my email"), start doing it immediately. Don't ask for permission to read their inbox - that's obviously what they want.
33
- - **Keep it human.** Never mention OAuth, tokens, APIs, sandboxes, credential proxies, or other technical internals. If something isn't working, say "Gmail needs to be reconnected" - not "the OAuth2 access token for google has expired."
34
- - **Show progress.** When running a tool that scans many emails, tell the user what you're doing: "Scanning your inbox for clutter..." Don't go silent.
35
- - **Be brief and warm.** One or two sentences per update is plenty. Don't over-explain what you're about to do - just do it and narrate lightly.
36
-
37
30
  When a platform is connected (auth test succeeds), always use the messaging API tools for that platform. Never fall back to browser automation, shell commands (bash, curl), or any other approach for operations that messaging tools can handle. The messaging tools handle authentication internally - never try to access tokens or call APIs directly. Browser automation is only appropriate for initial credential setup (OAuth consent screens), not for day-to-day messaging operations.
38
31
 
39
32
  **Exception: Slack.** Slack messaging should use the Slack Web API directly via CLI, not messaging tools. See the **slack** skill for details.
@@ -23,6 +23,7 @@ import * as acpSteer from "./bundled-skills/acp/tools/acp-steer.js";
23
23
  import * as appCreate from "./bundled-skills/app-builder/tools/app-create.js";
24
24
  import * as appDelete from "./bundled-skills/app-builder/tools/app-delete.js";
25
25
  import * as appGenerateIcon from "./bundled-skills/app-builder/tools/app-generate-icon.js";
26
+ import * as appList from "./bundled-skills/app-builder/tools/app-list.js";
26
27
  import * as appRefresh from "./bundled-skills/app-builder/tools/app-refresh.js";
27
28
  // ── app-control ────────────────────────────────────────────────────────────────
28
29
  import * as appControlClick from "./bundled-skills/app-control/tools/app-control-click.js";
@@ -142,6 +143,7 @@ export const bundledToolRegistry = new Map<string, SkillToolScript>([
142
143
  ["app-builder:tools/app-delete.ts", appDelete],
143
144
  ["app-builder:tools/app-refresh.ts", appRefresh],
144
145
  ["app-builder:tools/app-generate-icon.ts", appGenerateIcon],
146
+ ["app-builder:tools/app-list.ts", appList],
145
147
 
146
148
  // app-control
147
149
  ["app-control:tools/app-control-start.ts", appControlStart],