@vellumai/assistant 0.8.1 → 0.8.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 (506) hide show
  1. package/ARCHITECTURE.md +2 -7
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +5 -0
  5. package/docker-init-apt-root.sh +94 -0
  6. package/docker-kata-apt-env.sh +39 -0
  7. package/docs/plugins.md +88 -47
  8. package/docs/skills.md +9 -7
  9. package/examples/plugins/echo/README.md +27 -27
  10. package/examples/plugins/echo/package.json +3 -0
  11. package/examples/plugins/echo/register.ts +31 -31
  12. package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
  13. package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
  14. package/openapi.yaml +325 -3
  15. package/package.json +3 -1
  16. package/scripts/generate-openapi.ts +83 -10
  17. package/scripts/sync-llm-catalog.ts +2 -2
  18. package/scripts/sync-web-search-catalog.ts +47 -25
  19. package/src/__tests__/agent-image-optimize.test.ts +11 -3
  20. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  21. package/src/__tests__/anthropic-provider.test.ts +45 -0
  22. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  23. package/src/__tests__/app-executors.test.ts +220 -4
  24. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  25. package/src/__tests__/bundled-asset.test.ts +6 -6
  26. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  27. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  28. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  29. package/src/__tests__/clawhub.test.ts +75 -16
  30. package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
  31. package/src/__tests__/config-schema.test.ts +21 -0
  32. package/src/__tests__/config-set-route.test.ts +80 -0
  33. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  34. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  35. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  36. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  37. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  38. package/src/__tests__/context-token-estimator.test.ts +1 -0
  39. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  40. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  41. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  42. package/src/__tests__/conversation-agent-loop.test.ts +2 -0
  43. package/src/__tests__/conversation-error.test.ts +42 -3
  44. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  45. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  46. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  47. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  48. package/src/__tests__/conversation-pairing.test.ts +54 -0
  49. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  50. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  51. package/src/__tests__/conversation-queue.test.ts +4 -1
  52. package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
  53. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  54. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  55. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  56. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  57. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  58. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  59. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  60. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  61. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  62. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  63. package/src/__tests__/dm-backfill.test.ts +121 -10
  64. package/src/__tests__/document-tool-security.test.ts +258 -0
  65. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  66. package/src/__tests__/edit-propagation.test.ts +33 -0
  67. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  68. package/src/__tests__/external-plugin-loader.test.ts +60 -36
  69. package/src/__tests__/filing-service.test.ts +140 -0
  70. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  71. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  72. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  73. package/src/__tests__/helpers/wait-for.ts +21 -0
  74. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  75. package/src/__tests__/history-repair.test.ts +73 -0
  76. package/src/__tests__/host-app-control-proxy.test.ts +266 -10
  77. package/src/__tests__/image-credentials.test.ts +1 -1
  78. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  79. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  80. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  81. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  82. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  83. package/src/__tests__/injector-chain.test.ts +10 -8
  84. package/src/__tests__/install-skill-routing.test.ts +155 -37
  85. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
  86. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  87. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  88. package/src/__tests__/llm-catalog-parity.test.ts +55 -13
  89. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
  90. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  91. package/src/__tests__/llm-usage-store.test.ts +114 -0
  92. package/src/__tests__/managed-profile-guard.test.ts +31 -29
  93. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  94. package/src/__tests__/managed-store.test.ts +84 -192
  95. package/src/__tests__/media-generate-image.test.ts +1 -1
  96. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  97. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  98. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  99. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  100. package/src/__tests__/openai-provider.test.ts +24 -0
  101. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  102. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  103. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  104. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
  105. package/src/__tests__/platform.test.ts +2 -0
  106. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  107. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  108. package/src/__tests__/plugin-external-api.test.ts +68 -0
  109. package/src/__tests__/plugin-registry.test.ts +0 -77
  110. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  111. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  112. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  113. package/src/__tests__/plugin-types.test.ts +3 -13
  114. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  115. package/src/__tests__/process-message-display-content.test.ts +421 -0
  116. package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
  117. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  118. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
  119. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  120. package/src/__tests__/schedule-routes.test.ts +50 -3
  121. package/src/__tests__/schedule-store.test.ts +94 -0
  122. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  123. package/src/__tests__/schema-transforms.test.ts +20 -0
  124. package/src/__tests__/search-skills-unified.test.ts +0 -5
  125. package/src/__tests__/server-history-render.test.ts +43 -0
  126. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  127. package/src/__tests__/skill-load-tool.test.ts +27 -89
  128. package/src/__tests__/skill-memory.test.ts +23 -3
  129. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  130. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  131. package/src/__tests__/skills-install-extract.test.ts +49 -38
  132. package/src/__tests__/skills-install-staging.test.ts +159 -0
  133. package/src/__tests__/skills-uninstall.test.ts +9 -41
  134. package/src/__tests__/skills.test.ts +51 -58
  135. package/src/__tests__/slack-channel-config.test.ts +9 -0
  136. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  137. package/src/__tests__/system-prompt.test.ts +737 -63
  138. package/src/__tests__/terminal-tools.test.ts +28 -1
  139. package/src/__tests__/thread-backfill.test.ts +557 -27
  140. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  141. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  142. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  143. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  144. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  145. package/src/__tests__/tool-executor.test.ts +16 -4
  146. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  147. package/src/__tests__/turn-events-store.test.ts +256 -0
  148. package/src/__tests__/twilio-routes.test.ts +4 -0
  149. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  150. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  151. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  152. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  153. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  154. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  155. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  156. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  157. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  158. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  159. package/src/acp/resolve-agent.ts +1 -1
  160. package/src/agent/image-optimize.ts +13 -5
  161. package/src/calls/voice-session-bridge.ts +61 -42
  162. package/src/channels/types.ts +108 -0
  163. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  164. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  165. package/src/cli/commands/__tests__/schedules.test.ts +491 -0
  166. package/src/cli/commands/changelog.ts +106 -42
  167. package/src/cli/commands/conversations.ts +102 -17
  168. package/src/cli/commands/default-action.ts +10 -53
  169. package/src/cli/commands/notifications.ts +329 -317
  170. package/src/cli/commands/plugins.ts +185 -0
  171. package/src/cli/commands/schedules.ts +391 -0
  172. package/src/cli/commands/telemetry.ts +40 -0
  173. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  174. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  175. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  176. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  177. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  178. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  179. package/src/cli/lib/cli-colors.ts +12 -0
  180. package/src/cli/lib/confirm-prompt.ts +79 -0
  181. package/src/cli/lib/install-from-github.ts +304 -0
  182. package/src/cli/lib/list-installed-plugins.ts +137 -0
  183. package/src/cli/lib/uninstall-plugin.ts +82 -0
  184. package/src/cli/lib/unknown-command.ts +111 -0
  185. package/src/cli/program.ts +38 -2
  186. package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
  187. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  188. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  189. package/src/config/bundled-skills/document/SKILL.md +23 -3
  190. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  191. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  192. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  193. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  194. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  195. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  196. package/src/config/bundled-tool-registry.ts +6 -0
  197. package/src/config/feature-flag-registry.json +41 -1
  198. package/src/config/loader.ts +64 -38
  199. package/src/config/schema.ts +7 -10
  200. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  201. package/src/config/schemas/channels.ts +8 -0
  202. package/src/config/schemas/compaction.ts +28 -0
  203. package/src/config/schemas/heartbeat.ts +9 -0
  204. package/src/config/schemas/llm-request-logs.ts +31 -7
  205. package/src/config/schemas/llm.ts +3 -0
  206. package/src/config/schemas/memory-retrieval.ts +18 -0
  207. package/src/config/schemas/tools.ts +14 -0
  208. package/src/config/skills.ts +3 -96
  209. package/src/context/compactor.ts +1047 -0
  210. package/src/context/token-estimator.ts +2 -2
  211. package/src/context/window-manager.ts +197 -1520
  212. package/src/credential-execution/managed-catalog.ts +37 -0
  213. package/src/credential-health/credential-health-service.ts +280 -19
  214. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
  215. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  216. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  217. package/src/daemon/approval-generators.ts +8 -6
  218. package/src/daemon/config-watcher.ts +94 -31
  219. package/src/daemon/conversation-agent-loop.ts +169 -9
  220. package/src/daemon/conversation-error.ts +171 -37
  221. package/src/daemon/conversation-lifecycle.ts +53 -40
  222. package/src/daemon/conversation-messaging.ts +25 -6
  223. package/src/daemon/conversation-process.ts +49 -12
  224. package/src/daemon/conversation-runtime-assembly.ts +16 -1
  225. package/src/daemon/conversation-slash.ts +12 -5
  226. package/src/daemon/conversation-store.ts +11 -4
  227. package/src/daemon/conversation-tool-setup.ts +39 -7
  228. package/src/daemon/conversation.ts +33 -1
  229. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  230. package/src/daemon/first-greeting.ts +22 -2
  231. package/src/daemon/handlers/config-model.ts +6 -5
  232. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  233. package/src/daemon/handlers/shared.ts +14 -5
  234. package/src/daemon/handlers/skills.ts +111 -108
  235. package/src/daemon/history-repair.ts +28 -1
  236. package/src/daemon/host-app-control-proxy.ts +98 -23
  237. package/src/daemon/lifecycle.ts +45 -35
  238. package/src/daemon/meet-host-supervisor.ts +5 -4
  239. package/src/daemon/memory-v2-startup.ts +49 -0
  240. package/src/daemon/message-protocol.ts +1 -0
  241. package/src/daemon/message-types/conversations.ts +25 -0
  242. package/src/daemon/message-types/messages.ts +61 -0
  243. package/src/daemon/message-types/subagents.ts +1 -0
  244. package/src/daemon/message-types/sync.ts +1 -0
  245. package/src/daemon/pkb-reminder-builder.test.ts +1 -1
  246. package/src/daemon/pkb-reminder-builder.ts +1 -1
  247. package/src/daemon/plugin-source-watcher.ts +146 -0
  248. package/src/daemon/process-message.ts +21 -3
  249. package/src/daemon/server.ts +11 -2
  250. package/src/daemon/skill-memory-refresh.ts +29 -0
  251. package/src/documents/document-store.ts +221 -3
  252. package/src/embedded/plugin-api.ts +40 -0
  253. package/src/filing/filing-service.ts +39 -0
  254. package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
  255. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  256. package/src/heartbeat/heartbeat-service.ts +41 -0
  257. package/src/home/__tests__/feed-types.test.ts +40 -0
  258. package/src/home/feed-types.ts +22 -0
  259. package/src/home/post-connect-feed.ts +1 -0
  260. package/src/index.ts +18 -1
  261. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  262. package/src/mcp/client.ts +20 -4
  263. package/src/media/image-credentials.ts +3 -3
  264. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  265. package/src/memory/__tests__/conversation-queries.test.ts +263 -0
  266. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  267. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  268. package/src/memory/__tests__/message-content.test.ts +35 -0
  269. package/src/memory/bookmark-crud.ts +42 -10
  270. package/src/memory/context-search/sources/conversations.ts +62 -2
  271. package/src/memory/context-search/sources/workspace.ts +4 -0
  272. package/src/memory/conversation-crud.ts +63 -19
  273. package/src/memory/conversation-queries.ts +110 -10
  274. package/src/memory/db-init.ts +6 -0
  275. package/src/memory/delivery-crud.ts +152 -5
  276. package/src/memory/embedding-backend.ts +4 -4
  277. package/src/memory/external-conversation-store.ts +66 -5
  278. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
  279. package/src/memory/graph/conversation-graph-memory.ts +31 -15
  280. package/src/memory/graph/tools.ts +3 -3
  281. package/src/memory/indexer.ts +34 -29
  282. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  283. package/src/memory/jobs/embed-concept-page.ts +20 -11
  284. package/src/memory/jobs-worker.ts +6 -1
  285. package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
  286. package/src/memory/llm-request-log-source.ts +19 -52
  287. package/src/memory/llm-usage-store.ts +125 -5
  288. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  289. package/src/memory/message-content.ts +1 -1
  290. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  291. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  292. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  293. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  294. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  295. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  296. package/src/memory/migrations/index.ts +6 -0
  297. package/src/memory/migrations/registry.ts +8 -0
  298. package/src/memory/onboarding-events-store.ts +106 -0
  299. package/src/memory/schema/bookmarks.ts +0 -2
  300. package/src/memory/schema/calls.ts +1 -0
  301. package/src/memory/schema/inference.ts +1 -3
  302. package/src/memory/schema/infrastructure.ts +12 -0
  303. package/src/memory/turn-events-store.ts +127 -2
  304. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  305. package/src/memory/v2/__tests__/injection.test.ts +98 -8
  306. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  307. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  308. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  309. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  310. package/src/memory/v2/__tests__/router.test.ts +15 -0
  311. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  312. package/src/memory/v2/injection.ts +32 -6
  313. package/src/memory/v2/migration.ts +49 -19
  314. package/src/memory/v2/page-index.ts +35 -5
  315. package/src/memory/v2/prompts/router.ts +11 -8
  316. package/src/memory/v2/prompts/sweep.ts +2 -2
  317. package/src/memory/v2/qdrant.ts +135 -7
  318. package/src/memory/v2/router.ts +9 -8
  319. package/src/memory/v2/skill-store.ts +120 -35
  320. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  321. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  322. package/src/messaging/providers/slack/adapter.ts +43 -5
  323. package/src/messaging/providers/slack/client.ts +27 -0
  324. package/src/messaging/providers/slack/deep-link.ts +65 -0
  325. package/src/messaging/providers/slack/download.ts +104 -0
  326. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  327. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  328. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  329. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  330. package/src/messaging/providers/slack/types.ts +20 -1
  331. package/src/notifications/conversation-pairing.ts +2 -1
  332. package/src/notifications/decision-engine.ts +2 -1
  333. package/src/notifications/emit-signal.ts +20 -1
  334. package/src/notifications/home-feed-side-effect.ts +54 -0
  335. package/src/notifications/signal.ts +3 -1
  336. package/src/oauth/connection-resolver.ts +8 -4
  337. package/src/oauth/platform-connection.ts +6 -2
  338. package/src/oauth/seed-providers.ts +10 -1
  339. package/src/permissions/checker.ts +2 -0
  340. package/src/permissions/ipc-risk-types.ts +1 -0
  341. package/src/permissions/question-prompter.test.ts +416 -0
  342. package/src/permissions/question-prompter.ts +294 -0
  343. package/src/platform/client.test.ts +1 -1
  344. package/src/platform/client.ts +1 -1
  345. package/src/plugin-api/constants.ts +26 -0
  346. package/src/plugin-api/index.ts +34 -1
  347. package/src/plugin-api/types.ts +104 -22
  348. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  349. package/src/plugins/defaults/compaction.ts +0 -4
  350. package/src/plugins/defaults/empty-response.ts +0 -2
  351. package/src/plugins/defaults/history-repair.ts +0 -2
  352. package/src/plugins/defaults/injectors.ts +36 -3
  353. package/src/plugins/defaults/llm-call.ts +0 -2
  354. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  355. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  356. package/src/plugins/defaults/persistence.ts +0 -2
  357. package/src/plugins/defaults/title-generate.ts +0 -5
  358. package/src/plugins/defaults/token-estimate.ts +0 -2
  359. package/src/plugins/defaults/tool-error.ts +0 -7
  360. package/src/plugins/defaults/tool-execute.ts +0 -2
  361. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  362. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  363. package/src/plugins/external-api.ts +104 -0
  364. package/src/plugins/external-plugin-loader.ts +105 -32
  365. package/src/plugins/feature-gate.ts +22 -0
  366. package/src/plugins/pipeline.ts +37 -0
  367. package/src/plugins/registry.ts +48 -80
  368. package/src/plugins/types.ts +31 -26
  369. package/src/plugins/user-loader.ts +21 -2
  370. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  371. package/src/proactive-artifact/job.test.ts +37 -5
  372. package/src/prompts/__tests__/system-prompt.test.ts +12 -0
  373. package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
  374. package/src/prompts/normalize-onboarding.ts +27 -0
  375. package/src/prompts/sections.ts +302 -0
  376. package/src/prompts/system-prompt.ts +63 -166
  377. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  378. package/src/prompts/templates/system-sections.ts +173 -0
  379. package/src/providers/__tests__/inference.test.ts +22 -7
  380. package/src/providers/anthropic/client.ts +28 -28
  381. package/src/providers/connection-resolution.ts +7 -0
  382. package/src/providers/inference/adapter-factory.ts +41 -4
  383. package/src/providers/inference/connections.ts +74 -29
  384. package/src/providers/inference/resolve-auth.ts +12 -4
  385. package/src/providers/model-catalog.ts +294 -12
  386. package/src/providers/openai/chat-completions-provider.ts +10 -2
  387. package/src/providers/openrouter/client.ts +7 -0
  388. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
  389. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  390. package/src/providers/provider-availability.ts +17 -2
  391. package/src/providers/provider-catalog-visibility.ts +36 -0
  392. package/src/providers/registry.ts +22 -14
  393. package/src/providers/retry.ts +47 -1
  394. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  395. package/src/runtime/agent-wake.ts +42 -14
  396. package/src/runtime/auth/route-policy.ts +8 -1
  397. package/src/runtime/btw-sidechain.ts +2 -0
  398. package/src/runtime/http-types.ts +19 -0
  399. package/src/runtime/migrations/origin-mode.ts +1 -1
  400. package/src/runtime/pending-interactions.ts +1 -0
  401. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  402. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  403. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
  404. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  405. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  406. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  407. package/src/runtime/routes/acp-routes.ts +5 -3
  408. package/src/runtime/routes/auth-routes.ts +1 -1
  409. package/src/runtime/routes/bookmark-routes.ts +5 -3
  410. package/src/runtime/routes/btw-routes.ts +5 -1
  411. package/src/runtime/routes/channel-availability-routes.ts +121 -0
  412. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  413. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  414. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  415. package/src/runtime/routes/conversation-query-routes.ts +40 -35
  416. package/src/runtime/routes/conversation-routes.ts +90 -11
  417. package/src/runtime/routes/documents-routes.ts +25 -86
  418. package/src/runtime/routes/group-routes.ts +5 -0
  419. package/src/runtime/routes/inbound-conversation.ts +28 -8
  420. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  421. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  422. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  423. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  424. package/src/runtime/routes/index.ts +6 -0
  425. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  426. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  427. package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
  428. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  429. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  430. package/src/runtime/routes/integrations/twilio.ts +6 -13
  431. package/src/runtime/routes/notification-routes.ts +1 -1
  432. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  433. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  434. package/src/runtime/routes/question-routes.ts +259 -0
  435. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  436. package/src/runtime/routes/schedule-routes.ts +4 -7
  437. package/src/runtime/routes/subagents-routes.ts +57 -18
  438. package/src/runtime/routes/telemetry-routes.ts +27 -0
  439. package/src/runtime/routes/tts-routes.ts +27 -2
  440. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  441. package/src/runtime/routes/workspace-routes.ts +28 -0
  442. package/src/runtime/services/conversation-serializer.ts +39 -7
  443. package/src/runtime/sync/resource-sync-events.ts +93 -1
  444. package/src/schedule/schedule-store.ts +27 -2
  445. package/src/schedule/scheduler.ts +9 -1
  446. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  447. package/src/security/untrusted-content.ts +93 -8
  448. package/src/skills/catalog-files.ts +1 -1
  449. package/src/skills/catalog-install.ts +233 -116
  450. package/src/skills/clawhub.ts +70 -13
  451. package/src/skills/managed-store.ts +4 -119
  452. package/src/skills/skillssh-registry.ts +27 -48
  453. package/src/subagent/manager.ts +15 -7
  454. package/src/telemetry/types.ts +113 -1
  455. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  456. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  457. package/src/tools/apps/executors.ts +58 -7
  458. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  459. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  460. package/src/tools/browser/browser-execution.ts +15 -11
  461. package/src/tools/computer-use/definitions.ts +3 -3
  462. package/src/tools/credentials/vault.ts +1 -1
  463. package/src/tools/document/document-tool.ts +124 -1
  464. package/src/tools/filesystem/edit.ts +1 -1
  465. package/src/tools/filesystem/list.ts +1 -1
  466. package/src/tools/filesystem/read.ts +1 -1
  467. package/src/tools/filesystem/write.ts +5 -2
  468. package/src/tools/host-filesystem/transfer.ts +1 -1
  469. package/src/tools/host-terminal/host-shell.ts +1 -1
  470. package/src/tools/permission-checker.ts +1 -1
  471. package/src/tools/registry.ts +17 -7
  472. package/src/tools/schedule/create.ts +2 -2
  473. package/src/tools/schema-transforms.ts +7 -2
  474. package/src/tools/side-effects.ts +1 -0
  475. package/src/tools/skills/delete-managed.ts +4 -4
  476. package/src/tools/skills/execute.ts +1 -1
  477. package/src/tools/skills/scaffold-managed.ts +3 -2
  478. package/src/tools/subagent/notify-parent.ts +1 -1
  479. package/src/tools/system/request-permission.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +60 -1
  481. package/src/tools/tool-manifest.ts +2 -0
  482. package/src/tools/types.ts +72 -21
  483. package/src/tools/ui-surface/definitions.ts +6 -5
  484. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  485. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  486. package/src/types/onboarding-context.ts +2 -0
  487. package/src/util/errors.ts +17 -0
  488. package/src/util/platform.ts +10 -0
  489. package/src/watcher/__tests__/engine.test.ts +22 -0
  490. package/src/watcher/engine.ts +6 -2
  491. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  492. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  493. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  494. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  495. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  496. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  497. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  498. package/src/workspace/migrations/registry.ts +8 -0
  499. package/src/workspace/migrations/runner.ts +39 -9
  500. package/src/workspace/migrations/types.ts +4 -0
  501. package/examples/plugins/echo/bun.lock +0 -25
  502. package/src/__tests__/context-window-manager.test.ts +0 -2481
  503. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  504. package/src/context/prompts/compact.md +0 -26
  505. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  506. /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Filesystem watcher for the external plugins directory.
