@vellumai/assistant 0.8.3 → 0.8.4

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 (342) hide show
  1. package/docker-entrypoint.sh +0 -1
  2. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  3. package/openapi.yaml +610 -16
  4. package/package.json +1 -1
  5. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  6. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  7. package/src/__tests__/agent-loop.test.ts +88 -3
  8. package/src/__tests__/anthropic-provider.test.ts +272 -0
  9. package/src/__tests__/approval-cascade.test.ts +1 -1
  10. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  11. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  12. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  13. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  14. package/src/__tests__/compaction-events.test.ts +1 -1
  15. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  16. package/src/__tests__/config-watcher.test.ts +1 -1
  17. package/src/__tests__/context-token-estimator.test.ts +91 -1
  18. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  19. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
  20. package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
  21. package/src/__tests__/conversation-agent-loop.test.ts +25 -7
  22. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  23. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  24. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  25. package/src/__tests__/conversation-fork-crud.test.ts +161 -0
  26. package/src/__tests__/conversation-lifecycle.test.ts +1 -1
  27. package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
  28. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  29. package/src/__tests__/conversation-pairing.test.ts +2 -2
  30. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  31. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
  32. package/src/__tests__/conversation-queue.test.ts +1 -1
  33. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  34. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  35. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  36. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  37. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  38. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  39. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  40. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
  41. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  42. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  43. package/src/__tests__/credential-security-invariants.test.ts +6 -0
  44. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  45. package/src/__tests__/dm-backfill.test.ts +64 -0
  46. package/src/__tests__/dm-persistence.test.ts +33 -0
  47. package/src/__tests__/document-find-replace.test.ts +501 -0
  48. package/src/__tests__/first-greeting.test.ts +23 -2
  49. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  50. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  51. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  52. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  53. package/src/__tests__/host-file-proxy.test.ts +8 -1
  54. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  55. package/src/__tests__/identity-routes.test.ts +57 -0
  56. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  57. package/src/__tests__/injector-chain.test.ts +2 -0
  58. package/src/__tests__/injector-document-comments.test.ts +378 -0
  59. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  60. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  61. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  62. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  63. package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
  64. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  65. package/src/__tests__/llm-resolver.test.ts +85 -1
  66. package/src/__tests__/log-export-routes.test.ts +99 -2
  67. package/src/__tests__/message-queue-steer.test.ts +114 -0
  68. package/src/__tests__/openai-provider.test.ts +105 -0
  69. package/src/__tests__/openai-responses-provider.test.ts +4 -4
  70. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  71. package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
  72. package/src/__tests__/platform.test.ts +0 -3
  73. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  74. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  75. package/src/__tests__/process-message-display-content.test.ts +21 -16
  76. package/src/__tests__/server-history-render.test.ts +83 -4
  77. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  78. package/src/__tests__/system-prompt.test.ts +51 -28
  79. package/src/__tests__/terminal-tools.test.ts +11 -1
  80. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  81. package/src/__tests__/thread-backfill.test.ts +370 -22
  82. package/src/__tests__/tool-executor.test.ts +90 -1
  83. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  84. package/src/__tests__/twilio-routes.test.ts +1 -1
  85. package/src/__tests__/web-fetch.test.ts +2 -2
  86. package/src/__tests__/workspace-git-service.test.ts +88 -5
  87. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  88. package/src/agent/attachments.ts +1 -0
  89. package/src/agent/loop.ts +57 -20
  90. package/src/background-wake/next-wake.test.ts +289 -0
  91. package/src/background-wake/next-wake.ts +172 -0
  92. package/src/browser/operations.ts +15 -0
  93. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  94. package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
  95. package/src/cli/commands/conversations.ts +128 -1
  96. package/src/cli/commands/inference-providers.ts +147 -1
  97. package/src/cli/commands/memory-v2.ts +308 -0
  98. package/src/cli/commands/notifications.ts +24 -2
  99. package/src/cli/utils/conversation-id.ts +17 -5
  100. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  101. package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
  102. package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
  103. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  104. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  105. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  106. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  107. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  108. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  109. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  110. package/src/config/bundled-tool-registry.ts +22 -12
  111. package/src/config/call-site-defaults.ts +19 -0
  112. package/src/config/feature-flag-registry.json +99 -3
  113. package/src/config/llm-resolver.ts +16 -2
  114. package/src/config/schemas/__tests__/memory-v2.test.ts +4 -0
  115. package/src/config/schemas/call-site-catalog.ts +21 -0
  116. package/src/config/schemas/llm.ts +3 -0
  117. package/src/config/schemas/memory-v2.ts +48 -1
  118. package/src/context/compactor.ts +8 -1
  119. package/src/context/token-estimator.ts +47 -4
  120. package/src/context/window-manager.ts +25 -0
  121. package/src/credential-health/credential-health-service.ts +34 -19
  122. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  123. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  124. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  125. package/src/daemon/conversation-agent-loop-handlers.ts +153 -23
  126. package/src/daemon/conversation-agent-loop.ts +223 -54
  127. package/src/daemon/conversation-lifecycle.ts +142 -116
  128. package/src/daemon/conversation-messaging.ts +3 -0
  129. package/src/daemon/conversation-process.ts +273 -0
  130. package/src/daemon/conversation-queue-manager.ts +14 -0
  131. package/src/daemon/conversation-runtime-assembly.ts +135 -75
  132. package/src/daemon/conversation-slash.ts +37 -5
  133. package/src/daemon/conversation-surfaces.ts +45 -2
  134. package/src/daemon/conversation-tool-setup.ts +7 -0
  135. package/src/daemon/conversation.ts +42 -5
  136. package/src/daemon/first-greeting.ts +10 -0
  137. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  138. package/src/daemon/handlers/config-a2a.ts +160 -0
  139. package/src/daemon/handlers/config-model.test.ts +1 -0
  140. package/src/daemon/handlers/conversations.ts +79 -0
  141. package/src/daemon/handlers/shared.ts +92 -29
  142. package/src/daemon/host-bash-proxy.ts +1 -1
  143. package/src/daemon/host-cu-proxy.ts +1 -1
  144. package/src/daemon/host-file-proxy.ts +1 -1
  145. package/src/daemon/host-transfer-proxy.ts +1 -1
  146. package/src/daemon/lifecycle.ts +18 -4
  147. package/src/daemon/message-protocol.ts +4 -0
  148. package/src/daemon/message-types/conversations.ts +8 -0
  149. package/src/daemon/message-types/document-comments.ts +50 -0
  150. package/src/daemon/message-types/messages.ts +68 -1
  151. package/src/daemon/message-types/surfaces.ts +3 -1
  152. package/src/daemon/message-types/web-activity.ts +57 -0
  153. package/src/daemon/plugin-source-watcher.ts +135 -3
  154. package/src/daemon/process-message.ts +69 -12
  155. package/src/daemon/query-complexity-router.ts +75 -0
  156. package/src/daemon/trust-context.ts +6 -0
  157. package/src/documents/document-comments-store.test.ts +338 -0
  158. package/src/documents/document-comments-store.ts +237 -0
  159. package/src/documents/document-store.ts +202 -0
  160. package/src/heartbeat/__tests__/heartbeat-service.test.ts +0 -1
  161. package/src/heartbeat/heartbeat-service.ts +1 -0
  162. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  163. package/src/home/feed-types.ts +6 -1
  164. package/src/home/home-content-refresh.ts +52 -0
  165. package/src/home/home-greeting-cache.ts +69 -0
  166. package/src/home/home-greeting.ts +94 -0
  167. package/src/home/suggested-prompts.ts +177 -9
  168. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  169. package/src/memory/__tests__/memory-retrospective-job.test.ts +320 -6
  170. package/src/memory/conversation-crud.ts +133 -43
  171. package/src/memory/db-init.ts +16 -0
  172. package/src/memory/delivery-crud.ts +41 -0
  173. package/src/memory/delivery-status.ts +141 -15
  174. package/src/memory/external-conversation-store.ts +32 -1
  175. package/src/memory/jobs-worker.ts +21 -1
  176. package/src/memory/memory-retrospective-constants.ts +28 -0
  177. package/src/memory/memory-retrospective-enqueue.ts +3 -2
  178. package/src/memory/memory-retrospective-job.ts +408 -18
  179. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  180. package/src/memory/memory-v2-activation-log-store.ts +26 -8
  181. package/src/memory/migrations/100-core-tables.ts +1 -0
  182. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  183. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  184. package/src/memory/migrations/253-document-comments.ts +47 -0
  185. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  186. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  187. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  188. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  189. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  190. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  191. package/src/memory/migrations/index.ts +17 -0
  192. package/src/memory/migrations/registry.ts +25 -0
  193. package/src/memory/onboarding-events-store.ts +7 -0
  194. package/src/memory/schema/calls.ts +1 -0
  195. package/src/memory/schema/conversations.ts +3 -0
  196. package/src/memory/schema/infrastructure.ts +1 -0
  197. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  198. package/src/memory/v2/__tests__/injection.test.ts +31 -14
  199. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  200. package/src/memory/v2/__tests__/router.test.ts +489 -1
  201. package/src/memory/v2/consolidation-job.ts +14 -0
  202. package/src/memory/v2/injection-events.ts +101 -0
  203. package/src/memory/v2/injection.ts +21 -10
  204. package/src/memory/v2/page-index.ts +209 -7
  205. package/src/memory/v2/page-store.ts +18 -0
  206. package/src/memory/v2/router.ts +209 -55
  207. package/src/messaging/providers/index.ts +7 -1
  208. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  209. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  210. package/src/messaging/providers/slack/adapter.ts +178 -25
  211. package/src/messaging/providers/slack/api.test.ts +54 -0
  212. package/src/messaging/providers/slack/api.ts +119 -3
  213. package/src/messaging/providers/slack/client.ts +12 -0
  214. package/src/messaging/providers/slack/deep-link.ts +20 -1
  215. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  216. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  217. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  218. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  219. package/src/messaging/providers/slack/send.test.ts +77 -0
  220. package/src/messaging/providers/slack/send.ts +8 -2
  221. package/src/messaging/providers/slack/types.ts +14 -0
  222. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  223. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  224. package/src/notifications/conversation-seed-composer.ts +14 -2
  225. package/src/notifications/deferred-emit.ts +135 -0
  226. package/src/notifications/emit-signal.ts +9 -1
  227. package/src/notifications/home-feed-side-effect.ts +60 -30
  228. package/src/oauth/connect-orchestrator.ts +3 -0
  229. package/src/oauth/credential-token-resolver.ts +2 -0
  230. package/src/oauth/manual-token-connection.ts +19 -0
  231. package/src/oauth/oauth-store.ts +12 -0
  232. package/src/oauth/seed-providers.ts +22 -0
  233. package/src/permissions/prompter.ts +5 -2
  234. package/src/permissions/secret-prompter.ts +4 -1
  235. package/src/plugins/defaults/injectors.ts +82 -9
  236. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  237. package/src/prompts/normalize-onboarding.ts +40 -0
  238. package/src/prompts/sections.ts +32 -14
  239. package/src/prompts/system-prompt.ts +105 -68
  240. package/src/prompts/template-detection.ts +37 -0
  241. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  242. package/src/prompts/templates/BOOTSTRAP.md +8 -0
  243. package/src/prompts/templates/VOICE.md +3 -0
  244. package/src/prompts/templates/system-sections.ts +53 -3
  245. package/src/providers/anthropic/client.ts +132 -5
  246. package/src/providers/fireworks/client.ts +20 -2
  247. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  248. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  249. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  250. package/src/providers/inference/adapter-factory.ts +15 -1
  251. package/src/providers/inference/auth.ts +3 -3
  252. package/src/providers/inference/codex-token-refresh.ts +128 -0
  253. package/src/providers/inference/resolve-auth.ts +49 -6
  254. package/src/providers/model-catalog.ts +48 -1
  255. package/src/providers/openai/chat-completions-provider.ts +57 -20
  256. package/src/providers/openai/responses-provider.ts +9 -3
  257. package/src/providers/openrouter/client.ts +5 -1
  258. package/src/providers/types.ts +25 -0
  259. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  260. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  261. package/src/runtime/agent-wake.ts +151 -56
  262. package/src/runtime/auth/route-policy.ts +7 -3
  263. package/src/runtime/background-job-runner.ts +26 -0
  264. package/src/runtime/channel-reply-delivery.ts +182 -47
  265. package/src/runtime/channel-retry-sweep.ts +141 -16
  266. package/src/runtime/http-types.ts +7 -4
  267. package/src/runtime/pending-interactions.ts +51 -8
  268. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  269. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +55 -1
  270. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  271. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
  272. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  273. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  274. package/src/runtime/routes/approval-routes.ts +4 -1
  275. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  276. package/src/runtime/routes/content-source-routes.ts +78 -0
  277. package/src/runtime/routes/conversation-cli-routes.ts +146 -1
  278. package/src/runtime/routes/conversation-query-routes.ts +60 -1
  279. package/src/runtime/routes/conversation-routes.ts +281 -76
  280. package/src/runtime/routes/document-comments-routes.ts +287 -0
  281. package/src/runtime/routes/documents-routes.ts +33 -0
  282. package/src/runtime/routes/home-feed-routes.ts +6 -3
  283. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  284. package/src/runtime/routes/host-browser-routes.ts +8 -1
  285. package/src/runtime/routes/identity-routes.ts +21 -0
  286. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  287. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  288. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  289. package/src/runtime/routes/index.ts +12 -4
  290. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  291. package/src/runtime/routes/integrations/a2a.ts +60 -1
  292. package/src/runtime/routes/log-export-routes.ts +39 -0
  293. package/src/runtime/routes/memory-v2-routes.ts +217 -0
  294. package/src/runtime/routes/notification-routes.ts +19 -2
  295. package/src/runtime/routes/question-routes.ts +4 -1
  296. package/src/runtime/routes/sanity-routes.ts +159 -0
  297. package/src/runtime/routes/slack-channel-routes.ts +187 -0
  298. package/src/runtime/services/conversation-serializer.ts +30 -4
  299. package/src/schedule/integration-status.ts +3 -1
  300. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  301. package/src/security/oauth2-device-code.ts +307 -0
  302. package/src/security/oauth2.ts +26 -9
  303. package/src/security/secure-keys.ts +5 -0
  304. package/src/skills/catalog-install.ts +6 -2
  305. package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
  306. package/src/tools/browser/browser-execution.ts +93 -0
  307. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  308. package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
  309. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
  310. package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
  311. package/src/tools/browser/cdp-client/factory.ts +87 -3
  312. package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
  313. package/src/tools/browser/cdp-client/types.ts +36 -0
  314. package/src/tools/browser/pinned-tabs.ts +90 -0
  315. package/src/tools/document/document-comment-tool.test.ts +379 -0
  316. package/src/tools/document/document-comment-tool.ts +156 -0
  317. package/src/tools/document/document-tool.ts +128 -2
  318. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  319. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  320. package/src/tools/network/domain-normalize.ts +17 -0
  321. package/src/tools/network/web-fetch.ts +213 -64
  322. package/src/tools/network/web-search.ts +191 -66
  323. package/src/tools/terminal/safe-env.ts +3 -2
  324. package/src/tools/tool-approval-handler.ts +19 -12
  325. package/src/tools/types.ts +4 -0
  326. package/src/tools/ui-surface/definitions.ts +3 -1
  327. package/src/types/onboarding-context.ts +4 -0
  328. package/src/util/__tests__/favicon.test.ts +84 -0
  329. package/src/util/favicon.ts +40 -0
  330. package/src/util/platform.ts +0 -5
  331. package/src/workspace/git-service.ts +75 -4
  332. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  333. package/src/workspace/migrations/registry.ts +2 -0
  334. package/src/config/bundled-skills/document/SKILL.md +0 -54
  335. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  336. package/src/daemon/seed-files.ts +0 -18
  337. package/src/runtime/routes/interface-routes.ts +0 -43
  338. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  339. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  340. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  341. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  342. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -0,0 +1,378 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ import type { CommentRecord } from "../documents/document-comments-store.js";
