@vellumai/assistant 0.8.1 → 0.8.3

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 (630) hide show
  1. package/ARCHITECTURE.md +13 -19
  2. package/Dockerfile +75 -1
  3. package/bun.lock +11 -1
  4. package/docker-entrypoint.sh +17 -0
  5. package/docker-init-apt-root.sh +167 -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 +642 -5
  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-loop-exit-reason.test.ts +272 -0
  21. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  22. package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
  23. package/src/__tests__/anthropic-provider.test.ts +45 -0
  24. package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
  25. package/src/__tests__/app-executors.test.ts +220 -4
  26. package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
  27. package/src/__tests__/bundled-asset.test.ts +6 -6
  28. package/src/__tests__/channel-availability-routes.test.ts +206 -0
  29. package/src/__tests__/channel-delivery-store.test.ts +289 -1
  30. package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
  31. package/src/__tests__/clawhub.test.ts +75 -16
  32. package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
  33. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  34. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  35. package/src/__tests__/config-schema.test.ts +21 -0
  36. package/src/__tests__/config-set-route.test.ts +80 -0
  37. package/src/__tests__/config-sounds-sync.test.ts +97 -0
  38. package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
  39. package/src/__tests__/context-search-conversations-source.test.ts +117 -2
  40. package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
  41. package/src/__tests__/context-search-workspace-source.test.ts +7 -0
  42. package/src/__tests__/context-token-estimator.test.ts +31 -65
  43. package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
  44. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
  45. package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
  46. package/src/__tests__/conversation-agent-loop.test.ts +59 -1
  47. package/src/__tests__/conversation-error.test.ts +42 -3
  48. package/src/__tests__/conversation-fork-crud.test.ts +82 -0
  49. package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
  50. package/src/__tests__/conversation-lifecycle.test.ts +173 -0
  51. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  52. package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
  53. package/src/__tests__/conversation-pairing.test.ts +54 -0
  54. package/src/__tests__/conversation-process-callsite.test.ts +4 -1
  55. package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
  56. package/src/__tests__/conversation-queue.test.ts +4 -1
  57. package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
  58. package/src/__tests__/conversation-slash-queue.test.ts +59 -1
  59. package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
  60. package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
  61. package/src/__tests__/conversation-sync-tags.test.ts +235 -0
  62. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  63. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  64. package/src/__tests__/credential-security-invariants.test.ts +3 -2
  65. package/src/__tests__/date-context.test.ts +45 -0
  66. package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
  67. package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
  68. package/src/__tests__/disk-pressure-tools.test.ts +1 -0
  69. package/src/__tests__/dm-backfill.test.ts +121 -10
  70. package/src/__tests__/document-tool-security.test.ts +258 -0
  71. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
  72. package/src/__tests__/edit-propagation.test.ts +33 -0
  73. package/src/__tests__/empty-response-pipeline.test.ts +0 -4
  74. package/src/__tests__/external-plugin-loader.test.ts +151 -55
  75. package/src/__tests__/filing-service.test.ts +140 -0
  76. package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
  77. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  78. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  79. package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
  80. package/src/__tests__/heartbeat-service.test.ts +24 -164
  81. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  82. package/src/__tests__/helpers/tar-fixtures.ts +39 -0
  83. package/src/__tests__/helpers/wait-for.ts +21 -0
  84. package/src/__tests__/history-repair-pipeline.test.ts +0 -3
  85. package/src/__tests__/history-repair.test.ts +73 -0
  86. package/src/__tests__/host-app-control-proxy.test.ts +507 -10
  87. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  88. package/src/__tests__/image-credentials.test.ts +1 -1
  89. package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
  90. package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
  91. package/src/__tests__/inference-profile-reaper.test.ts +4 -2
  92. package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
  93. package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
  94. package/src/__tests__/injector-background-turn.test.ts +153 -0
  95. package/src/__tests__/injector-chain.test.ts +15 -8
  96. package/src/__tests__/install-skill-routing.test.ts +155 -37
  97. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
  98. package/src/__tests__/list-messages-page-latest.test.ts +55 -0
  99. package/src/__tests__/llm-call-pipeline.test.ts +0 -3
  100. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  101. package/src/__tests__/llm-catalog-parity.test.ts +58 -13
  102. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  103. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  104. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
  105. package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
  106. package/src/__tests__/llm-resolver.test.ts +255 -2
  107. package/src/__tests__/llm-usage-store.test.ts +114 -0
  108. package/src/__tests__/managed-profile-guard.test.ts +41 -29
  109. package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
  110. package/src/__tests__/managed-store.test.ts +84 -192
  111. package/src/__tests__/media-generate-image.test.ts +1 -1
  112. package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
  113. package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
  114. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  115. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  116. package/src/__tests__/notification-deep-link.test.ts +15 -0
  117. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  118. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  119. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  120. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  121. package/src/__tests__/oauth-commands-routes.test.ts +168 -16
  122. package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
  123. package/src/__tests__/openai-provider.test.ts +242 -3
  124. package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
  125. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  126. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  127. package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
  128. package/src/__tests__/persistence-pipeline.test.ts +0 -2
  129. package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
  130. package/src/__tests__/platform.test.ts +2 -0
  131. package/src/__tests__/plugin-api-shim.test.ts +125 -0
  132. package/src/__tests__/plugin-bootstrap.test.ts +10 -36
  133. package/src/__tests__/plugin-external-api.test.ts +68 -0
  134. package/src/__tests__/plugin-registry.test.ts +0 -77
  135. package/src/__tests__/plugin-route-contribution.test.ts +0 -1
  136. package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
  137. package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
  138. package/src/__tests__/plugin-types.test.ts +3 -13
  139. package/src/__tests__/process-message-background-slack.test.ts +8 -1
  140. package/src/__tests__/process-message-display-content.test.ts +421 -0
  141. package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
  142. package/src/__tests__/provider-error-scenarios.test.ts +111 -0
  143. package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
  144. package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
  145. package/src/__tests__/schedule-routes.test.ts +50 -3
  146. package/src/__tests__/schedule-store.test.ts +94 -0
  147. package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
  148. package/src/__tests__/schema-transforms.test.ts +20 -0
  149. package/src/__tests__/search-skills-unified.test.ts +0 -5
  150. package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
  151. package/src/__tests__/server-history-render.test.ts +43 -0
  152. package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
  153. package/src/__tests__/skill-load-tool.test.ts +27 -89
  154. package/src/__tests__/skill-memory.test.ts +23 -3
  155. package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
  156. package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
  157. package/src/__tests__/skills-install-extract.test.ts +49 -38
  158. package/src/__tests__/skills-install-staging.test.ts +159 -0
  159. package/src/__tests__/skills-uninstall.test.ts +9 -41
  160. package/src/__tests__/skills.test.ts +51 -58
  161. package/src/__tests__/slack-channel-config.test.ts +9 -0
  162. package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
  163. package/src/__tests__/system-prompt.test.ts +670 -63
  164. package/src/__tests__/terminal-tools.test.ts +28 -1
  165. package/src/__tests__/thread-backfill.test.ts +557 -27
  166. package/src/__tests__/title-generate-pipeline.test.ts +0 -13
  167. package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
  168. package/src/__tests__/tool-error-pipeline.test.ts +0 -3
  169. package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
  170. package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
  171. package/src/__tests__/tool-executor.test.ts +16 -4
  172. package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
  173. package/src/__tests__/turn-events-store.test.ts +256 -0
  174. package/src/__tests__/twilio-routes.test.ts +4 -0
  175. package/src/__tests__/user-plugin-loader.test.ts +0 -7
  176. package/src/__tests__/voice-session-bridge.test.ts +198 -0
  177. package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
  178. package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
  179. package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
  180. package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
  181. package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
  182. package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
  183. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  184. package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
  185. package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
  186. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  187. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  188. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  189. package/src/a2a/__tests__/task-store.test.ts +246 -0
  190. package/src/a2a/agent-card.ts +58 -0
  191. package/src/a2a/feature-gate.ts +8 -0
  192. package/src/a2a/protocol-constants.ts +21 -0
  193. package/src/a2a/protocol-errors.ts +50 -0
  194. package/src/a2a/protocol-types.ts +162 -0
  195. package/src/a2a/task-store.ts +168 -0
  196. package/src/acp/resolve-agent.ts +1 -1
  197. package/src/agent/image-optimize.ts +13 -5
  198. package/src/agent/loop.ts +167 -18
  199. package/src/calls/voice-session-bridge.ts +61 -42
  200. package/src/channels/config.ts +9 -0
  201. package/src/channels/types.ts +122 -0
  202. package/src/cli/__tests__/unknown-command.test.ts +24 -0
  203. package/src/cli/commands/__tests__/changelog.test.ts +304 -319
  204. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  205. package/src/cli/commands/__tests__/schedules.test.ts +960 -0
  206. package/src/cli/commands/changelog.ts +106 -42
  207. package/src/cli/commands/conversations.ts +102 -17
  208. package/src/cli/commands/default-action.ts +10 -53
  209. package/src/cli/commands/notifications.ts +388 -346
  210. package/src/cli/commands/plugins.ts +252 -0
  211. package/src/cli/commands/schedules.ts +683 -0
  212. package/src/cli/commands/telemetry.ts +40 -0
  213. package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
  214. package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
  215. package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
  216. package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
  217. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  218. package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
  219. package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
  220. package/src/cli/lib/cli-colors.ts +12 -0
  221. package/src/cli/lib/confirm-prompt.ts +79 -0
  222. package/src/cli/lib/install-from-github.ts +303 -0
  223. package/src/cli/lib/list-installed-plugins.ts +137 -0
  224. package/src/cli/lib/search-plugins.ts +163 -0
  225. package/src/cli/lib/uninstall-plugin.ts +82 -0
  226. package/src/cli/lib/unknown-command.ts +111 -0
  227. package/src/cli/program.ts +52 -2
  228. package/src/config/assistant-feature-flags.ts +24 -54
  229. package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
  230. package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
  231. package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
  232. package/src/config/bundled-skills/document/SKILL.md +23 -3
  233. package/src/config/bundled-skills/document/TOOLS.json +53 -0
  234. package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
  235. package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
  236. package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
  237. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  238. package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
  239. package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
  240. package/src/config/bundled-tool-registry.ts +6 -0
  241. package/src/config/call-site-defaults.ts +105 -0
  242. package/src/config/feature-flag-registry.json +41 -9
  243. package/src/config/llm-resolver.ts +52 -1
  244. package/src/config/loader.ts +64 -38
  245. package/src/config/schema.ts +9 -10
  246. package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
  247. package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
  248. package/src/config/schemas/channels.ts +17 -0
  249. package/src/config/schemas/compaction.ts +28 -0
  250. package/src/config/schemas/conversations.ts +10 -0
  251. package/src/config/schemas/heartbeat.ts +23 -0
  252. package/src/config/schemas/llm-request-logs.ts +31 -7
  253. package/src/config/schemas/llm.ts +1 -0
  254. package/src/config/schemas/memory-retrieval.ts +18 -0
  255. package/src/config/schemas/memory-retrospective.ts +1 -1
  256. package/src/config/schemas/memory-v2.ts +4 -4
  257. package/src/config/schemas/memory.ts +3 -1
  258. package/src/config/schemas/tools.ts +14 -0
  259. package/src/config/seed-inference-profiles.ts +99 -29
  260. package/src/config/skills.ts +3 -96
  261. package/src/context/compactor.ts +1107 -0
  262. package/src/context/token-estimator.ts +34 -36
  263. package/src/context/window-manager.ts +197 -1520
  264. package/src/credential-execution/managed-catalog.ts +37 -0
  265. package/src/credential-health/credential-health-service.ts +280 -19
  266. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
  267. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
  268. package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
  269. package/src/daemon/approval-generators.ts +8 -6
  270. package/src/daemon/config-watcher.ts +94 -31
  271. package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
  272. package/src/daemon/conversation-agent-loop.ts +198 -11
  273. package/src/daemon/conversation-error.ts +171 -37
  274. package/src/daemon/conversation-lifecycle.ts +53 -40
  275. package/src/daemon/conversation-messaging.ts +25 -6
  276. package/src/daemon/conversation-process.ts +49 -12
  277. package/src/daemon/conversation-runtime-assembly.ts +25 -1
  278. package/src/daemon/conversation-slash.ts +12 -5
  279. package/src/daemon/conversation-store.ts +11 -4
  280. package/src/daemon/conversation-tool-setup.ts +39 -7
  281. package/src/daemon/conversation.ts +33 -8
  282. package/src/daemon/date-context.ts +40 -0
  283. package/src/daemon/external-plugins-bootstrap.ts +217 -181
  284. package/src/daemon/first-greeting.ts +22 -2
  285. package/src/daemon/guardian-action-generators.ts +1 -125
  286. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  287. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  288. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  289. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  290. package/src/daemon/handlers/config-a2a.ts +289 -0
  291. package/src/daemon/handlers/config-model.ts +6 -5
  292. package/src/daemon/handlers/config-slack-channel.ts +15 -3
  293. package/src/daemon/handlers/conversations.ts +1 -0
  294. package/src/daemon/handlers/shared.ts +14 -5
  295. package/src/daemon/handlers/skills.ts +111 -108
  296. package/src/daemon/history-repair.ts +28 -1
  297. package/src/daemon/host-app-control-proxy.ts +153 -27
  298. package/src/daemon/host-proxy-preactivation.ts +85 -18
  299. package/src/daemon/lifecycle.ts +89 -91
  300. package/src/daemon/meet-host-supervisor.ts +5 -4
  301. package/src/daemon/memory-v2-startup.ts +85 -0
  302. package/src/daemon/message-protocol.ts +1 -0
  303. package/src/daemon/message-types/conversations.ts +25 -0
  304. package/src/daemon/message-types/messages.ts +61 -0
  305. package/src/daemon/message-types/notifications.ts +21 -0
  306. package/src/daemon/message-types/subagents.ts +1 -0
  307. package/src/daemon/message-types/sync.ts +1 -0
  308. package/src/daemon/pkb-reminder-builder.test.ts +11 -54
  309. package/src/daemon/pkb-reminder-builder.ts +5 -20
  310. package/src/daemon/plugin-source-watcher.ts +146 -0
  311. package/src/daemon/process-message.ts +24 -3
  312. package/src/daemon/server.ts +11 -2
  313. package/src/daemon/skill-memory-refresh.ts +33 -0
  314. package/src/daemon/wake-target-adapter.ts +2 -0
  315. package/src/documents/document-store.ts +221 -3
  316. package/src/embedded/plugin-api.ts +40 -0
  317. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  318. package/src/export/transcript-formatter.ts +54 -20
  319. package/src/filing/filing-service.ts +39 -0
  320. package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
  321. package/src/heartbeat/heartbeat-run-store.ts +2 -1
  322. package/src/heartbeat/heartbeat-service.ts +73 -189
  323. package/src/home/__tests__/feed-types.test.ts +80 -0
  324. package/src/home/feed-types.ts +36 -2
  325. package/src/home/post-connect-feed.ts +1 -0
  326. package/src/index.ts +18 -1
  327. package/src/ipc/cli-client.ts +147 -45
  328. package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
  329. package/src/mcp/client.ts +20 -4
  330. package/src/media/image-credentials.ts +3 -3
  331. package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
  332. package/src/memory/__tests__/conversation-queries.test.ts +483 -0
  333. package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
  334. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  335. package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
  336. package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
  337. package/src/memory/__tests__/message-content.test.ts +35 -0
  338. package/src/memory/bookmark-crud.ts +42 -10
  339. package/src/memory/context-search/sources/conversations.ts +62 -2
  340. package/src/memory/context-search/sources/workspace.ts +4 -0
  341. package/src/memory/conversation-crud.ts +63 -19
  342. package/src/memory/conversation-queries.ts +197 -11
  343. package/src/memory/conversation-title-service.ts +26 -4
  344. package/src/memory/db-init.ts +12 -0
  345. package/src/memory/delivery-crud.ts +152 -5
  346. package/src/memory/embedding-backend.ts +4 -4
  347. package/src/memory/external-conversation-store.ts +66 -5
  348. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
  349. package/src/memory/graph/conversation-graph-memory.ts +49 -21
  350. package/src/memory/graph/tools.ts +9 -40
  351. package/src/memory/indexer.ts +34 -29
  352. package/src/memory/invite-store.ts +53 -0
  353. package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
  354. package/src/memory/jobs/embed-concept-page.ts +20 -11
  355. package/src/memory/jobs-worker.ts +6 -1
  356. package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
  357. package/src/memory/llm-request-log-source.ts +19 -52
  358. package/src/memory/llm-request-log-store.ts +92 -1
  359. package/src/memory/llm-usage-store.ts +125 -5
  360. package/src/memory/memory-retrospective-enqueue.ts +1 -20
  361. package/src/memory/memory-retrospective-job.ts +33 -6
  362. package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
  363. package/src/memory/message-content.ts +1 -1
  364. package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
  365. package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
  366. package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
  367. package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
  368. package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
  369. package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
  370. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  371. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  372. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  373. package/src/memory/migrations/index.ts +9 -0
  374. package/src/memory/migrations/registry.ts +16 -0
  375. package/src/memory/onboarding-events-store.ts +106 -0
  376. package/src/memory/schema/a2a.ts +15 -0
  377. package/src/memory/schema/bookmarks.ts +0 -2
  378. package/src/memory/schema/calls.ts +1 -0
  379. package/src/memory/schema/index.ts +1 -0
  380. package/src/memory/schema/inference.ts +3 -3
  381. package/src/memory/schema/infrastructure.ts +13 -0
  382. package/src/memory/turn-events-store.ts +127 -2
  383. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  384. package/src/memory/v2/__tests__/activation.test.ts +0 -8
  385. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  386. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  387. package/src/memory/v2/__tests__/injection.test.ts +288 -11
  388. package/src/memory/v2/__tests__/migration.test.ts +87 -0
  389. package/src/memory/v2/__tests__/page-index.test.ts +83 -0
  390. package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
  391. package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
  392. package/src/memory/v2/__tests__/router.test.ts +15 -0
  393. package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
  394. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  395. package/src/memory/v2/activation-store.ts +14 -16
  396. package/src/memory/v2/cli-command-content.ts +19 -0
  397. package/src/memory/v2/cli-command-store.ts +304 -0
  398. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  399. package/src/memory/v2/injection.ts +81 -26
  400. package/src/memory/v2/migration.ts +49 -19
  401. package/src/memory/v2/page-index.ts +63 -8
  402. package/src/memory/v2/prompts/router.ts +11 -8
  403. package/src/memory/v2/prompts/sweep.ts +2 -2
  404. package/src/memory/v2/qdrant.ts +135 -7
  405. package/src/memory/v2/router.ts +9 -8
  406. package/src/memory/v2/skill-store.ts +120 -35
  407. package/src/memory/v2/static-context.ts +4 -4
  408. package/src/memory/v2/types.ts +23 -0
  409. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  410. package/src/messaging/providers/a2a/deliver.ts +156 -0
  411. package/src/messaging/providers/gmail/client.ts +9 -2
  412. package/src/messaging/providers/index.ts +11 -2
  413. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
  414. package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
  415. package/src/messaging/providers/slack/adapter.ts +43 -5
  416. package/src/messaging/providers/slack/client.ts +27 -0
  417. package/src/messaging/providers/slack/deep-link.ts +65 -0
  418. package/src/messaging/providers/slack/download.ts +104 -0
  419. package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
  420. package/src/messaging/providers/slack/message-metadata.ts +27 -0
  421. package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
  422. package/src/messaging/providers/slack/render-transcript.ts +69 -5
  423. package/src/messaging/providers/slack/types.ts +20 -1
  424. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  425. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  426. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  427. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
  428. package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
  429. package/src/notifications/adapters/macos.ts +12 -2
  430. package/src/notifications/broadcaster.ts +29 -4
  431. package/src/notifications/conversation-pairing.ts +2 -1
  432. package/src/notifications/copy-composer.ts +17 -64
  433. package/src/notifications/decision-engine.ts +113 -45
  434. package/src/notifications/deterministic-checks.ts +96 -0
  435. package/src/notifications/emit-signal.ts +21 -1
  436. package/src/notifications/home-feed-side-effect.ts +138 -5
  437. package/src/notifications/signal.ts +3 -5
  438. package/src/notifications/types.ts +8 -0
  439. package/src/oauth/connection-resolver.ts +8 -4
  440. package/src/oauth/platform-connection.test.ts +43 -3
  441. package/src/oauth/platform-connection.ts +19 -6
  442. package/src/oauth/seed-providers.ts +10 -1
  443. package/src/permissions/checker.ts +2 -0
  444. package/src/permissions/ipc-risk-types.ts +1 -0
  445. package/src/permissions/question-prompter.test.ts +416 -0
  446. package/src/permissions/question-prompter.ts +294 -0
  447. package/src/platform/client.test.ts +1 -1
  448. package/src/platform/client.ts +1 -1
  449. package/src/plugin-api/constants.ts +26 -0
  450. package/src/plugin-api/index.ts +34 -1
  451. package/src/plugin-api/types.ts +104 -22
  452. package/src/plugins/defaults/circuit-breaker.ts +0 -5
  453. package/src/plugins/defaults/compaction.ts +0 -4
  454. package/src/plugins/defaults/empty-response.ts +0 -2
  455. package/src/plugins/defaults/history-repair.ts +0 -2
  456. package/src/plugins/defaults/injectors.ts +74 -22
  457. package/src/plugins/defaults/llm-call.ts +0 -2
  458. package/src/plugins/defaults/memory-retrieval.ts +0 -1
  459. package/src/plugins/defaults/overflow-reduce.ts +0 -1
  460. package/src/plugins/defaults/persistence.ts +0 -2
  461. package/src/plugins/defaults/title-generate.ts +0 -5
  462. package/src/plugins/defaults/token-estimate.ts +0 -2
  463. package/src/plugins/defaults/tool-error.ts +0 -7
  464. package/src/plugins/defaults/tool-execute.ts +0 -2
  465. package/src/plugins/defaults/tool-result-truncate.ts +0 -4
  466. package/src/plugins/ensure-plugin-api-shim.ts +96 -0
  467. package/src/plugins/external-api.ts +104 -0
  468. package/src/plugins/external-plugin-loader.ts +187 -42
  469. package/src/plugins/feature-gate.ts +22 -0
  470. package/src/plugins/pipeline.ts +37 -0
  471. package/src/plugins/registry.ts +48 -80
  472. package/src/plugins/types.ts +40 -26
  473. package/src/plugins/user-loader.ts +21 -2
  474. package/src/proactive-artifact/aux-message-injector.ts +11 -0
  475. package/src/proactive-artifact/job.test.ts +37 -5
  476. package/src/prompts/__tests__/system-prompt.test.ts +10 -43
  477. package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
  478. package/src/prompts/normalize-onboarding.ts +27 -0
  479. package/src/prompts/sections.ts +302 -0
  480. package/src/prompts/system-prompt.ts +63 -174
  481. package/src/prompts/templates/BOOTSTRAP.md +17 -1
  482. package/src/prompts/templates/system-sections.ts +164 -0
  483. package/src/providers/__tests__/inference.test.ts +24 -7
  484. package/src/providers/anthropic/client.ts +28 -28
  485. package/src/providers/call-site-routing.ts +24 -6
  486. package/src/providers/connection-resolution.ts +68 -11
  487. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  488. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  489. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  490. package/src/providers/inference/adapter-factory.ts +32 -6
  491. package/src/providers/inference/auth.ts +12 -0
  492. package/src/providers/inference/backfill.ts +14 -1
  493. package/src/providers/inference/connections.ts +159 -34
  494. package/src/providers/inference/resolve-auth.ts +14 -4
  495. package/src/providers/model-catalog.ts +249 -12
  496. package/src/providers/model-intents.ts +3 -3
  497. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  498. package/src/providers/openai/chat-completions-provider.ts +169 -8
  499. package/src/providers/openrouter/client.ts +49 -4
  500. package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
  501. package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
  502. package/src/providers/provider-availability.ts +17 -2
  503. package/src/providers/provider-catalog-visibility.ts +38 -0
  504. package/src/providers/provider-send-message.ts +27 -12
  505. package/src/providers/registry.ts +52 -15
  506. package/src/providers/retry.ts +47 -1
  507. package/src/runtime/__tests__/agent-wake.test.ts +152 -0
  508. package/src/runtime/agent-wake.ts +103 -15
  509. package/src/runtime/auth/route-policy.ts +21 -1
  510. package/src/runtime/btw-sidechain.ts +2 -0
  511. package/src/runtime/http-server.ts +7 -16
  512. package/src/runtime/http-types.ts +19 -47
  513. package/src/runtime/migrations/origin-mode.ts +1 -1
  514. package/src/runtime/pending-interactions.ts +1 -0
  515. package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
  516. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  517. package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
  518. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
  519. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  520. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  521. package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
  522. package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
  523. package/src/runtime/routes/acp-routes-list.test.ts +143 -0
  524. package/src/runtime/routes/acp-routes.ts +5 -3
  525. package/src/runtime/routes/auth-routes.ts +1 -1
  526. package/src/runtime/routes/bookmark-routes.ts +5 -3
  527. package/src/runtime/routes/btw-routes.ts +5 -1
  528. package/src/runtime/routes/channel-availability-routes.ts +126 -0
  529. package/src/runtime/routes/consolidation-routes.ts +100 -0
  530. package/src/runtime/routes/conversation-cli-routes.ts +44 -3
  531. package/src/runtime/routes/conversation-list-routes.ts +3 -20
  532. package/src/runtime/routes/conversation-management-routes.ts +17 -42
  533. package/src/runtime/routes/conversation-query-routes.ts +99 -35
  534. package/src/runtime/routes/conversation-routes.ts +97 -11
  535. package/src/runtime/routes/documents-routes.ts +25 -86
  536. package/src/runtime/routes/group-routes.ts +5 -0
  537. package/src/runtime/routes/inbound-conversation.ts +28 -8
  538. package/src/runtime/routes/inbound-message-handler.ts +236 -41
  539. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
  540. package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
  541. package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
  542. package/src/runtime/routes/index.ts +8 -0
  543. package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
  544. package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
  545. package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
  546. package/src/runtime/routes/integrations/a2a.ts +235 -0
  547. package/src/runtime/routes/integrations/slack/share.ts +4 -52
  548. package/src/runtime/routes/integrations/slack/token.ts +43 -0
  549. package/src/runtime/routes/integrations/twilio.ts +6 -13
  550. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  551. package/src/runtime/routes/notification-routes.ts +1 -1
  552. package/src/runtime/routes/oauth-commands-routes.ts +105 -15
  553. package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
  554. package/src/runtime/routes/question-routes.ts +259 -0
  555. package/src/runtime/routes/rename-conversation-routes.ts +2 -33
  556. package/src/runtime/routes/schedule-routes.ts +4 -7
  557. package/src/runtime/routes/subagents-routes.ts +98 -18
  558. package/src/runtime/routes/telemetry-routes.ts +27 -0
  559. package/src/runtime/routes/tts-routes.ts +27 -2
  560. package/src/runtime/routes/workspace-routes.test.ts +43 -0
  561. package/src/runtime/routes/workspace-routes.ts +28 -0
  562. package/src/runtime/services/conversation-serializer.ts +39 -7
  563. package/src/runtime/sync/resource-sync-events.ts +93 -1
  564. package/src/schedule/schedule-store.ts +27 -2
  565. package/src/schedule/scheduler.ts +9 -1
  566. package/src/security/__tests__/untrusted-content.test.ts +86 -0
  567. package/src/security/untrusted-content.ts +93 -8
  568. package/src/skills/catalog-files.ts +1 -1
  569. package/src/skills/catalog-install.ts +233 -116
  570. package/src/skills/clawhub.ts +70 -13
  571. package/src/skills/managed-store.ts +4 -119
  572. package/src/skills/skillssh-registry.ts +27 -48
  573. package/src/subagent/manager.ts +17 -7
  574. package/src/telemetry/types.ts +113 -1
  575. package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
  576. package/src/telemetry/usage-telemetry-reporter.ts +113 -7
  577. package/src/tools/apps/executors.ts +58 -7
  578. package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
  579. package/src/tools/ask-question/ask-question-tool.ts +304 -0
  580. package/src/tools/browser/browser-execution.ts +15 -11
  581. package/src/tools/computer-use/definitions.ts +3 -3
  582. package/src/tools/credentials/vault.ts +1 -1
  583. package/src/tools/document/document-tool.ts +124 -1
  584. package/src/tools/filesystem/edit.ts +1 -1
  585. package/src/tools/filesystem/list.ts +1 -1
  586. package/src/tools/filesystem/read.ts +1 -1
  587. package/src/tools/filesystem/write.ts +5 -2
  588. package/src/tools/host-filesystem/transfer.ts +1 -1
  589. package/src/tools/host-terminal/host-shell.ts +1 -1
  590. package/src/tools/memory/register.ts +1 -9
  591. package/src/tools/permission-checker.ts +1 -1
  592. package/src/tools/registry.ts +17 -7
  593. package/src/tools/schedule/create.ts +2 -2
  594. package/src/tools/schema-transforms.ts +7 -2
  595. package/src/tools/side-effects.ts +1 -0
  596. package/src/tools/skills/delete-managed.ts +4 -4
  597. package/src/tools/skills/execute.ts +1 -1
  598. package/src/tools/skills/scaffold-managed.ts +3 -2
  599. package/src/tools/subagent/notify-parent.ts +1 -1
  600. package/src/tools/system/request-permission.ts +2 -2
  601. package/src/tools/terminal/safe-env.ts +60 -1
  602. package/src/tools/tool-manifest.ts +2 -0
  603. package/src/tools/types.ts +107 -21
  604. package/src/tools/ui-surface/definitions.ts +6 -5
  605. package/src/tts/__tests__/provider-adapters.test.ts +76 -2
  606. package/src/tts/providers/elevenlabs-provider.ts +75 -1
  607. package/src/types/onboarding-context.ts +2 -0
  608. package/src/util/errors.ts +17 -0
  609. package/src/util/platform.ts +10 -0
  610. package/src/watcher/__tests__/engine.test.ts +22 -0
  611. package/src/watcher/engine.ts +6 -2
  612. package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
  613. package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
  614. package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
  615. package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
  616. package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
  617. package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
  618. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
  619. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  620. package/src/workspace/migrations/registry.ts +10 -0
  621. package/src/workspace/migrations/runner.ts +39 -9
  622. package/src/workspace/migrations/types.ts +4 -0
  623. package/examples/plugins/echo/bun.lock +0 -25
  624. package/src/__tests__/context-window-manager.test.ts +0 -2481
  625. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  626. package/src/context/__tests__/compact-prompt.test.ts +0 -63
  627. package/src/context/prompts/compact.md +0 -26
  628. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  629. package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
  630. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
