@vellumai/assistant 0.8.2 → 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 (503) hide show
  1. package/ARCHITECTURE.md +11 -12
  2. package/docker-entrypoint.sh +13 -2
  3. package/docker-init-apt-root.sh +79 -6
  4. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  5. package/openapi.yaml +945 -36
  6. package/package.json +1 -1
  7. package/src/__tests__/agent-loop-exit-reason.test.ts +271 -0
  8. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  9. package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +272 -0
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/background-workers-disk-pressure.test.ts +2 -1
  14. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  15. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  16. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  17. package/src/__tests__/compaction-events.test.ts +1 -1
  18. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  19. package/src/__tests__/compactor-tail-resolution.test.ts +107 -1
  20. package/src/__tests__/config-get-vision-flag.test.ts +136 -0
  21. package/src/__tests__/config-loader-backfill.test.ts +115 -18
  22. package/src/__tests__/config-watcher.test.ts +1 -1
  23. package/src/__tests__/context-token-estimator.test.ts +112 -57
  24. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  25. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +54 -3
  26. package/src/__tests__/conversation-agent-loop-overflow.test.ts +31 -6
  27. package/src/__tests__/conversation-agent-loop.test.ts +77 -3
  28. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  29. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  30. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  31. package/src/__tests__/conversation-fork-crud.test.ts +161 -0
  32. package/src/__tests__/conversation-lifecycle.test.ts +1 -1
  33. package/src/__tests__/conversation-load-cleaned-at.test.ts +279 -0
  34. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  35. package/src/__tests__/conversation-media-retry.test.ts +19 -8
  36. package/src/__tests__/conversation-pairing.test.ts +2 -2
  37. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  38. package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -1
  39. package/src/__tests__/conversation-queue.test.ts +1 -1
  40. package/src/__tests__/conversation-runtime-assembly.test.ts +290 -85
  41. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  42. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  43. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  44. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  45. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  46. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  47. package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -1
  48. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  49. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  50. package/src/__tests__/credential-security-invariants.test.ts +6 -0
  51. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  52. package/src/__tests__/date-context.test.ts +45 -0
  53. package/src/__tests__/dm-backfill.test.ts +64 -0
  54. package/src/__tests__/dm-persistence.test.ts +33 -0
  55. package/src/__tests__/document-find-replace.test.ts +501 -0
  56. package/src/__tests__/external-plugin-loader.test.ts +91 -19
  57. package/src/__tests__/first-greeting.test.ts +23 -2
  58. package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
  59. package/src/__tests__/guardian-dispatch.test.ts +1 -0
  60. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  61. package/src/__tests__/heartbeat-service.test.ts +24 -164
  62. package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
  63. package/src/__tests__/host-app-control-proxy.test.ts +241 -0
  64. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  65. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  66. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  67. package/src/__tests__/host-file-proxy.test.ts +8 -1
  68. package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
  69. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  70. package/src/__tests__/identity-routes.test.ts +57 -0
  71. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  72. package/src/__tests__/injector-background-turn.test.ts +153 -0
  73. package/src/__tests__/injector-chain.test.ts +7 -0
  74. package/src/__tests__/injector-document-comments.test.ts +378 -0
  75. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  76. package/src/__tests__/lifecycle-memory-v2-seed.test.ts +9 -2
  77. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  78. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  79. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  80. package/src/__tests__/list-messages-tool-merge.test.ts +17 -16
  81. package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
  82. package/src/__tests__/llm-catalog-parity.test.ts +3 -0
  83. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  84. package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
  85. package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
  86. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +2 -0
  87. package/src/__tests__/llm-resolver.test.ts +340 -3
  88. package/src/__tests__/log-export-routes.test.ts +99 -2
  89. package/src/__tests__/managed-profile-guard.test.ts +10 -0
  90. package/src/__tests__/message-queue-steer.test.ts +114 -0
  91. package/src/__tests__/notification-decision-fallback.test.ts +0 -91
  92. package/src/__tests__/notification-decision-strategy.test.ts +14 -31
  93. package/src/__tests__/notification-deep-link.test.ts +15 -0
  94. package/src/__tests__/notification-guardian-path.test.ts +1 -2
  95. package/src/__tests__/notification-platform-adapter.test.ts +5 -4
  96. package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
  97. package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
  98. package/src/__tests__/openai-provider.test.ts +323 -3
  99. package/src/__tests__/openai-responses-cutover-guard.test.ts +3 -3
  100. package/src/__tests__/openai-responses-provider.test.ts +4 -4
  101. package/src/__tests__/openrouter-provider-only.test.ts +51 -3
  102. package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
  103. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  104. package/src/__tests__/pending-interactions-resolved-event.test.ts +190 -0
  105. package/src/__tests__/platform-proxy-context.test.ts +6 -1
  106. package/src/__tests__/platform.test.ts +0 -3
  107. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  108. package/src/__tests__/plugin-tool-contribution.test.ts +3 -3
  109. package/src/__tests__/plugin-types.test.ts +2 -2
  110. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  111. package/src/__tests__/process-message-display-content.test.ts +21 -16
  112. package/src/__tests__/provider-catalog-visibility.test.ts +16 -0
  113. package/src/__tests__/provider-platform-proxy-integration.test.ts +27 -25
  114. package/src/__tests__/secret-routes-platform-proxy.test.ts +1 -1
  115. package/src/__tests__/server-history-render.test.ts +83 -4
  116. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  117. package/src/__tests__/system-prompt.test.ts +57 -101
  118. package/src/__tests__/terminal-tools.test.ts +11 -1
  119. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  120. package/src/__tests__/thread-backfill.test.ts +370 -22
  121. package/src/__tests__/tool-executor.test.ts +90 -1
  122. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  123. package/src/__tests__/twilio-routes.test.ts +1 -1
  124. package/src/__tests__/web-fetch.test.ts +2 -2
  125. package/src/__tests__/workspace-git-service.test.ts +88 -5
  126. package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
  127. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  128. package/src/a2a/__tests__/agent-card.test.ts +98 -0
  129. package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
  130. package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
  131. package/src/a2a/__tests__/task-store.test.ts +246 -0
  132. package/src/a2a/agent-card.ts +58 -0
  133. package/src/a2a/feature-gate.ts +8 -0
  134. package/src/a2a/protocol-constants.ts +21 -0
  135. package/src/a2a/protocol-errors.ts +50 -0
  136. package/src/a2a/protocol-types.ts +162 -0
  137. package/src/a2a/task-store.ts +168 -0
  138. package/src/agent/attachments.ts +1 -0
  139. package/src/agent/loop.ts +208 -22
  140. package/src/background-wake/next-wake.test.ts +289 -0
  141. package/src/background-wake/next-wake.ts +172 -0
  142. package/src/browser/operations.ts +15 -0
  143. package/src/channels/config.ts +9 -0
  144. package/src/channels/types.ts +14 -0
  145. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  146. package/src/cli/commands/__tests__/memory-v2.test.ts +9 -12
  147. package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
  148. package/src/cli/commands/__tests__/schedules.test.ts +469 -0
  149. package/src/cli/commands/conversations.ts +128 -1
  150. package/src/cli/commands/inference-providers.ts +147 -1
  151. package/src/cli/commands/memory-v2.ts +308 -0
  152. package/src/cli/commands/notifications.ts +89 -37
  153. package/src/cli/commands/plugins.ts +67 -0
  154. package/src/cli/commands/schedules.ts +297 -5
  155. package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
  156. package/src/cli/lib/install-from-github.ts +8 -9
  157. package/src/cli/lib/search-plugins.ts +163 -0
  158. package/src/cli/program.ts +14 -0
  159. package/src/cli/utils/conversation-id.ts +17 -5
  160. package/src/config/assistant-feature-flags.ts +24 -54
  161. package/src/config/bundled-skills/app-builder/SKILL.md +117 -1
  162. package/src/config/bundled-skills/document-editor/SKILL.md +115 -0
  163. package/src/config/bundled-skills/document-editor/TOOLS.json +240 -0
  164. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  165. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  166. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  167. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  168. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  169. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  170. package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
  171. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  172. package/src/config/bundled-tool-registry.ts +22 -12
  173. package/src/config/call-site-defaults.ts +124 -0
  174. package/src/config/feature-flag-registry.json +111 -23
  175. package/src/config/llm-resolver.ts +66 -1
  176. package/src/config/schema.ts +2 -0
  177. package/src/config/schemas/__tests__/memory-v2.test.ts +7 -3
  178. package/src/config/schemas/call-site-catalog.ts +21 -0
  179. package/src/config/schemas/channels.ts +9 -0
  180. package/src/config/schemas/conversations.ts +10 -0
  181. package/src/config/schemas/heartbeat.ts +14 -0
  182. package/src/config/schemas/llm.ts +4 -3
  183. package/src/config/schemas/memory-retrospective.ts +1 -1
  184. package/src/config/schemas/memory-v2.ts +51 -4
  185. package/src/config/schemas/memory.ts +3 -1
  186. package/src/config/seed-inference-profiles.ts +99 -29
  187. package/src/context/compactor.ts +80 -13
  188. package/src/context/token-estimator.ts +72 -31
  189. package/src/context/window-manager.ts +25 -0
  190. package/src/credential-health/credential-health-service.ts +34 -19
  191. package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -22
  192. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  193. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  194. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  195. package/src/daemon/conversation-agent-loop-handlers.ts +231 -23
  196. package/src/daemon/conversation-agent-loop.ts +252 -56
  197. package/src/daemon/conversation-lifecycle.ts +142 -116
  198. package/src/daemon/conversation-messaging.ts +3 -0
  199. package/src/daemon/conversation-process.ts +273 -0
  200. package/src/daemon/conversation-queue-manager.ts +14 -0
  201. package/src/daemon/conversation-runtime-assembly.ts +144 -75
  202. package/src/daemon/conversation-slash.ts +37 -5
  203. package/src/daemon/conversation-surfaces.ts +45 -2
  204. package/src/daemon/conversation-tool-setup.ts +7 -0
  205. package/src/daemon/conversation.ts +42 -12
  206. package/src/daemon/date-context.ts +40 -0
  207. package/src/daemon/first-greeting.ts +10 -0
  208. package/src/daemon/guardian-action-generators.ts +1 -125
  209. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  210. package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
  211. package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
  212. package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
  213. package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
  214. package/src/daemon/handlers/config-a2a.ts +449 -0
  215. package/src/daemon/handlers/config-model.test.ts +1 -0
  216. package/src/daemon/handlers/conversations.ts +80 -0
  217. package/src/daemon/handlers/shared.ts +92 -29
  218. package/src/daemon/host-app-control-proxy.ts +69 -18
  219. package/src/daemon/host-bash-proxy.ts +1 -1
  220. package/src/daemon/host-cu-proxy.ts +1 -1
  221. package/src/daemon/host-file-proxy.ts +1 -1
  222. package/src/daemon/host-proxy-preactivation.ts +85 -18
  223. package/src/daemon/host-transfer-proxy.ts +1 -1
  224. package/src/daemon/lifecycle.ts +67 -65
  225. package/src/daemon/memory-v2-startup.ts +49 -13
  226. package/src/daemon/message-protocol.ts +4 -0
  227. package/src/daemon/message-types/conversations.ts +8 -0
  228. package/src/daemon/message-types/document-comments.ts +50 -0
  229. package/src/daemon/message-types/messages.ts +68 -1
  230. package/src/daemon/message-types/notifications.ts +21 -0
  231. package/src/daemon/message-types/surfaces.ts +3 -1
  232. package/src/daemon/message-types/web-activity.ts +57 -0
  233. package/src/daemon/pkb-reminder-builder.test.ts +10 -53
  234. package/src/daemon/pkb-reminder-builder.ts +4 -19
  235. package/src/daemon/plugin-source-watcher.ts +135 -3
  236. package/src/daemon/process-message.ts +72 -12
  237. package/src/daemon/query-complexity-router.ts +75 -0
  238. package/src/daemon/skill-memory-refresh.ts +5 -1
  239. package/src/daemon/trust-context.ts +6 -0
  240. package/src/daemon/wake-target-adapter.ts +2 -0
  241. package/src/documents/document-comments-store.test.ts +338 -0
  242. package/src/documents/document-comments-store.ts +237 -0
  243. package/src/documents/document-store.ts +202 -0
  244. package/src/export/__tests__/transcript-formatter.test.ts +121 -0
  245. package/src/export/transcript-formatter.ts +54 -20
  246. package/src/heartbeat/__tests__/heartbeat-service.test.ts +44 -1
  247. package/src/heartbeat/heartbeat-service.ts +35 -191
  248. package/src/home/__tests__/feed-types.test.ts +40 -0
  249. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  250. package/src/home/feed-types.ts +20 -3
  251. package/src/home/home-content-refresh.ts +52 -0
  252. package/src/home/home-greeting-cache.ts +69 -0
  253. package/src/home/home-greeting.ts +94 -0
  254. package/src/home/suggested-prompts.ts +177 -9
  255. package/src/ipc/cli-client.ts +147 -45
  256. package/src/memory/__tests__/conversation-queries.test.ts +220 -0
  257. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  258. package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
  259. package/src/memory/__tests__/memory-retrospective-job.test.ts +407 -10
  260. package/src/memory/conversation-crud.ts +133 -43
  261. package/src/memory/conversation-queries.ts +87 -1
  262. package/src/memory/conversation-title-service.ts +26 -4
  263. package/src/memory/db-init.ts +22 -0
  264. package/src/memory/delivery-crud.ts +41 -0
  265. package/src/memory/delivery-status.ts +141 -15
  266. package/src/memory/external-conversation-store.ts +32 -1
  267. package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +84 -3
  268. package/src/memory/graph/conversation-graph-memory.ts +18 -6
  269. package/src/memory/graph/tools.ts +6 -37
  270. package/src/memory/invite-store.ts +53 -0
  271. package/src/memory/jobs-worker.ts +21 -1
  272. package/src/memory/llm-request-log-source-clickhouse.ts +7 -2
  273. package/src/memory/llm-request-log-store.ts +92 -1
  274. package/src/memory/memory-retrospective-constants.ts +28 -0
  275. package/src/memory/memory-retrospective-enqueue.ts +4 -22
  276. package/src/memory/memory-retrospective-job.ts +438 -21
  277. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  278. package/src/memory/memory-v2-activation-log-store.ts +26 -8
  279. package/src/memory/migrations/100-core-tables.ts +1 -0
  280. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  281. package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
  282. package/src/memory/migrations/251-a2a-tasks.ts +49 -0
  283. package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
  284. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  285. package/src/memory/migrations/253-document-comments.ts +47 -0
  286. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  287. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  288. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  289. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  290. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  291. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  292. package/src/memory/migrations/index.ts +20 -0
  293. package/src/memory/migrations/registry.ts +33 -0
  294. package/src/memory/onboarding-events-store.ts +7 -0
  295. package/src/memory/schema/a2a.ts +15 -0
  296. package/src/memory/schema/calls.ts +1 -0
  297. package/src/memory/schema/conversations.ts +3 -0
  298. package/src/memory/schema/index.ts +1 -0
  299. package/src/memory/schema/inference.ts +2 -0
  300. package/src/memory/schema/infrastructure.ts +2 -0
  301. package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
  302. package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
  303. package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
  304. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  305. package/src/memory/v2/__tests__/injection.test.ts +221 -17
  306. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  307. package/src/memory/v2/__tests__/router.test.ts +489 -1
  308. package/src/memory/v2/__tests__/static-context.test.ts +12 -1
  309. package/src/memory/v2/activation-store.ts +14 -16
  310. package/src/memory/v2/cli-command-content.ts +19 -0
  311. package/src/memory/v2/cli-command-store.ts +304 -0
  312. package/src/memory/v2/consolidation-job.ts +14 -0
  313. package/src/memory/v2/frontmatter-sweep.ts +7 -1
  314. package/src/memory/v2/injection-events.ts +101 -0
  315. package/src/memory/v2/injection.ts +69 -29
  316. package/src/memory/v2/page-index.ts +246 -19
  317. package/src/memory/v2/page-store.ts +18 -0
  318. package/src/memory/v2/router.ts +209 -55
  319. package/src/memory/v2/static-context.ts +4 -4
  320. package/src/memory/v2/types.ts +23 -0
  321. package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
  322. package/src/messaging/providers/a2a/deliver.ts +156 -0
  323. package/src/messaging/providers/gmail/client.ts +9 -2
  324. package/src/messaging/providers/index.ts +18 -3
  325. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  326. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  327. package/src/messaging/providers/slack/adapter.ts +178 -25
  328. package/src/messaging/providers/slack/api.test.ts +54 -0
  329. package/src/messaging/providers/slack/api.ts +119 -3
  330. package/src/messaging/providers/slack/client.ts +12 -0
  331. package/src/messaging/providers/slack/deep-link.ts +20 -1
  332. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  333. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  334. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  335. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  336. package/src/messaging/providers/slack/send.test.ts +77 -0
  337. package/src/messaging/providers/slack/send.ts +8 -2
  338. package/src/messaging/providers/slack/types.ts +14 -0
  339. package/src/notifications/__tests__/broadcaster.test.ts +203 -0
  340. package/src/notifications/__tests__/decision-engine.test.ts +283 -0
  341. package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
  342. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +5 -1
  343. package/src/notifications/__tests__/home-feed-side-effect.test.ts +521 -36
  344. package/src/notifications/adapters/macos.ts +12 -2
  345. package/src/notifications/broadcaster.ts +29 -4
  346. package/src/notifications/conversation-seed-composer.ts +14 -2
  347. package/src/notifications/copy-composer.ts +17 -64
  348. package/src/notifications/decision-engine.ts +111 -44
  349. package/src/notifications/deferred-emit.ts +135 -0
  350. package/src/notifications/deterministic-checks.ts +96 -0
  351. package/src/notifications/emit-signal.ts +10 -1
  352. package/src/notifications/home-feed-side-effect.ts +136 -27
  353. package/src/notifications/signal.ts +0 -4
  354. package/src/notifications/types.ts +8 -0
  355. package/src/oauth/connect-orchestrator.ts +3 -0
  356. package/src/oauth/credential-token-resolver.ts +2 -0
  357. package/src/oauth/manual-token-connection.ts +19 -0
  358. package/src/oauth/oauth-store.ts +12 -0
  359. package/src/oauth/platform-connection.test.ts +43 -3
  360. package/src/oauth/platform-connection.ts +13 -4
  361. package/src/oauth/seed-providers.ts +22 -0
  362. package/src/permissions/prompter.ts +5 -2
  363. package/src/permissions/secret-prompter.ts +4 -1
  364. package/src/plugins/defaults/injectors.ts +118 -26
  365. package/src/plugins/external-plugin-loader.ts +82 -10
  366. package/src/plugins/types.ts +16 -7
  367. package/src/prompts/__tests__/system-prompt.test.ts +44 -45
  368. package/src/prompts/__tests__/task-progress-hint-section.test.ts +4 -8
  369. package/src/prompts/normalize-onboarding.ts +40 -0
  370. package/src/prompts/sections.ts +32 -14
  371. package/src/prompts/system-prompt.ts +105 -76
  372. package/src/prompts/template-detection.ts +37 -0
  373. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  374. package/src/prompts/templates/BOOTSTRAP.md +13 -5
  375. package/src/prompts/templates/VOICE.md +3 -0
  376. package/src/prompts/templates/system-sections.ts +51 -10
  377. package/src/providers/__tests__/inference.test.ts +2 -0
  378. package/src/providers/anthropic/client.ts +132 -5
  379. package/src/providers/call-site-routing.ts +24 -6
  380. package/src/providers/connection-resolution.ts +63 -13
  381. package/src/providers/fireworks/client.ts +20 -2
  382. package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
  383. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  384. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  385. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  386. package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
  387. package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
  388. package/src/providers/inference/adapter-factory.ts +24 -21
  389. package/src/providers/inference/auth.ts +15 -3
  390. package/src/providers/inference/backfill.ts +14 -1
  391. package/src/providers/inference/codex-token-refresh.ts +128 -0
  392. package/src/providers/inference/connections.ts +85 -5
  393. package/src/providers/inference/resolve-auth.ts +50 -5
  394. package/src/providers/model-catalog.ts +244 -242
  395. package/src/providers/model-intents.ts +3 -3
  396. package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
  397. package/src/providers/openai/chat-completions-provider.ts +215 -25
  398. package/src/providers/openai/responses-provider.ts +9 -3
  399. package/src/providers/openrouter/client.ts +46 -4
  400. package/src/providers/platform-proxy/constants.ts +3 -4
  401. package/src/providers/provider-catalog-visibility.ts +3 -1
  402. package/src/providers/provider-send-message.ts +27 -12
  403. package/src/providers/registry.ts +30 -1
  404. package/src/providers/types.ts +25 -0
  405. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  406. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  407. package/src/runtime/agent-wake.ts +212 -57
  408. package/src/runtime/auth/route-policy.ts +20 -3
  409. package/src/runtime/background-job-runner.ts +26 -0
  410. package/src/runtime/channel-reply-delivery.ts +182 -47
  411. package/src/runtime/channel-retry-sweep.ts +141 -16
  412. package/src/runtime/http-server.ts +7 -16
  413. package/src/runtime/http-types.ts +7 -51
  414. package/src/runtime/pending-interactions.ts +51 -8
  415. package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
  416. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  417. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +121 -5
  418. package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
  419. package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
  420. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  421. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +271 -0
  422. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  423. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  424. package/src/runtime/routes/approval-routes.ts +4 -1
  425. package/src/runtime/routes/channel-availability-routes.ts +5 -0
  426. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  427. package/src/runtime/routes/consolidation-routes.ts +100 -0
  428. package/src/runtime/routes/content-source-routes.ts +78 -0
  429. package/src/runtime/routes/conversation-cli-routes.ts +146 -1
  430. package/src/runtime/routes/conversation-query-routes.ts +130 -12
  431. package/src/runtime/routes/conversation-routes.ts +288 -76
  432. package/src/runtime/routes/document-comments-routes.ts +287 -0
  433. package/src/runtime/routes/documents-routes.ts +33 -0
  434. package/src/runtime/routes/home-feed-routes.ts +6 -3
  435. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  436. package/src/runtime/routes/host-browser-routes.ts +8 -1
  437. package/src/runtime/routes/identity-routes.ts +21 -0
  438. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  439. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  440. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  441. package/src/runtime/routes/index.ts +14 -4
  442. package/src/runtime/routes/inference-provider-connection-routes.ts +192 -3
  443. package/src/runtime/routes/integrations/a2a.ts +294 -0
  444. package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
  445. package/src/runtime/routes/log-export-routes.ts +39 -0
  446. package/src/runtime/routes/memory-v2-routes.ts +217 -0
  447. package/src/runtime/routes/notification-routes.ts +19 -2
  448. package/src/runtime/routes/question-routes.ts +4 -1
  449. package/src/runtime/routes/sanity-routes.ts +159 -0
  450. package/src/runtime/routes/slack-channel-routes.ts +187 -0
  451. package/src/runtime/routes/subagents-routes.ts +41 -0
  452. package/src/runtime/services/conversation-serializer.ts +30 -4
  453. package/src/schedule/integration-status.ts +3 -1
  454. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  455. package/src/security/oauth2-device-code.ts +307 -0
  456. package/src/security/oauth2.ts +26 -9
  457. package/src/security/secure-keys.ts +5 -0
  458. package/src/skills/catalog-install.ts +6 -2
  459. package/src/subagent/manager.ts +2 -0
  460. package/src/tools/browser/__tests__/pinned-tabs.test.ts +80 -0
  461. package/src/tools/browser/browser-execution.ts +93 -0
  462. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  463. package/src/tools/browser/cdp-client/__tests__/types.test.ts +1 -0
  464. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +10 -0
  465. package/src/tools/browser/cdp-client/extension-cdp-client.ts +15 -1
  466. package/src/tools/browser/cdp-client/factory.ts +87 -3
  467. package/src/tools/browser/cdp-client/local-cdp-client.ts +9 -0
  468. package/src/tools/browser/cdp-client/types.ts +36 -0
  469. package/src/tools/browser/pinned-tabs.ts +90 -0
  470. package/src/tools/document/document-comment-tool.test.ts +379 -0
  471. package/src/tools/document/document-comment-tool.ts +156 -0
  472. package/src/tools/document/document-tool.ts +128 -2
  473. package/src/tools/memory/register.ts +1 -9
  474. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  475. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  476. package/src/tools/network/domain-normalize.ts +17 -0
  477. package/src/tools/network/web-fetch.ts +213 -64
  478. package/src/tools/network/web-search.ts +191 -66
  479. package/src/tools/registry.ts +2 -2
  480. package/src/tools/terminal/safe-env.ts +3 -2
  481. package/src/tools/tool-approval-handler.ts +19 -12
  482. package/src/tools/types.ts +41 -2
  483. package/src/tools/ui-surface/definitions.ts +3 -1
  484. package/src/types/onboarding-context.ts +4 -0
  485. package/src/util/__tests__/favicon.test.ts +84 -0
  486. package/src/util/favicon.ts +40 -0
  487. package/src/util/platform.ts +0 -5
  488. package/src/workspace/git-service.ts +75 -4
  489. package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
  490. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  491. package/src/workspace/migrations/registry.ts +4 -0
  492. package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
  493. package/src/config/bundled-skills/document/SKILL.md +0 -54
  494. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  495. package/src/daemon/seed-files.ts +0 -18
  496. package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
  497. package/src/runtime/guardian-action-conversation-turn.ts +0 -99
  498. package/src/runtime/routes/interface-routes.ts +0 -43
  499. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  500. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  501. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  502. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  503. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -148,6 +148,7 @@ describe("PR 11 — inbound Slack message metadata persistence", () => {
148
148
  {
149
149
  slackInbound: {
150
150
  channelId: "C0123CHANNEL",
151
+ channelName: "engineering",
151
152
  channelTs: "1700000001.111111",
152
153
  threadTs: "1700000000.000001",
153
154
  displayName: "Alice",
@@ -162,6 +163,7 @@ describe("PR 11 — inbound Slack message metadata persistence", () => {
162
163
  expect(slackMeta!.source).toBe("slack");
163
164
  expect(slackMeta!.eventKind).toBe("message");
164
165
  expect(slackMeta!.channelId).toBe("C0123CHANNEL");
166
+ expect(slackMeta!.channelName).toBe("engineering");
165
167
  expect(slackMeta!.channelTs).toBe("1700000001.111111");
166
168
  expect(slackMeta!.threadTs).toBe("1700000000.000001");
167
169
  expect(slackMeta!.displayName).toBe("Alice");
@@ -194,6 +196,7 @@ describe("PR 11 — inbound Slack message metadata persistence", () => {
194
196
  expect(slackMeta!.channelTs).toBe("1700000010.222222");
195
197
  expect(slackMeta!.threadTs).toBeUndefined();
196
198
  expect(slackMeta!.displayName).toBe("Bob");
199
+ expect(slackMeta!.channelName).toBeUndefined();
197
200
  });
198
201
 
199
202
  test("Slack normalized content is persisted with raw channelTs in slackMeta", async () => {
@@ -0,0 +1,153 @@
1
+ import { beforeEach, describe, expect, mock, test } from "bun:test";
2
+
3
+ let configBackgroundInjection: string =
4
+ "This is a background turn — your guardian isn't watching. If anything noteworthy comes up, send them a notification so they see it when they're back by invoking the `notifications` skill (`assistant notifications send --message \"...\"`)";
5
+
6
+ const realLoaderForBackgroundTurnTest = await import("../config/loader.js");
7
+ const realGetConfigForBackgroundTurnTest =
8
+ realLoaderForBackgroundTurnTest.getConfig;
9
+ mock.module("../config/loader.js", () => ({
10
+ ...realLoaderForBackgroundTurnTest,
11
+ getConfig: () => {
12
+ const real = realGetConfigForBackgroundTurnTest();
13
+ return {
14
+ ...real,
15
+ conversations: {
16
+ ...real.conversations,
17
+ backgroundInjection: configBackgroundInjection,
18
+ },
19
+ };
20
+ },
21
+ }));
22
+
23
+ import {
24
+ DEFAULT_INJECTOR_ORDER,
25
+ defaultInjectorsPlugin,
26
+ } from "../plugins/defaults/injectors.js";
27
+ import {
28
+ registerPlugin,
29
+ resetPluginRegistryForTests,
30
+ } from "../plugins/registry.js";
31
+ import type { Injector, TurnContext } from "../plugins/types.js";
32
+
33
+ function findInjector(name: string): Injector {
34
+ const injector = defaultInjectorsPlugin.injectors?.find(
35
+ (candidate) => candidate.name === name,
36
+ );
37
+ if (!injector) {
38
+ throw new Error(`injector '${name}' not registered`);
39
+ }
40
+ return injector;
41
+ }
42
+
43
+ function makeContext(overrides: Partial<TurnContext> = {}): TurnContext {
44
+ return {
45
+ requestId: "req-test",
46
+ conversationId: "conv-test",
47
+ turnIndex: 0,
48
+ trust: { sourceChannel: "vellum", trustClass: "guardian" },
49
+ ...overrides,
50
+ };
51
+ }
52
+
53
+ const backgroundInjector = findInjector("background-turn");
54
+
55
+ const DEFAULT_INJECTION_TEXT =
56
+ "This is a background turn — your guardian isn't watching. If anything noteworthy comes up, send them a notification so they see it when they're back by invoking the `notifications` skill (`assistant notifications send --message \"...\"`)";
57
+
58
+ describe("background-turn injector", () => {
59
+ beforeEach(() => {
60
+ resetPluginRegistryForTests();
61
+ registerPlugin(defaultInjectorsPlugin);
62
+ configBackgroundInjection = DEFAULT_INJECTION_TEXT;
63
+ });
64
+
65
+ test("returns null when isBackgroundConversation is false", async () => {
66
+ const result = await backgroundInjector.produce(
67
+ makeContext({
68
+ injectionInputs: {
69
+ isBackgroundConversation: false,
70
+ isNonInteractive: true,
71
+ },
72
+ }),
73
+ );
74
+ expect(result).toBeNull();
75
+ });
76
+
77
+ test("returns null when isBackgroundConversation is unset", async () => {
78
+ const result = await backgroundInjector.produce(
79
+ makeContext({ injectionInputs: { isNonInteractive: true } }),
80
+ );
81
+ expect(result).toBeNull();
82
+ });
83
+
84
+ test("returns null when the guardian is actively connected (interactive turn)", async () => {
85
+ const result = await backgroundInjector.produce(
86
+ makeContext({
87
+ injectionInputs: {
88
+ isBackgroundConversation: true,
89
+ isNonInteractive: false,
90
+ },
91
+ }),
92
+ );
93
+ expect(result).toBeNull();
94
+ });
95
+
96
+ test("returns null when isNonInteractive is unset", async () => {
97
+ const result = await backgroundInjector.produce(
98
+ makeContext({ injectionInputs: { isBackgroundConversation: true } }),
99
+ );
100
+ expect(result).toBeNull();
101
+ });
102
+
103
+ test("wraps configured text in <background_turn> tags when active and non-interactive", async () => {
104
+ const block = await backgroundInjector.produce(
105
+ makeContext({
106
+ injectionInputs: {
107
+ isBackgroundConversation: true,
108
+ isNonInteractive: true,
109
+ },
110
+ }),
111
+ );
112
+
113
+ expect(block).toEqual({
114
+ id: "background-turn",
115
+ text: `<background_turn>\n${DEFAULT_INJECTION_TEXT}\n</background_turn>`,
116
+ placement: "prepend-user-tail",
117
+ });
118
+ expect(backgroundInjector.order).toBe(
119
+ DEFAULT_INJECTOR_ORDER.backgroundTurn,
120
+ );
121
+ });
122
+
123
+ test("returns null when configured text is the empty string", async () => {
124
+ configBackgroundInjection = "";
125
+
126
+ const result = await backgroundInjector.produce(
127
+ makeContext({
128
+ injectionInputs: {
129
+ isBackgroundConversation: true,
130
+ isNonInteractive: true,
131
+ },
132
+ }),
133
+ );
134
+ expect(result).toBeNull();
135
+ });
136
+
137
+ test("uses operator-configured override text verbatim", async () => {
138
+ configBackgroundInjection = "Custom reminder body.";
139
+
140
+ const block = await backgroundInjector.produce(
141
+ makeContext({
142
+ injectionInputs: {
143
+ isBackgroundConversation: true,
144
+ isNonInteractive: true,
145
+ },
146
+ }),
147
+ );
148
+
149
+ expect(block?.text).toBe(
150
+ "<background_turn>\nCustom reminder body.\n</background_turn>",
151
+ );
152
+ });
153
+ });
@@ -95,12 +95,14 @@ describe("injector chain", () => {
95
95
  expect(names).toEqual([
96
96
  "disk-pressure-warning",
97
97
  "workspace-context",
98
+ "background-turn",
98
99
  "unified-turn-context",
99
100
  "pkb-context",
100
101
  "pkb-reminder",
101
102
  "memory-v2-static",
102
103
  "now-md",
103
104
  "active-documents",
105
+ "document-comments",
104
106
  "subagent-status",
105
107
  "slack-messages",
106
108
  "thread-focus",
@@ -117,6 +119,9 @@ describe("injector chain", () => {
117
119
  expect(byName.get("workspace-context")).toBe(
118
120
  DEFAULT_INJECTOR_ORDER.workspaceContext,
119
121
  );
122
+ expect(byName.get("background-turn")).toBe(
123
+ DEFAULT_INJECTOR_ORDER.backgroundTurn,
124
+ );
120
125
  expect(byName.get("unified-turn-context")).toBe(
121
126
  DEFAULT_INJECTOR_ORDER.unifiedTurnContext,
122
127
  );
@@ -154,6 +159,7 @@ describe("injector chain", () => {
154
159
  expect(names).toEqual([
155
160
  "disk-pressure-warning", // 5
156
161
  "workspace-context", // 10
162
+ "background-turn", // 15
157
163
  "unified-turn-context", // 20
158
164
  "plugin-25", // 25 — slots in
159
165
  "pkb-context", // 30
@@ -161,6 +167,7 @@ describe("injector chain", () => {
161
167
  "memory-v2-static", // 38
162
168
  "now-md", // 40
163
169
  "active-documents", // 45
170
+ "document-comments", // 46
164
171
  "subagent-status", // 50
165
172
  "slack-messages", // 60
166
173
  "thread-focus", // 70
@@ -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
  });
@@ -71,6 +71,13 @@ mock.module("../memory/v2/skill-store.js", () => ({
71
71
  },
72
72
  }));
73
73
 
74
+ // Mock the sibling CLI-command seeder so `rebuildBm25CorpusStatsAndReseedSkills`
75
+ // (which runs both reseeds in parallel) does not invoke the real Qdrant-backed
76
+ // implementation and emit warnings that break the no-warnings assertions below.
77
+ mock.module("../memory/v2/cli-command-store.js", () => ({
78
+ seedV2CliCommandEntries: async (): Promise<void> => {},
79
+ }));
80
+
74
81
  mock.module("../memory/v2/qdrant.js", () => ({
75
82
  ensureConceptPageCollection: async (): Promise<{ migrated: boolean }> => {
76
83
  state.ensureCollectionCallCount += 1;
@@ -312,10 +319,10 @@ describe("rebuildBm25CorpusStatsAndReseedSkills", () => {
312
319
  expect(state.warnCalls).toHaveLength(0);
313
320
  });
314
321
 
315
- test("builds corpus stats but skips skill reseed when v2 is disabled", async () => {
322
+ test("skips both corpus stats and skill reseed when v2 is disabled", async () => {
316
323
  await rebuildBm25CorpusStatsAndReseedSkills(makeConfig(false));
317
324
 
318
- expect(state.corpusStatsBuildCount).toBe(1);
325
+ expect(state.corpusStatsBuildCount).toBe(0);
319
326
  expect(state.seedCallCount).toBe(0);
320
327
  expect(state.warnCalls).toHaveLength(0);
321
328
  });