4
+
5
+ let listCommentsMock = mock((..._args: unknown[]) => [] as CommentRecord[]);
6
+
7
+ mock.module("../documents/document-comments-store.js", () => ({
8
+ listComments: (...args: unknown[]) => listCommentsMock(...args),
9
+ }));
10
+
11
+ const { DEFAULT_INJECTOR_ORDER, defaultInjectorsPlugin } =
12
+ await import("../plugins/defaults/injectors.js");
13
+ import type { Injector, TurnContext } from "../plugins/types.js";
14
+
15
+ function findInjector(name: string): Injector {
16
+ const injector = defaultInjectorsPlugin.injectors?.find(
17
+ (candidate) => candidate.name === name,
18
+ );
19
+ if (!injector) {
20
+ throw new Error(`injector '${name}' not registered`);
21
+ }
22
+ return injector;
23
+ }
24
+
25
+ function makeContext(overrides: Partial<TurnContext> = {}): TurnContext {
26
+ return {
27
+ requestId: "req-test",
28
+ conversationId: "conv-test",
29
+ turnIndex: 0,
30
+ trust: { sourceChannel: "vellum", trustClass: "guardian" },
31
+ ...overrides,
32
+ };
33
+ }
34
+
35
+ function makeComment(overrides: Partial<CommentRecord> = {}): CommentRecord {
36
+ return {
37
+ id: "comment-abc",
38
+ surfaceId: "doc-1",
39
+ conversationId: "conv-test",
40
+ author: "user",
41
+ content: "Fix this paragraph",
42
+ anchorStart: null,
43
+ anchorEnd: null,
44
+ anchorText: null,
45
+ parentCommentId: null,
46
+ status: "open",
47
+ resolvedBy: null,
48
+ resolvedAt: null,
49
+ createdAt: 1000,
50
+ updatedAt: 1000,
51
+ ...overrides,
52
+ };
53
+ }
54
+
55
+ const injector = findInjector("document-comments");
56
+
57
+ describe("document-comments injector", () => {
58
+ beforeEach(() => {
59
+ listCommentsMock = mock(() => [] as CommentRecord[]);
60
+ });
61
+
62
+ test("returns null when no active documents exist", async () => {
63
+ const block = await injector.produce(
64
+ makeContext({
65
+ injectionInputs: { mode: "full", activeDocuments: [] },
66
+ }),
67
+ );
68
+ expect(block).toBeNull();
69
+ });
70
+
71
+ test("returns null when activeDocuments is undefined", async () => {
72
+ const block = await injector.produce(
73
+ makeContext({
74
+ injectionInputs: { mode: "full" },
75
+ }),
76
+ );
77
+ expect(block).toBeNull();
78
+ });
79
+
80
+ test("returns null when mode is minimal", async () => {
81
+ const block = await injector.produce(
82
+ makeContext({
83
+ injectionInputs: {
84
+ mode: "minimal",
85
+ activeDocuments: [
86
+ {
87
+ surfaceId: "doc-1",
88
+ title: "My Doc",
89
+ wordCount: 100,
90
+ updatedAt: 1000,
91
+ },
92
+ ],
93
+ },
94
+ }),
95
+ );
96
+ expect(block).toBeNull();
97
+ });
98
+
99
+ test("returns null when no documents have open comments", async () => {
100
+ listCommentsMock = mock(() => []);
101
+ const block = await injector.produce(
102
+ makeContext({
103
+ injectionInputs: {
104
+ mode: "full",
105
+ activeDocuments: [
106
+ {
107
+ surfaceId: "doc-1",
108
+ title: "My Doc",
109
+ wordCount: 100,
110
+ updatedAt: 1000,
111
+ },
112
+ ],
113
+ },
114
+ }),
115
+ );
116
+ expect(block).toBeNull();
117
+ });
118
+
119
+ test("formats doc-level comments correctly", async () => {
120
+ listCommentsMock = mock(() => [
121
+ makeComment({
122
+ id: "comment-id1",
123
+ content: "This introduction needs more context",
124
+ anchorText: null,
125
+ }),
126
+ ]);
127
+
128
+ const block = await injector.produce(
129
+ makeContext({
130
+ injectionInputs: {
131
+ mode: "full",
132
+ activeDocuments: [
133
+ {
134
+ surfaceId: "doc-1",
135
+ title: "My Doc",
136
+ wordCount: 100,
137
+ updatedAt: 1000,
138
+ },
139
+ ],
140
+ },
141
+ }),
142
+ );
143
+
144
+ expect(block).not.toBeNull();
145
+ expect(block!.id).toBe("document-comments");
146
+ expect(block!.placement).toBe("prepend-user-tail");
147
+ expect(block!.text).toContain("(doc-level)");
148
+ expect(block!.text).toContain("Comment #comment-id1");
149
+ expect(block!.text).toContain('"This introduction needs more context"');
150
+ });
151
+
152
+ test("formats inline comments with anchor text", async () => {
153
+ listCommentsMock = mock(() => [
154
+ makeComment({
155
+ id: "comment-id2",
156
+ content: "Cite the research paper",
157
+ anchorText: "the quick brown fox",
158
+ anchorStart: 10,
159
+ anchorEnd: 29,
160
+ }),
161
+ ]);
162
+
163
+ const block = await injector.produce(
164
+ makeContext({
165
+ injectionInputs: {
166
+ mode: "full",
167
+ activeDocuments: [
168
+ {
169
+ surfaceId: "doc-1",
170
+ title: "My Doc",
171
+ wordCount: 100,
172
+ updatedAt: 1000,
173
+ },
174
+ ],
175
+ },
176
+ }),
177
+ );
178
+
179
+ expect(block).not.toBeNull();
180
+ expect(block!.text).toContain('inline, anchored to "the quick brown fox"');
181
+ expect(block!.text).toContain("Comment #comment-id2");
182
+ expect(block!.text).toContain('"Cite the research paper"');
183
+ });
184
+
185
+ test("mixes doc-level and inline comments for the same document", async () => {
186
+ listCommentsMock = mock(() => [
187
+ makeComment({
188
+ id: "comment-id1",
189
+ content: "This introduction needs more context",
190
+ anchorText: null,
191
+ }),
192
+ makeComment({
193
+ id: "comment-id2",
194
+ content: "Cite the research paper",
195
+ anchorText: "the quick brown fox",
196
+ anchorStart: 10,
197
+ anchorEnd: 29,
198
+ }),
199
+ ]);
200
+
201
+ const block = await injector.produce(
202
+ makeContext({
203
+ injectionInputs: {
204
+ mode: "full",
205
+ activeDocuments: [
206
+ {
207
+ surfaceId: "doc-xxx",
208
+ title: "Title",
209
+ wordCount: 200,
210
+ updatedAt: 1000,
211
+ },
212
+ ],
213
+ },
214
+ }),
215
+ );
216
+
217
+ expect(block).not.toBeNull();
218
+ expect(block!.text).toContain('Document: "Title" (surface_id: "doc-xxx")');
219
+ expect(block!.text).toContain(
220
+ '- Comment #comment-id1 (doc-level): "This introduction needs more context"',
221
+ );
222
+ expect(block!.text).toContain(
223
+ '- Comment #comment-id2 (inline, anchored to "the quick brown fox"): "Cite the research paper"',
224
+ );
225
+ });
226
+
227
+ test("respects the 10-comment cap per document", async () => {
228
+ const comments = Array.from({ length: 15 }, (_, i) =>
229
+ makeComment({
230
+ id: `comment-${i}`,
231
+ content: `Comment number ${i}`,
232
+ createdAt: 1000 + i,
233
+ }),
234
+ );
235
+ listCommentsMock = mock(() => comments);
236
+
237
+ const block = await injector.produce(
238
+ makeContext({
239
+ injectionInputs: {
240
+ mode: "full",
241
+ activeDocuments: [
242
+ {
243
+ surfaceId: "doc-1",
244
+ title: "Big Doc",
245
+ wordCount: 500,
246
+ updatedAt: 1000,
247
+ },
248
+ ],
249
+ },
250
+ }),
251
+ );
252
+
253
+ expect(block).not.toBeNull();
254
+ // The store returns ASC order; .slice(-10) takes the 10 most recent
255
+ for (let i = 5; i < 15; i++) {
256
+ expect(block!.text).toContain(`Comment #comment-${i}`);
257
+ }
258
+ // Earlier comments should be excluded (use exact line match to avoid
259
+ // substring collisions like "comment-1" matching "comment-10")
260
+ for (let i = 0; i < 5; i++) {
261
+ expect(block!.text).not.toContain(`Comment #comment-${i} (`);
262
+ }
263
+ });
264
+
265
+ test("handles multiple documents with comments", async () => {
266
+ let callCount = 0;
267
+ listCommentsMock = mock(() => {
268
+ callCount++;
269
+ if (callCount === 1) {
270
+ return [makeComment({ id: "c1", content: "Fix typo" })];
271
+ }
272
+ return [
273
+ makeComment({
274
+ id: "c2",
275
+ content: "Add citation",
276
+ anchorText: "some text",
277
+ anchorStart: 0,
278
+ anchorEnd: 9,
279
+ }),
280
+ ];
281
+ });
282
+
283
+ const block = await injector.produce(
284
+ makeContext({
285
+ injectionInputs: {
286
+ mode: "full",
287
+ activeDocuments: [
288
+ {
289
+ surfaceId: "doc-1",
290
+ title: "Doc A",
291
+ wordCount: 100,
292
+ updatedAt: 1000,
293
+ },
294
+ {
295
+ surfaceId: "doc-2",
296
+ title: "Doc B",
297
+ wordCount: 200,
298
+ updatedAt: 2000,
299
+ },
300
+ ],
301
+ },
302
+ }),
303
+ );
304
+
305
+ expect(block).not.toBeNull();
306
+ expect(block!.text).toContain('Document: "Doc A" (surface_id: "doc-1")');
307
+ expect(block!.text).toContain('Document: "Doc B" (surface_id: "doc-2")');
308
+ expect(block!.text).toContain("Comment #c1");
309
+ expect(block!.text).toContain("Comment #c2");
310
+ });
311
+
312
+ test("skips documents with zero open comments", async () => {
313
+ let callCount = 0;
314
+ listCommentsMock = mock(() => {
315
+ callCount++;
316
+ if (callCount === 1) return [];
317
+ return [makeComment({ id: "c1", content: "Needs work" })];
318
+ });
319
+
320
+ const block = await injector.produce(
321
+ makeContext({
322
+ injectionInputs: {
323
+ mode: "full",
324
+ activeDocuments: [
325
+ {
326
+ surfaceId: "doc-1",
327
+ title: "Empty Doc",
328
+ wordCount: 100,
329
+ updatedAt: 1000,
330
+ },
331
+ {
332
+ surfaceId: "doc-2",
333
+ title: "Commented Doc",
334
+ wordCount: 200,
335
+ updatedAt: 2000,
336
+ },
337
+ ],
338
+ },
339
+ }),
340
+ );
341
+
342
+ expect(block).not.toBeNull();
343
+ expect(block!.text).not.toContain("Empty Doc");
344
+ expect(block!.text).toContain("Commented Doc");
345
+ });
346
+
347
+ test("has the correct order value", () => {
348
+ expect(injector.order).toBe(DEFAULT_INJECTOR_ORDER.documentComments);
349
+ expect(injector.order).toBe(46);
350
+ });
351
+
352
+ test("wraps output in <document_comments> tags with instructions", async () => {
353
+ listCommentsMock = mock(() => [
354
+ makeComment({ id: "c1", content: "Fix this" }),
355
+ ]);
356
+
357
+ const block = await injector.produce(
358
+ makeContext({
359
+ injectionInputs: {
360
+ mode: "full",
361
+ activeDocuments: [
362
+ {
363
+ surfaceId: "doc-1",
364
+ title: "My Doc",
365
+ wordCount: 100,
366
+ updatedAt: 1000,
367
+ },
368
+ ],
369
+ },
370
+ }),
371
+ );
372
+
373
+ expect(block).not.toBeNull();
374
+ expect(block!.text).toMatch(/^<document_comments>/);
375
+ expect(block!.text).toMatch(/<\/document_comments>$/);
376
+ expect(block!.text).toContain("comment_resolve");
377
+ });
378
+ });
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * When `getConfig().memory.v2.enabled` is true:
5
5
  * - `pkb-context` silences itself (concept pages own retrieval).