@@ -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
+ );
@@ -0,0 +1,121 @@
1
+ import { describe, expect, mock, test } from "bun:test";
2
+
3
+ import { z } from "zod";
4
+
5
+ type TestMessage = {
6
+ id: string;
7
+ conversationId: string;
8
+ role: string;
9
+ content: string;
10
+ createdAt: number;
11
+ metadata: string | null;
12
+ };
13
+
14
+ const parentMessages: TestMessage[] = [
15
+ {
16
+ id: "msg-parent-1",
17
+ conversationId: "parent-conv",
18
+ role: "user",
19
+ content: JSON.stringify([{ type: "text", text: "go research foo" }]),
20
+ createdAt: 1_700_000_000_000,
21
+ metadata: null,
22
+ },
23
+ {
24
+ id: "msg-parent-2",
25
+ conversationId: "parent-conv",
26
+ role: "assistant",
27
+ content: JSON.stringify([{ type: "text", text: "spawning subagent" }]),
28
+ createdAt: 1_700_000_001_000,
29
+ metadata: JSON.stringify({
30
+ subagentNotification: {
31
+ subagentId: "sa-1",
32
+ label: "research-foo",
33
+ status: "completed",
34
+ conversationId: "child-conv-1",
35
+ },
36
+ }),
37
+ },
38
+ ];
39
+
40
+ const childMessages: TestMessage[] = [
41
+ {
42
+ id: "msg-child-1",
43
+ conversationId: "child-conv-1",
44
+ role: "user",
45
+ content: JSON.stringify([
46
+ { type: "text", text: "Objective: research foo and report back." },
47
+ ]),
48
+ createdAt: 1_700_000_002_000,
49
+ metadata: null,
50
+ },
51
+ {
52
+ id: "msg-child-2",
53
+ conversationId: "child-conv-1",
54
+ role: "assistant",
55
+ content: JSON.stringify([
56
+ { type: "text", text: "I found that foo is a bar." },
57
+ ]),
58
+ createdAt: 1_700_000_003_000,
59
+ metadata: null,
60
+ },
61
+ ];
62
+
63
+ const messageMetadataSchema = z.object({
64
+ subagentNotification: z
65
+ .object({
66
+ subagentId: z.string(),
67
+ label: z.string(),
68
+ status: z.enum(["running", "completed", "failed", "aborted"]),
69
+ conversationId: z.string().optional(),
70
+ })
71
+ .optional(),
72
+ });
73
+
74
+ mock.module("../../memory/conversation-crud.js", () => ({
75
+ getConversation: (_id: string) => null,
76
+ getMessages: (id: string) =>
77
+ id === "child-conv-1" ? childMessages : parentMessages,
78
+ messageMetadataSchema,
79
+ }));
80
+
81
+ mock.module("../../util/truncate.js", () => ({
82
+ truncate: (s: string) => s,
83
+ }));
84
+
85
+ mock.module("../../daemon/date-context.js", () => ({
86
+ formatLocalTimestamp: (_ts: number, _tz?: string) => "TIME",
87
+ }));
88
+
89
+ const { formatMessageSliceForTranscript } =
90
+ await import("../transcript-formatter.js");
91
+
92
+ describe("formatMessageSliceForTranscript subagent labels", () => {
93
+ test("embedded subagent transcripts render with generic role labels even when parent display names are provided", () => {
94
+ const out = formatMessageSliceForTranscript(parentMessages, {
95
+ assistantName: "Bob",
96
+ userName: "Alice",
97
+ });
98
+
99
+ // Parent messages use the provided display names.
100
+ expect(out).toContain("## Alice (TIME)");
101
+ expect(out).toContain("## Bob (TIME)");
102
+
103
+ // Subagent block headers must use generic labels — the child "user" message
104
+ // is actually the parent assistant's objective, so labeling it "Alice"
105
+ // would misattribute the assistant's tasking text to the human user.
106
+ expect(out).toContain("### Subagent: research-foo (completed)");
107
+ expect(out).toContain("> **User** (TIME)");
108
+ expect(out).toContain("> **Assistant** (TIME)");
109
+ expect(out).not.toContain("> **Alice**");
110
+ expect(out).not.toContain("> **Bob**");
111
+ });
112
+
113
+ test("without display-name options, parent and subagent both use generic labels", () => {
114
+ const out = formatMessageSliceForTranscript(parentMessages);
115
+
116
+ expect(out).toContain("## User (TIME)");
117
+ expect(out).toContain("## Assistant (TIME)");
118
+ expect(out).toContain("> **User** (TIME)");
119
+ expect(out).toContain("> **Assistant** (TIME)");
120
+ });
121
+ });
@@ -5,6 +5,7 @@
5
5
  * subagent conversation sections when present in message metadata.