3
+ *
4
+ * Watches `<workspaceDir>/plugins/` recursively using fs.watch (FSEvents on
5
+ * macOS). When a plugin directory is created or modified, debounces per
6
+ * top-level directory name (= the plugin name) and dispatches to the
7
+ * watcher's internal change handler so the daemon can register or reload
8
+ * the plugin without a restart.
9
+ *
10
+ * Mirrors `app-source-watcher.ts` (same DebouncerMap shape, same start/stop
11
+ * lifecycle, same ensureStarted late-create hook), but exposes its lifecycle
12
+ * through a static singleton so tool side-effects can call
13
+ * `PluginSourceWatcher.getInstance().ensureStarted()` directly without an
14
+ * intermediate module-level injection.
15
+ */
16
+
17
+ import { type FSWatcher, mkdirSync, watch } from "node:fs";
18
+
19
+ import { DebouncerMap } from "../util/debounce.js";
20
+ import { getLogger } from "../util/logger.js";
21
+ import { getWorkspacePluginsDir } from "../util/platform.js";
22
+ import { reregisterExternalPlugin } from "./external-plugins-bootstrap.js";
23
+
24
+ const log = getLogger("plugin-source-watcher");
25
+
26
+ const PLUGIN_SOURCE_DEBOUNCE_MS = 500;
27
+
28
+ /**
29
+ * Extract the plugin's top-level directory name from a relative path within
30
+ * the plugins root. Returns null for a stray file directly in `plugins/`
31
+ * (not a plugin dir).
32
+ */
33
+ function resolvePluginNameFromRelPath(relPath: string): string | null {
34
+ const slashIdx = relPath.indexOf("/");
35
+ if (slashIdx === -1) {
36
+ // Bare entry under plugins/ — fs.watch reports these when a new
37
+ // directory is first created. We treat the name as the plugin name and
38
+ // let the install path decide whether it's a real plugin.
39
+ return relPath.length > 0 ? relPath : null;
40
+ }
41
+ const dirName = relPath.slice(0, slashIdx);
42
+ return dirName.length > 0 ? dirName : null;
43
+ }
44
+
45
+ export class PluginSourceWatcher {
46
+ /**
47
+ * Process-wide singleton. Callers reach the watcher via
48
+ * {@link PluginSourceWatcher.getInstance} rather than instantiating
49
+ * directly so tool side-effects (`assistant plugins install`) can call
50
+ * `ensureStarted()` on the same watcher the daemon `start()`/`stop()`
51
+ * lifecycle owns, without threading an instance through a registered
52
+ * module-level callback.
53
+ */
54
+ private static singleton: PluginSourceWatcher | null = null;
55
+
56
+ static getInstance(): PluginSourceWatcher {
57
+ PluginSourceWatcher.singleton ??= new PluginSourceWatcher();
58
+ return PluginSourceWatcher.singleton;
59
+ }
60
+
61
+ /** Test-only — drops the singleton so the next `getInstance()` rebuilds. */
62
+ static resetForTests(): void {
63
+ PluginSourceWatcher.singleton?.stop();
64
+ PluginSourceWatcher.singleton = null;
65
+ }
66
+
67
+ private watcher: FSWatcher | null = null;
68
+ private started = false;
69
+ private debouncer = new DebouncerMap({
70
+ defaultDelayMs: PLUGIN_SOURCE_DEBOUNCE_MS,
71
+ maxEntries: 50,
72
+ });
73
+
74
+ start(): void {
75
+ this.started = true;
76
+ this.tryWatch();
77
+ }
78
+
79
+ /**
80
+ * Ensure the watcher is running. Idempotent — safe to call after a tool
81
+ * side-effect that may have just created the plugins directory.
82
+ * No-op if `start()` has not been called yet.
83
+ */
84
+ ensureStarted(): void {
85
+ if (!this.started || this.watcher) return;
86
+ this.tryWatch();
87
+ }
88
+
89
+ stop(): void {
90
+ this.started = false;
91
+ this.debouncer.cancelAll();
92
+ if (this.watcher) {
93
+ this.watcher.close();
94
+ this.watcher = null;
95
+ }
96
+ }
97
+
98
+ private onChange(pluginName: string): Promise<void> {
99
+ return reregisterExternalPlugin(pluginName);
100
+ }
101
+
102
+ private tryWatch(): void {
103
+ if (this.watcher) return;
104
+
105
+ let pluginsDir: string;
106
+ try {
107
+ pluginsDir = getWorkspacePluginsDir();
108
+ } catch {
109
+ log.warn(
110
+ "Could not resolve plugins directory; plugin source watching disabled",
111
+ );
112
+ return;
113
+ }
114
+
115
+ try {
116
+ mkdirSync(pluginsDir, { recursive: true });
117
+ } catch (err) {
118
+ log.warn(
119
+ { err, pluginsDir },
120
+ "Could not create plugins directory; source watching disabled",
121
+ );
122
+ return;
123
+ }
124
+
125
+ try {
126
+ this.watcher = watch(
127
+ pluginsDir,
128
+ { recursive: true },
129
+ (_eventType, filename) => {
130
+ if (!filename) return;
131
+ const pluginName = resolvePluginNameFromRelPath(filename);
132
+ if (!pluginName) return;
133
+ this.debouncer.schedule(`plugin:${pluginName}`, () => {
134
+ void this.onChange(pluginName);
135
+ });
136
+ },
137
+ );
138
+ log.info({ pluginsDir }, "Plugin source watcher started");
139
+ } catch (err) {
140
+ log.warn(
141
+ { err },
142
+ "Failed to watch plugins directory; source watching disabled",
143
+ );
144
+ }
145
+ }
146
+ }
@@ -30,9 +30,13 @@ import {
30
30
  import { updateMetaFile } from "../memory/conversation-disk-view.js";
31
31
  import { broadcastMessage } from "../runtime/assistant-event-hub.js";
32
32
  import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
33
+ import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
33
34
  import { getLogger } from "../util/logger.js";
34
35
  import type { Conversation } from "./conversation.js";
35
- import { buildSlackMetaForPersistence } from "./conversation-messaging.js";
36
+ import {
37
+ buildSlackMetaForPersistence,
38
+ serializePersistedUserMessageContent,
39
+ } from "./conversation-messaging.js";
36
40
  import { formatCompactResult } from "./conversation-process.js";
37
41
  import { resolveChannelCapabilities } from "./conversation-runtime-assembly.js";
38
42
  import {
@@ -314,7 +318,11 @@ export async function processMessage(
314
318
  const persisted = await addMessage(
315
319
  conversationId,
316
320
  "user",
317
- JSON.stringify(cleanMsg.content),
321
+ serializePersistedUserMessageContent(
322
+ content,
323
+ attachments,
324
+ options?.displayContent,
325
+ ),
318
326
  userMetaWithSlack,
319
327
  );
320
328
  conversation.getMessages().push(llmMsg);
@@ -368,6 +376,7 @@ export async function processMessage(
368
376
  serverChannelMeta,
369
377
  );
370
378
  conversation.getMessages().push(assistantMsg);
379
+ publishConversationMessagesChanged(conversationId);
371
380
  return { messageId: persisted.id };
372
381
  }
373
382
 
@@ -399,7 +408,11 @@ export async function processMessage(
399
408
  const persisted = await addMessage(
400
409
  conversationId,
401
410
  "user",
402
- JSON.stringify(cleanMsg.content),
411
+ serializePersistedUserMessageContent(
412
+ content,
413
+ attachments,
414
+ options?.displayContent,
415
+ ),
403
416
  compactUserMeta,
404
417
  );
405
418
  conversation.getMessages().push(cleanMsg);
@@ -421,6 +434,7 @@ export async function processMessage(
421
434
  compactChannelMeta,
422
435
  );
423
436
  conversation.getMessages().push(assistantMsg);
437
+ publishConversationMessagesChanged(conversationId);
424
438
  return { messageId: persisted.id };
425
439
  }
426
440
 
@@ -435,7 +449,9 @@ export async function processMessage(
435
449
  attachments,
436
450
  requestId,
437
451
  persistMetadata,
452
+ options?.displayContent,
438
453
  );
454
+ publishConversationMessagesChanged(conversationId);
439
455
 
440
456
  if (options?.isInteractive === true) {
441
457
  conversation.updateClient(broadcastMessage, false);
@@ -499,7 +515,9 @@ export async function processMessageInBackground(
499
515
  attachments,
500
516
  requestId,
501
517
  persistMetadata,
518
+ options?.displayContent,
502
519
  );
520
+ publishConversationMessagesChanged(conversationId);
503
521
 
504
522
  if (options?.isInteractive === true) {
505
523
  conversation.updateClient(broadcastMessage, false);
@@ -16,7 +16,9 @@ import { broadcastMessage } from "../runtime/assistant-event-hub.js";
16
16
  import { getSigningKeyFingerprint } from "../runtime/auth/token-service.js";
17
17
  import {
18
18
  publishAvatarChanged,
19
+ publishConfigChanged,
19
20
  publishIdentityChanged,
21
+ publishSoundsConfigUpdated,
20
22
  } from "../runtime/sync/resource-sync-events.js";
21
23
  import { updatePublishedAppDeployment } from "../services/published-app-updater.js";
22
24
  import { getSubagentManager } from "../subagent/index.js";
@@ -43,6 +45,8 @@ import { refreshSurfacesForApp } from "./conversation-surfaces.js";
43
45
  import { parseIdentityFields } from "./handlers/identity.js";
44
46
  import type { ConversationCreateOptions } from "./handlers/shared.js";
45
47
  import { setGlobalSkillIpcSender } from "./meet-host-supervisor.js";
48
+ import { PluginSourceWatcher } from "./plugin-source-watcher.js";
49
+ import { refreshSkillCapabilityMemories } from "./skill-memory-refresh.js";
46
50
 
47
51
  const log = getLogger("server");
48
52
 
@@ -68,6 +72,7 @@ export class DaemonServer {
68
72
  // Composed subsystems
69
73
  private configWatcher = getConfigWatcher();
70
74
  private appSourceWatcher = new AppSourceWatcher();
75
+ private pluginSourceWatcher = PluginSourceWatcher.getInstance();
71
76
  private cliIpc = new AssistantIpcServer();
72
77
  private skillIpc = new SkillIpcServer();
73
78
 
@@ -193,11 +198,11 @@ export class DaemonServer {
193
198
  }
194
199
 
195
200
  private broadcastConfigChanged(): void {
196
- broadcastMessage({ type: "config_changed" });
201
+ publishConfigChanged();
197
202
  }
198
203
 
199
204
  private broadcastSoundsConfigUpdated(): void {
200
- broadcastMessage({ type: "sounds_config_updated" });
205
+ publishSoundsConfigUpdated();
201
206
  }
202
207
 
203
208
  private broadcastAvatarUpdated(): void {
@@ -279,12 +284,15 @@ export class DaemonServer {
279
284
  () => this.broadcastSoundsConfigUpdated(),
280
285
  () => this.broadcastAvatarUpdated(),
281
286
  () => this.broadcastConfigChanged(),
287
+ () => refreshSkillCapabilityMemories(getConfig()),
282
288
  );
283
289
 
284
290
  this.syncIdentityToPlatform();
285
291
 
286
292
  this.appSourceWatcher.start((appId) => this.handleAppSourceChange(appId));
287
293
 
294
+ this.pluginSourceWatcher.start();
295
+
288
296
  // Broadcast contacts_changed to all clients when any contact mutation occurs.
289
297
  this.unsubscribeContactChange = onContactChange(() => {
290
298
  broadcastMessage({ type: "contacts_changed" });
@@ -299,6 +307,7 @@ export class DaemonServer {
299
307
  this.evictor.stop();
300
308
  this.configWatcher.stop();
301
309
  this.appSourceWatcher.stop();
310
+ this.pluginSourceWatcher.stop();
302
311
  this.cliIpc.stop();
303
312
  this.skillIpc.stop();
304
313
  if (this.unsubscribeContactChange) {
@@ -0,0 +1,29 @@
1
+ import { getConfig } from "../config/loader.js";
2
+ import type { AssistantConfig } from "../config/schema.js";
3
+ import {
4
+ seedSkillGraphNodes,
5
+ seedUninstalledCatalogSkillMemories,
6
+ } from "../memory/graph/capability-seed.js";
7
+ import { getLogger } from "../util/logger.js";
8
+ import { maybeSeedMemoryV2Skills } from "./memory-v2-startup.js";
9
+
10
+ const log = getLogger("skill-memory-refresh");
11
+
12
+ export function refreshSkillCapabilityMemories(
13
+ config: AssistantConfig = getConfig(),
14
+ ): void {
15
+ seedSkillGraphNodes();
16
+ maybeSeedMemoryV2Skills(config);
17
+ void seedUninstalledCatalogSkillMemories()
18
+ .then(() => {
19
+ // Re-run after the async catalog fetch populates the cache so stale
20
+ // installed-skill nodes can be pruned without deleting catalog-only nodes.
21
+ seedSkillGraphNodes();
22
+ })
23
+ .catch((err) =>
24
+ log.warn(
25
+ { err },
26
+ "Uninstalled catalog skill memory seeding failed — continuing",
27
+ ),
28
+ );
29
+ }
@@ -5,11 +5,26 @@
5
5
  * background jobs (e.g. proactive artifact generation) can persist documents
6
6
  * without going through the HTTP layer.
7
7
  */
8
- import { rawGet, rawRun } from "../memory/raw-query.js";
8
+ import { rawAll, rawGet, rawRun } from "../memory/raw-query.js";
9
9
  import { getLogger } from "../util/logger.js";
10
10
 
11
11
  const log = getLogger("document-store");
12
12
 
13
+ // ---------------------------------------------------------------------------
14
+ // Shared types
15
+ // ---------------------------------------------------------------------------
16
+
17
+ /** A document record with camelCase field names, mapped from the SQLite row. */
18
+ export interface DocumentRecord {
19
+ surfaceId: string;
20
+ conversationId: string;
21
+ title: string;
22
+ content: string;
23
+ wordCount: number;
24
+ createdAt: number;
25
+ updatedAt: number;
26
+ }
27
+
13
28
  // ---------------------------------------------------------------------------
14
29
  // Junction table helper
15
30
  // ---------------------------------------------------------------------------
@@ -27,6 +42,204 @@ export function addDocumentConversation(
27
42
  );
28
43
  }
29
44
 
45
+ // ---------------------------------------------------------------------------
46
+ // Shared query helpers
47
+ // ---------------------------------------------------------------------------
48
+
49
+ interface DocumentRow {
50
+ surface_id: string;
51
+ conversation_id: string;
52
+ title: string;
53
+ content: string;
54
+ word_count: number;
55
+ created_at: number;
56
+ updated_at: number;
57
+ }
58
+
59
+ type DocumentListRow = Omit<DocumentRow, "content">;
60
+
61
+ function escapeSqlLikePattern(value: string): string {
62
+ return value.replace(/[\\%_]/g, "\\$&");
63
+ }
64
+
65
+ function mapRowToRecord(row: DocumentRow): DocumentRecord {
66
+ return {
67
+ surfaceId: row.surface_id,
68
+ conversationId: row.conversation_id,
69
+ title: row.title,
70
+ content: row.content,
71
+ wordCount: row.word_count,
72
+ createdAt: row.created_at,
73
+ updatedAt: row.updated_at,
74
+ };
75
+ }
76
+
77
+ /** Look up a single document by surface ID. Returns `null` when not found. */
78
+ export function getDocumentById(surfaceId: string): DocumentRecord | null {
79
+ try {
80
+ const row = rawGet<DocumentRow>(
81
+ /*sql*/ `SELECT surface_id, conversation_id, title, content, word_count, created_at, updated_at
82
+ FROM documents
83
+ WHERE surface_id = ?`,
84
+ surfaceId,
85
+ );
86
+
87
+ if (!row) {
88
+ log.info({ surfaceId }, "Document not found");
89
+ return null;
90
+ }
91
+
92
+ log.info({ surfaceId }, "Loaded document");
93
+ return mapRowToRecord(row);
94
+ } catch (error) {
95
+ log.error({ err: error, surfaceId }, "Load error");
96
+ return null;
97
+ }
98
+ }
99
+
100
+ /** Return true when a document is associated with a conversation. */
101
+ export function isDocumentAssociatedWithConversation(
102
+ surfaceId: string,
103
+ conversationId: string,
104
+ ): boolean {
105
+ try {
106
+ const row = rawGet<{ found: number }>(
107
+ /*sql*/ `
108
+ SELECT 1 AS found
109
+ FROM document_conversations
110
+ WHERE surface_id = ? AND conversation_id = ?
111
+ LIMIT 1
112
+ `,
113
+ surfaceId,
114
+ conversationId,
115
+ );
116
+ return row != null;
117
+ } catch (error) {
118
+ log.error(
119
+ { err: error, surfaceId, conversationId },
120
+ "Document association check error",
121
+ );
122
+ return false;
123
+ }
124
+ }
125
+
126
+ /**
127
+ * List documents for a given conversation (via the junction table).
128
+ * Returns an empty array when the conversation has no documents or on error.
129
+ */
130
+ export function getDocumentsForConversation(
131
+ conversationId: string,
132
+ ): Omit<DocumentRecord, "content">[] {
133
+ try {
134
+ const rows = rawAll<DocumentListRow>(
135
+ /*sql*/ `
136
+ SELECT d.surface_id, dc.conversation_id AS conversation_id,
137
+ d.title, d.word_count, d.created_at, d.updated_at
138
+ FROM documents d
139
+ INNER JOIN document_conversations dc ON d.surface_id = dc.surface_id
140
+ WHERE dc.conversation_id = ?
141
+ ORDER BY d.updated_at DESC
142
+ `,
143
+ conversationId,
144
+ );
145
+
146
+ log.info(
147
+ { conversationId, count: rows.length },
148
+ "Listed documents for conversation",
149
+ );
150
+ return rows.map((row) => ({
151
+ surfaceId: row.surface_id,
152
+ conversationId: row.conversation_id,
153
+ title: row.title,
154
+ wordCount: row.word_count,
155
+ createdAt: row.created_at,
156
+ updatedAt: row.updated_at,
157
+ }));
158
+ } catch (error) {
159
+ log.error({ err: error, conversationId }, "List error");
160
+ return [];
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Search documents by title substring (case-insensitive).
166
+ * When `conversationId` is supplied, only documents associated with that
167
+ * conversation are returned.
168
+ * Returns documents ordered by most recently updated.
169
+ */
170
+ export function searchDocumentsByTitle(
171
+ query: string,
172
+ options: { conversationId?: string } = {},
173
+ ): Omit<DocumentRecord, "content">[] {
174
+ try {
175
+ const pattern = `%${escapeSqlLikePattern(query)}%`;
176
+ const rows = options.conversationId
177
+ ? rawAll<DocumentListRow>(
178
+ /*sql*/ `
179
+ SELECT d.surface_id, dc.conversation_id AS conversation_id,
180
+ d.title, d.word_count, d.created_at, d.updated_at
181
+ FROM documents d
182
+ INNER JOIN document_conversations dc ON d.surface_id = dc.surface_id
183
+ WHERE dc.conversation_id = ?
184
+ AND d.title COLLATE NOCASE LIKE ? ESCAPE '\\'
185
+ ORDER BY d.updated_at DESC
186
+ LIMIT 20
187
+ `,
188
+ options.conversationId,
189
+ pattern,
190
+ )
191
+ : rawAll<DocumentListRow>(
192
+ /*sql*/ `
193
+ SELECT surface_id, conversation_id, title, word_count, created_at, updated_at
194
+ FROM documents
195
+ WHERE title COLLATE NOCASE LIKE ? ESCAPE '\\'
196
+ ORDER BY updated_at DESC
197
+ LIMIT 20
198
+ `,
199
+ pattern,
200
+ );
201
+
202
+ log.info(
203
+ { query, conversationId: options.conversationId, count: rows.length },
204
+ "Searched documents by title",
205
+ );
206
+ return rows.map((row) => ({
207
+ surfaceId: row.surface_id,
208
+ conversationId: row.conversation_id,
209
+ title: row.title,
210
+ wordCount: row.word_count,
211
+ createdAt: row.created_at,
212
+ updatedAt: row.updated_at,
213
+ }));
214
+ } catch (error) {
215
+ log.error({ err: error, query }, "Search error");
216
+ return [];
217
+ }
218
+ }
219
+
220
+ /**
221
+ * Delete a document and its conversation associations.
222
+ * Returns `true` if the document existed and was deleted, `false` otherwise.
223
+ */
224
+ export function deleteDocument(surfaceId: string): boolean {
225
+ try {
226
+ const changes = rawRun(
227
+ /*sql*/ `DELETE FROM documents WHERE surface_id = ?`,
228
+ surfaceId,
229
+ );
230
+ rawRun(
231
+ /*sql*/ `DELETE FROM document_conversations WHERE surface_id = ?`,
232
+ surfaceId,
233
+ );
234
+ const existed = changes > 0;
235
+ log.info({ surfaceId, existed }, "Deleted document");
236
+ return existed;
237
+ } catch (error) {
238
+ log.error({ err: error, surfaceId }, "Delete error");
239
+ return false;
240
+ }
241
+ }
242
+
30
243
  // ---------------------------------------------------------------------------
31
244
  // Document persistence
32
245
  // ---------------------------------------------------------------------------
@@ -89,7 +302,7 @@ export function updateDocumentContent(
89
302
  surfaceId: string,
90
303
  markdown: string,
91
304
  mode: string,
92
- ): void {
305
+ ): { success: true } | { success: false; error: string } {
93
306
  try {
94
307
  const existing = rawGet<{ content: string }>(
95
308
  /*sql*/ `SELECT content FROM documents WHERE surface_id = ?`,
@@ -97,7 +310,7 @@ export function updateDocumentContent(
97
310
  );
98
311
  if (!existing) {
99
312
  log.info({ surfaceId }, "No persisted document to update");
100
- return;
313
+ return { success: false, error: "Document not found" };
101
314
  }
102
315
  const sep = mode === "append" && existing.content.length > 0 ? "\n\n" : "";
103
316
  const newContent =
@@ -113,7 +326,12 @@ export function updateDocumentContent(
113
326
  surfaceId,
114
327
  );
115
328
  log.info({ surfaceId, mode }, "Updated document content");
329
+ return { success: true };
116
330
  } catch (error) {
117
331
  log.error({ err: error, surfaceId }, "Document content update error");
332
+ return {
333
+ success: false,
334
+ error: error instanceof Error ? error.message : "Unknown error",
335
+ };
118
336
  }
119
337
  }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Embedded handle for `@vellumai/plugin-api`.
3
+ *
4
+ * Standard `import * as pluginApi from "../plugin-api/index.ts"` lets Bun's
5
+ * normal bundler walk the plugin-api module graph at build time — relative
6
+ * imports inside plugin-api (`./types.js`, future runtime siblings) are
7
+ * inlined naturally, with no separate build step. In `bun --compile`, this
8
+ * means plugin-api ships as part of the assistant binary's regular code
9
+ * graph; in JIT/Docker, it loads from source.
10
+ *
11
+ * The loaded namespace is then installed on `globalThis` under a versioned
12
+ * symbol. The boot-time shim writer (`ensurePluginApiShim`) enumerates
13
+ * {@link PLUGIN_API_EXPORTS} and generates a tiny ESM module at
14
+ * `<workspaceDir>/node_modules/@vellumai/plugin-api/index.js` that
15
+ * re-binds each runtime export from `globalThis`. User plugins that
16
+ * `import { ... } from "@vellumai/plugin-api"` walk up to that shim and
17
+ * pick up the bindings.
18
+ *
19
+ * Type-only exports erase before this module loads, so `Object.keys`
20
+ * sees only runtime exports. That's correct — types are a dev-time
21
+ * concern, resolved against plugin-api source via tsconfig path-mapping
22
+ * (or, post-PR-5, against a generated `.d.ts` next to the runtime
23
+ * shim).
24
+ */
25
+
26
+ import * as pluginApi from "../plugin-api/index.js";
27
+
28
+ /** Symbol key under which the plugin-api namespace is published on globalThis. */
29
+ export const PLUGIN_API_REGISTRY_KEY = Symbol.for("vellum.plugin-api.v1");
30
+
31
+ // Install on globalThis once at module-load time. The shim writer reads
32
+ // `PLUGIN_API_EXPORTS` to know which bindings to re-export; the shim's
33
+ // generated body then reads `globalThis[PLUGIN_API_REGISTRY_KEY]` to grab
34
+ // the live module namespace.
35
+ (globalThis as Record<symbol, unknown>)[PLUGIN_API_REGISTRY_KEY] = pluginApi;
36
+
37
+ /** Names of the runtime exports the workspace shim should re-bind. */
38
+ export const PLUGIN_API_EXPORTS: readonly string[] = Object.freeze(
39
+ Object.keys(pluginApi),
40
+ );