6
- * - `pkb-reminder` still fires (its body is generic recall/remember
7
- * guidance) but skips the PKB-search hints — those name PKB paths.
6
+ * - `pkb-reminder` silences itself (the v2 static `<memory>` block
7
+ * supplants the generic recall/remember nudge).
8
8
  * - `now-md` fires unchanged (workspace state, independent of PKB).
9
9
  *
10
10
  * Mocks `getConfig` at the module level so each test can flip the effective
@@ -85,7 +85,7 @@ describe("PKB injector v2 cutover behavior", () => {
85
85
  expect(texts.some((t) => t.includes("<NOW.md"))).toBe(true);
86
86
  });
87
87
 
88
- test("v2 active → pkb-context silenced; pkb-reminder + now-md still fire", async () => {
88
+ test("v2 active → pkb-context and pkb-reminder silenced; now-md still fires", async () => {
89
89
  v2Active = true;
90
90
  const result = await applyRuntimeInjections(RUN_MESSAGES, {
91
91
  turnContext: makeTurnContext(),
@@ -99,29 +99,8 @@ describe("PKB injector v2 cutover behavior", () => {
99
99
 
100
100
  const texts = tailTexts(result.messages);
101
101
  expect(texts.some((t) => t.includes("<knowledge_base>"))).toBe(false);
102
- expect(texts.some((t) => t.includes("<system_reminder>"))).toBe(true);
102
+ expect(texts.some((t) => t.includes("<system_reminder>"))).toBe(false);
103
103
  expect(texts.some((t) => t.includes("<NOW.md"))).toBe(true);
104
104
  expect(texts).toContain("What next?");
105
105
  });
106
-
107
- test("v2 active → pkb-reminder body fires without the hybrid-search hints", async () => {
108
- v2Active = true;
109
- const result = await applyRuntimeInjections(RUN_MESSAGES, {
110
- turnContext: makeTurnContext(),
111
- pkbActive: true,
112
- pkbScopeId: "scope-default",
113
- pkbRoot: "/tmp/pkb",
114
- pkbConversation: { messages: [] },
115
- // Provide a query vector so the v1 path WOULD have called searchPkbFiles
116
- // and rendered hints. Under v2, the call is skipped and the reminder
117
- // is rendered with empty hints — i.e. no "files look especially
118
- // relevant" line.
119
- pkbQueryVector: [0.1, 0.2, 0.3],
120
- });
121
-
122
- const texts = tailTexts(result.messages);
123
- const reminder = texts.find((t) => t.includes("<system_reminder>"));
124
- expect(reminder).toBeDefined();
125
- expect(reminder).not.toContain("files look especially relevant");
126
- });
127
106
  });
@@ -78,7 +78,7 @@ describe("handleListMessages attachments", () => {
78
78
  const stored = uploadAttachment("photo.png", "image/png", IMAGE_BASE64);
79
79
  linkAttachmentToMessage(msg.id, stored.id, 0);
80
80
 
81
- const response = handleListMessages(createTestArgs(conv.id), null);
81
+ const response = handleListMessages(createTestArgs(conv.id));
82
82
  const body = response as { messages: MessagePayload[] };
83
83
 
84
84
  expect(body.messages).toHaveLength(1);
@@ -103,7 +103,7 @@ describe("handleListMessages attachments", () => {
103
103
  );
104
104
  linkAttachmentToMessage(msg.id, stored.id, 0);
105
105
 
106
- const response = handleListMessages(createTestArgs(conv.id), null);
106
+ const response = handleListMessages(createTestArgs(conv.id));
107
107
  const body = response as { messages: MessagePayload[] };
108
108
 
109
109
  expect(body.messages).toHaveLength(1);
@@ -125,7 +125,7 @@ describe("handleListMessages attachments", () => {
125
125
  const stored = uploadAttachment("result.png", "image/png", IMAGE_BASE64);
126
126
  linkAttachmentToMessage(msg.id, stored.id, 0);
127
127
 
128
- const response = handleListMessages(createTestArgs(conv.id), null);
128
+ const response = handleListMessages(createTestArgs(conv.id));
129
129
  const body = response as { messages: MessagePayload[] };
130
130
 
131
131
  expect(body.messages).toHaveLength(1);
@@ -153,7 +153,7 @@ describe("handleListMessages attachments", () => {
153
153
  linkAttachmentToMessage(msg.id, imgStored.id, 0);
154
154
  linkAttachmentToMessage(msg.id, docStored.id, 1);
155
155
 
156
- const response = handleListMessages(createTestArgs(conv.id), null);
156
+ const response = handleListMessages(createTestArgs(conv.id));
157
157
  const body = response as { messages: MessagePayload[] };
158
158
 
159
159
  const attachments = body.messages[0].attachments!;
@@ -177,7 +177,7 @@ describe("handleListMessages no_response filtering", () => {
177
177
  JSON.stringify([{ type: "text", text: "<no_response/>" }]),
178
178
  );
179
179
 
180
- const response = handleListMessages(createTestArgs(conv.id), null);
180
+ const response = handleListMessages(createTestArgs(conv.id));
181
181
  const body = response as {
182
182
  messages: { content: string; textSegments: string[] }[];
183
183
  };
@@ -199,7 +199,7 @@ describe("handleListMessages no_response filtering", () => {
199
199
  ]),
200
200
  );
201
201
 
202
- const response = handleListMessages(createTestArgs(conv.id), null);
202
+ const response = handleListMessages(createTestArgs(conv.id));
203
203
  const body = response as {
204
204
  messages: { content: string; textSegments: string[] }[];
205
205
  };
@@ -228,7 +228,7 @@ describe("handleListMessages no_response filtering", () => {
228
228
  ]),
229
229
  );
230
230
 
231
- const response = handleListMessages(createTestArgs(conv.id), null);
231
+ const response = handleListMessages(createTestArgs(conv.id));
232
232
  const body = response as {
233
233
  messages: {
234
234
  content: string;
@@ -252,7 +252,7 @@ describe("handleListMessages no_response filtering", () => {
252
252
  JSON.stringify([{ type: "text", text: "What does <no_response/> do?" }]),
253
253
  );
254
254
 
255
- const response = handleListMessages(createTestArgs(conv.id), null);
255
+ const response = handleListMessages(createTestArgs(conv.id));
256
256
  const body = response as {
257
257
  messages: { content: string }[];
258
258
  };
@@ -308,7 +308,7 @@ describe("handleListMessages pagination", () => {
308
308
  const conv = createConversation();
309
309
  await insertMessages(conv.id, 5);
310
310
 
311
- const response = handleListMessages(createTestArgs(conv.id), null);
311
+ const response = handleListMessages(createTestArgs(conv.id));
312
312
  const body = response as unknown as PaginatedResponse;
313
313
 
314
314
  expect(body.messages).toHaveLength(5);
@@ -322,7 +322,7 @@ describe("handleListMessages pagination", () => {
322
322
  await insertMessages(conv.id, 5);
323
323
 
324
324
  const args = createPaginatedArgs(conv.id, { limit: "3" });
325
- const response = handleListMessages(args, null);
325
+ const response = handleListMessages(args);
326
326
  const body = response as unknown as PaginatedResponse;
327
327
 
328
328
  // Option A: without beforeTimestamp, all messages are returned regardless of limit
@@ -339,7 +339,7 @@ describe("handleListMessages pagination", () => {
339
339
  beforeTimestamp: String(msgs[7].createdAt),
340
340
  limit: "3",
341
341
  });
342
- const response = handleListMessages(args, null);
342
+ const response = handleListMessages(args);
343
343
  const body = response as unknown as PaginatedResponse;
344
344
 
345
345
  expect(body.messages).toHaveLength(3);
@@ -360,7 +360,7 @@ describe("handleListMessages pagination", () => {
360
360
  beforeTimestamp: String(msgs[1].createdAt),
361
361
  limit: "10",
362
362
  });
363
- const response = handleListMessages(args, null);
363
+ const response = handleListMessages(args);
364
364
  const body = response as unknown as PaginatedResponse;
365
365
 
366
366
  const ids = body.messages.map((m) => m.id);
@@ -378,7 +378,7 @@ describe("handleListMessages pagination", () => {
378
378
  beforeTimestamp: String(msgs[4].createdAt + 1),
379
379
  limit: "10",
380
380
  });
381
- const response = handleListMessages(args, null);
381
+ const response = handleListMessages(args);
382
382
  const body = response as unknown as PaginatedResponse;
383
383
 
384
384
  expect(body.messages).toHaveLength(5);
@@ -394,7 +394,7 @@ describe("handleListMessages pagination", () => {
394
394
  beforeTimestamp: String(msgs[4].createdAt + 1),
395
395
  limit: "3",
396
396
  });
397
- const response = handleListMessages(args, null);
397
+ const response = handleListMessages(args);
398
398
  const body = response as unknown as PaginatedResponse;
399
399
 
400
400
  expect(body.messages).toHaveLength(3);
@@ -405,7 +405,7 @@ describe("handleListMessages pagination", () => {
405
405
 
406
406
  test("empty / nonexistent conversation → empty messages, no pagination metadata", async () => {
407
407
  const args = createPaginatedArgs("nonexistent-conv-id");
408
- const response = handleListMessages(args, null);
408
+ const response = handleListMessages(args);
409
409
  const body = response as unknown as PaginatedResponse;
410
410
 
411
411
  expect(body.messages).toEqual([]);
@@ -418,13 +418,17 @@ describe("handleListMessages pagination", () => {
418
418
  const conv = createConversation();
419
419
  const args = createPaginatedArgs(conv.id, { limit: "abc" });
420
420
 
421
- expect(() => handleListMessages(args, null)).toThrow("limit must be a valid number");
421
+ expect(() => handleListMessages(args)).toThrow(
422
+ "limit must be a valid number",
423
+ );
422
424
  });
423
425
 
424
426
  test("invalid beforeTimestamp (NaN) → 400", async () => {
425
427
  const conv = createConversation();
426
428
  const args = createPaginatedArgs(conv.id, { beforeTimestamp: "abc" });
427
429
 
428
- expect(() => handleListMessages(args, null)).toThrow("beforeTimestamp must be a valid number");
430
+ expect(() => handleListMessages(args)).toThrow(
431
+ "beforeTimestamp must be a valid number",
432
+ );
429
433
  });
430
434
  });