6
6
  */
7
7
 
8
+ import { formatLocalTimestamp } from "../daemon/date-context.js";
8
9
  import {
9
10
  getConversation,
10
11
  getMessages,
@@ -23,10 +24,6 @@ interface ContentBlock {
23
24
  source?: { media_type?: string; filename?: string };
24
25
  }
25
26
 
26
- function formatTimestamp(ms: number): string {
27
- return new Date(ms).toISOString().replace("T", " ").slice(0, 19);
28
- }
29
-
30
27
  function extractAnalysisText(blocks: ContentBlock[]): string {
31
28
  const parts: string[] = [];
32
29
  for (const block of blocks) {
@@ -67,15 +64,36 @@ function extractAnalysisText(blocks: ContentBlock[]): string {
67
64
  return parts.join("\n");
68
65
  }
69
66
 
70
- function formatRole(role: string): string {
71
- return role === "user" ? "User" : "Assistant";
67
+ export interface TranscriptFormatOptions {
68
+ timeZone?: string;
69
+ assistantName?: string | null;
70
+ userName?: string | null;
71
+ }
72
+
73
+ function resolveName(
74
+ name: string | null | undefined,
75
+ fallback: string,
76
+ ): string {
77
+ return name && name.length > 0 ? name : fallback;
78
+ }
79
+
80
+ function formatRole(
81
+ role: string,
82
+ options: TranscriptFormatOptions = {},
83
+ ): string {
84
+ return role === "user"
85
+ ? resolveName(options.userName, "User")
86
+ : resolveName(options.assistantName, "Assistant");
72
87
  }
73
88
 
74
- function formatSubagentMessages(msgs: ReturnType<typeof getMessages>): string {
89
+ function formatSubagentMessages(
90
+ msgs: ReturnType<typeof getMessages>,
91
+ options: TranscriptFormatOptions = {},
92
+ ): string {
75
93
  const lines: string[] = [];
76
94
  for (const msg of msgs) {
77
- const role = formatRole(msg.role);
78
- const time = formatTimestamp(msg.createdAt);
95
+ const role = formatRole(msg.role, options);
96
+ const time = formatLocalTimestamp(msg.createdAt, options.timeZone);
79
97
  const content = parseContent(msg.content);
80
98
  const text = extractAnalysisText(content);
81
99
  if (text) {
@@ -103,24 +121,33 @@ type TranscriptMessage = ReturnType<typeof getMessages>[number];
103
121
  * Format a slice of messages as a transcript body (no top-of-conversation
104
122
  * header). Used by background jobs that process incremental slices — the
105
123
  * memory-retrospective job re-renders only the messages added since its
106
- * last successful run rather than the whole conversation. The format
107
- * matches `buildAnalysisTranscript` per message so downstream agents see a
108
- * consistent shape regardless of whether the input is a full transcript or
109
- * a slice.
124
+ * last successful run rather than the whole conversation. The per-message
125
+ * structural shape matches `buildAnalysisTranscript` (header line, body,
126
+ * optional subagent block) so downstream agents see consistent framing.
127
+ * The participant *labels*, however, intentionally diverge: this function
128
+ * honors `TranscriptFormatOptions` so the memory-retrospective prompt can
129
+ * render the conversation under the assistant and user display names,
130
+ * while `buildAnalysisTranscript` always uses generic "User"/"Assistant"
131
+ * labels for the analyze-conversation flow.
110
132
  */
111
133
  export function formatMessageSliceForTranscript(
112
134
  messages: TranscriptMessage[],
135
+ options: TranscriptFormatOptions = {},
113
136
  ): string {
114
137
  const lines: string[] = [];
115
138
  for (const msg of messages) {
116
- appendMessageBlock(lines, msg);
139
+ appendMessageBlock(lines, msg, options);
117
140
  }
118
141
  return lines.join("\n");
119
142
  }
120
143
 
121
- function appendMessageBlock(lines: string[], msg: TranscriptMessage): void {
122
- const role = formatRole(msg.role);
123
- const time = formatTimestamp(msg.createdAt);
144
+ function appendMessageBlock(
145
+ lines: string[],
146
+ msg: TranscriptMessage,
147
+ options: TranscriptFormatOptions = {},
148
+ ): void {
149
+ const role = formatRole(msg.role, options);
150
+ const time = formatLocalTimestamp(msg.createdAt, options.timeZone);
124
151
  const content = parseContent(msg.content);
125
152
  const text = extractAnalysisText(content);
126
153
 
@@ -142,7 +169,14 @@ function appendMessageBlock(lines: string[], msg: TranscriptMessage): void {
142
169
  const subMessages = getMessages(notif.conversationId);
143
170
  lines.push(`### Subagent: ${notif.label} (${notif.status})`);
144
171
  lines.push("");
145
- lines.push(formatSubagentMessages(subMessages));
172
+ // Subagent conversations persist the parent assistant's objective
173
+ // as a `user` message (see subagent/manager.ts), so reusing the
174
+ // parent's display-name options would render the assistant's
175
+ // tasking text under the human user's name. Keep child transcripts
176
+ // on generic role labels — and only pass through the time zone.
177
+ lines.push(
178
+ formatSubagentMessages(subMessages, { timeZone: options.timeZone }),
179
+ );
146
180
  lines.push("");
147
181
  }
148
182
  }
@@ -163,12 +197,12 @@ export function buildAnalysisTranscript(conversationId: string): string {
163
197
  const lines: string[] = [];
164
198
 
165
199
  lines.push(`# Conversation: ${title}`);
166
- lines.push(`Created: ${formatTimestamp(conversation.createdAt)}`);
200
+ lines.push(`Created: ${formatLocalTimestamp(conversation.createdAt)}`);
167
201
  lines.push("");
168
202
 
169
203
  for (const msg of allMessages) {
170
204
  const role = formatRole(msg.role);
171
- const time = formatTimestamp(msg.createdAt);
205
+ const time = formatLocalTimestamp(msg.createdAt);
172
206
  const content = parseContent(msg.content);
173
207
  const text = extractAnalysisText(content);
174
208
 
@@ -17,6 +17,11 @@ const log = getLogger("filing-service");
17
17
 
18
18
  const FILING_TIMEOUT_MS = 15 * 60 * 1000; // 15 minutes
19
19
 
20
+ // When compaction skips because a filing run holds the serialization lock,
21
+ // retry on this near-term cadence so phase-aligned 24h timers don't starve
22
+ // compaction across consecutive ticks.
23
+ const COMPACTION_CONTENDED_RETRY_MS = 10 * 60 * 1000; // 10 minutes
24
+
20
25
  const FILING_PROMPT_TEMPLATE = `You are running a periodic knowledge base filing job. This is a background maintenance task focused on the buffer.
21
26
 
22
27
  Read \`pkb/buffer.md\`. For each item in the buffer:
@@ -66,6 +71,7 @@ This is your knowledge base — keep it sharp.`;
66
71
 
67
72
  export interface FilingDeps {
68
73
  getCurrentHour?: () => number;
74
+ compactionContendedRetryMs?: number;
69
75
  }
70
76
 
71
77
  export class FilingService {
@@ -79,8 +85,10 @@ export class FilingService {
79
85
  private readonly deps: FilingDeps;
80
86
  private timer: ReturnType<typeof setInterval> | null = null;
81
87
  private compactionTimer: ReturnType<typeof setInterval> | null = null;
88
+ private compactionRetryTimer: ReturnType<typeof setTimeout> | null = null;
82
89
  private activeRun: Promise<void> | null = null;
83
90
  private activeCompactionRun: Promise<void> | null = null;
91
+ private stopped = false;
84
92
  private _lastRunAt: number | null = null;
85
93
  private _nextRunAt: number | null = null;
86
94
  private _lastCompactionAt: number | null = null;
@@ -108,6 +116,7 @@ export class FilingService {
108
116
  }
109
117
 
110
118
  start(): void {
119
+ this.stopped = false;
111
120
  const fullConfig = getConfig();
112
121
  if (fullConfig.memory.v2.enabled) {
113
122
  log.info("Filing service disabled — memory v2 is active");
@@ -157,12 +166,14 @@ export class FilingService {
157
166
  clearInterval(this.compactionTimer);
158
167
  this.compactionTimer = null;
159
168
  }
169
+ this.clearCompactionRetry();
160
170
  this._nextRunAt = null;
161
171
  this._nextCompactionAt = null;
162
172
  this.start();
163
173
  }
164
174
 
165
175
  async stop(): Promise<void> {
176
+ this.stopped = true;
166
177
  if (this.timer) {
167
178
  clearInterval(this.timer);
168
179
  this.timer = null;
@@ -171,6 +182,7 @@ export class FilingService {
171
182
  clearInterval(this.compactionTimer);
172
183
  this.compactionTimer = null;
173
184
  }
185
+ this.clearCompactionRetry();
174
186
  this._nextRunAt = null;
175
187
  this._nextCompactionAt = null;
176
188
  const inflight: Promise<void>[] = [];
@@ -269,9 +281,13 @@ export class FilingService {
269
281
  log.debug(
270
282
  "Filing run in progress, skipping compaction to avoid concurrent PKB writes",
271
283
  );
284
+ this.scheduleCompactionRetry(
285
+ this.deps.compactionContendedRetryMs ?? COMPACTION_CONTENDED_RETRY_MS,
286
+ );
272
287
  return false;
273
288
  }
274
289
 
290
+ this.clearCompactionRetry();
275
291
  const run = this.executeCompactionRun();
276
292
  this.activeCompactionRun = run;
277
293
  try {
@@ -301,6 +317,29 @@ export class FilingService {
301
317
  this._nextCompactionAt = Date.now() + intervalMs;
302
318
  }
303
319
 
320
+ private scheduleCompactionRetry(delayMs: number): void {
321
+ this.clearCompactionRetry();
322
+ if (this.stopped) return;
323
+ this.compactionRetryTimer = setTimeout(() => {
324
+ this.compactionRetryTimer = null;
325
+ if (this.stopped) return;
326
+ this.runCompactionOnce().catch((err) => {
327
+ log.error({ err }, "Compaction retry failed");
328
+ });
329
+ }, delayMs);
330
+ // unref so the pending retry doesn't keep the daemon process alive on
331
+ // shutdown paths that don't call stop().
332
+ this.compactionRetryTimer.unref?.();
333
+ this._nextCompactionAt = Date.now() + delayMs;
334
+ }
335
+
336
+ private clearCompactionRetry(): void {
337
+ if (this.compactionRetryTimer) {
338
+ clearTimeout(this.compactionRetryTimer);
339
+ this.compactionRetryTimer = null;
340
+ }
341
+ }
342
+
304
343
  private shouldSkipForDiskPressure(source: "filing" | "compaction"): boolean {
305
344
  const diskPressureGate = checkDiskPressureBackgroundGate("background-work");
306
345
  if (diskPressureGate.action === "allow") return false;