@vellumai/assistant 0.8.3 → 0.8.5

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 (665) hide show
  1. package/ARCHITECTURE.md +2 -2
  2. package/docker-entrypoint.sh +0 -1
  3. package/docs/browser-use-architecture-phase2.md +1 -1
  4. package/knip.json +2 -1
  5. package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
  6. package/openapi.yaml +1492 -100
  7. package/package.json +1 -1
  8. package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
  9. package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
  10. package/src/__tests__/agent-loop.test.ts +88 -3
  11. package/src/__tests__/anthropic-provider.test.ts +302 -33
  12. package/src/__tests__/approval-cascade.test.ts +1 -1
  13. package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
  14. package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
  15. package/src/__tests__/audit-log-rotation.test.ts +70 -16
  16. package/src/__tests__/background-workers-disk-pressure.test.ts +4 -3
  17. package/src/__tests__/btw-routes.test.ts +2 -3
  18. package/src/__tests__/call-controller.test.ts +0 -1
  19. package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
  20. package/src/__tests__/channel-delivery-store.test.ts +193 -0
  21. package/src/__tests__/channel-guardian.test.ts +3 -3
  22. package/src/__tests__/channel-reply-delivery.test.ts +284 -5
  23. package/src/__tests__/channel-retry-sweep.test.ts +274 -1
  24. package/src/__tests__/checker.test.ts +6 -15
  25. package/src/__tests__/compaction-events.test.ts +2 -1
  26. package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
  27. package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -0
  28. package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
  29. package/src/__tests__/computer-use-tools.test.ts +2 -4
  30. package/src/__tests__/config-watcher.test.ts +1 -1
  31. package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
  32. package/src/__tests__/context-token-estimator.test.ts +91 -1
  33. package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
  34. package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
  35. package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
  36. package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
  37. package/src/__tests__/conversation-agent-loop.test.ts +188 -129
  38. package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
  39. package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
  40. package/src/__tests__/conversation-clean-command.test.ts +137 -0
  41. package/src/__tests__/conversation-clear-safety.test.ts +25 -25
  42. package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
  43. package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
  44. package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
  45. package/src/__tests__/conversation-error.test.ts +31 -0
  46. package/src/__tests__/conversation-fork-crud.test.ts +324 -0
  47. package/src/__tests__/conversation-lifecycle.test.ts +53 -12
  48. package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
  49. package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
  50. package/src/__tests__/conversation-pairing.test.ts +2 -2
  51. package/src/__tests__/conversation-process-callsite.test.ts +1 -1
  52. package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
  53. package/src/__tests__/conversation-queue.test.ts +1 -1
  54. package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
  55. package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
  56. package/src/__tests__/conversation-runtime-assembly.test.ts +264 -81
  57. package/src/__tests__/conversation-seed-composer.test.ts +66 -4
  58. package/src/__tests__/conversation-skill-tools.test.ts +2 -5
  59. package/src/__tests__/conversation-slash-commands.test.ts +36 -8
  60. package/src/__tests__/conversation-slash-queue.test.ts +1 -1
  61. package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
  62. package/src/__tests__/conversation-speed-override.test.ts +1 -1
  63. package/src/__tests__/conversation-store.test.ts +1 -1
  64. package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
  65. package/src/__tests__/conversation-sync-tags.test.ts +99 -32
  66. package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
  67. package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
  68. package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
  69. package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
  70. package/src/__tests__/credential-execution-tools.test.ts +6 -6
  71. package/src/__tests__/credential-security-invariants.test.ts +7 -0
  72. package/src/__tests__/credential-vault-unit.test.ts +2 -2
  73. package/src/__tests__/cu-unified-flow.test.ts +10 -1
  74. package/src/__tests__/dm-backfill.test.ts +64 -0
  75. package/src/__tests__/dm-persistence.test.ts +33 -0
  76. package/src/__tests__/document-find-replace.test.ts +501 -0
  77. package/src/__tests__/dynamic-page-surface.test.ts +2 -2
  78. package/src/__tests__/email-html-renderer.test.ts +12 -0
  79. package/src/__tests__/first-greeting.test.ts +23 -2
  80. package/src/__tests__/gateway-flag-listener.test.ts +237 -0
  81. package/src/__tests__/gemini-provider.test.ts +78 -0
  82. package/src/__tests__/guardian-dispatch.test.ts +0 -1
  83. package/src/__tests__/guardian-outbound-http.test.ts +7 -5
  84. package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
  85. package/src/__tests__/headless-browser-navigate.test.ts +172 -0
  86. package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
  87. package/src/__tests__/heartbeat-service.test.ts +4 -0
  88. package/src/__tests__/host-bash-proxy.test.ts +6 -0
  89. package/src/__tests__/host-browser-proxy.test.ts +10 -0
  90. package/src/__tests__/host-cu-proxy.test.ts +8 -1
  91. package/src/__tests__/host-file-proxy.test.ts +8 -1
  92. package/src/__tests__/host-shell-tool.test.ts +1 -1
  93. package/src/__tests__/host-transfer-proxy.test.ts +8 -1
  94. package/src/__tests__/identity-routes.test.ts +57 -0
  95. package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
  96. package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
  97. package/src/__tests__/injector-chain.test.ts +2 -0
  98. package/src/__tests__/injector-document-comments.test.ts +378 -0
  99. package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
  100. package/src/__tests__/list-messages-attachments.test.ts +21 -17
  101. package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
  102. package/src/__tests__/list-messages-page-latest.test.ts +130 -14
  103. package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
  104. package/src/__tests__/llm-context-normalization.test.ts +0 -2
  105. package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
  106. package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
  107. package/src/__tests__/llm-resolver.test.ts +161 -9
  108. package/src/__tests__/llm-usage-store.test.ts +66 -0
  109. package/src/__tests__/log-export-routes.test.ts +99 -2
  110. package/src/__tests__/logger.test.ts +89 -0
  111. package/src/__tests__/mcp-abort-signal.test.ts +2 -2
  112. package/src/__tests__/media-generate-image.test.ts +31 -0
  113. package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
  114. package/src/__tests__/message-queue-steer.test.ts +114 -0
  115. package/src/__tests__/model-intents.test.ts +2 -4
  116. package/src/__tests__/notification-guardian-path.test.ts +0 -1
  117. package/src/__tests__/onboarding-template-contract.test.ts +1 -1
  118. package/src/__tests__/openai-provider.test.ts +151 -0
  119. package/src/__tests__/openai-responses-provider.test.ts +118 -16
  120. package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
  121. package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
  122. package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
  123. package/src/__tests__/platform.test.ts +2 -5
  124. package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
  125. package/src/__tests__/plugin-bootstrap.test.ts +2 -2
  126. package/src/__tests__/plugin-source-watcher.test.ts +302 -0
  127. package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
  128. package/src/__tests__/plugin-types.test.ts +3 -2
  129. package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
  130. package/src/__tests__/pricing.test.ts +12 -0
  131. package/src/__tests__/process-message-background-slack.test.ts +1 -51
  132. package/src/__tests__/process-message-display-content.test.ts +21 -16
  133. package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
  134. package/src/__tests__/registry.test.ts +2 -8
  135. package/src/__tests__/require-fresh-approval.test.ts +2 -2
  136. package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
  137. package/src/__tests__/server-history-render.test.ts +83 -4
  138. package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
  139. package/src/__tests__/skill-feature-flags.test.ts +2 -2
  140. package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
  141. package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
  142. package/src/__tests__/skill-tool-factory.test.ts +1 -1
  143. package/src/__tests__/steer-tool-repair.test.ts +249 -0
  144. package/src/__tests__/subagent-notify-parent.test.ts +1 -1
  145. package/src/__tests__/suggestion-routes.test.ts +1 -0
  146. package/src/__tests__/sync-message-contract.test.ts +59 -0
  147. package/src/__tests__/system-prompt.test.ts +161 -124
  148. package/src/__tests__/terminal-tools.test.ts +12 -2
  149. package/src/__tests__/thinking-block-replay.test.ts +113 -0
  150. package/src/__tests__/thread-backfill.test.ts +370 -22
  151. package/src/__tests__/tool-approval-handler.test.ts +1 -5
  152. package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
  153. package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
  154. package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
  155. package/src/__tests__/tool-executor.test.ts +89 -53
  156. package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
  157. package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
  158. package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
  159. package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
  160. package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
  161. package/src/__tests__/twilio-routes.test.ts +1 -1
  162. package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
  163. package/src/__tests__/usage-routes.test.ts +3 -0
  164. package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
  165. package/src/__tests__/web-fetch.test.ts +2 -2
  166. package/src/__tests__/workspace-git-service.test.ts +94 -10
  167. package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
  168. package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
  169. package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
  170. package/src/acp/prepare-agent-env.ts +78 -0
  171. package/src/acp/session-manager.ts +1 -1
  172. package/src/agent/attachments.ts +1 -0
  173. package/src/agent/loop.ts +65 -20
  174. package/src/api/README.md +5 -0
  175. package/src/api/index.ts +4 -0
  176. package/src/api/package.json +10 -0
  177. package/src/background-wake/background-wake-routes.test.ts +233 -0
  178. package/src/background-wake/next-wake.test.ts +289 -0
  179. package/src/background-wake/next-wake.ts +172 -0
  180. package/src/background-wake/runtime-registry.ts +24 -0
  181. package/src/browser/operations.ts +15 -0
  182. package/src/cli/commands/__tests__/browser.test.ts +23 -5
  183. package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
  184. package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
  185. package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
  186. package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
  187. package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
  188. package/src/cli/commands/__tests__/memory-v2.test.ts +10 -12
  189. package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
  190. package/src/cli/commands/browser.ts +247 -0
  191. package/src/cli/commands/conversations.ts +128 -1
  192. package/src/cli/commands/domain.ts +91 -41
  193. package/src/cli/commands/inference-providers.ts +147 -1
  194. package/src/cli/commands/inference.ts +93 -40
  195. package/src/cli/commands/memory-v2-compare-render.ts +115 -0
  196. package/src/cli/commands/memory-v2.ts +483 -0
  197. package/src/cli/commands/memory-v3-render.ts +344 -0
  198. package/src/cli/commands/memory-v3.ts +316 -0
  199. package/src/cli/commands/notifications.ts +24 -2
  200. package/src/cli/program.ts +2 -0
  201. package/src/cli/utils/conversation-id.ts +17 -5
  202. package/src/config/assistant-feature-flags.ts +21 -9
  203. package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
  204. package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
  205. package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
  206. package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
  207. package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
  208. package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
  209. package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
  210. package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
  211. package/src/config/bundled-skills/document-editor/tools/document-replace-text.ts +12 -0
  212. package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
  213. package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
  214. package/src/config/bundled-skills/media-processing/SKILL.md +8 -0
  215. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
  216. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
  217. package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
  218. package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
  219. package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
  220. package/src/config/bundled-skills/schedule/SKILL.md +8 -0
  221. package/src/config/bundled-tool-registry.ts +24 -12
  222. package/src/config/call-site-defaults.ts +20 -0
  223. package/src/config/feature-flag-registry.json +115 -3
  224. package/src/config/llm-resolver.ts +16 -2
  225. package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
  226. package/src/config/schemas/call-site-catalog.ts +35 -0
  227. package/src/config/schemas/llm.ts +14 -0
  228. package/src/config/schemas/memory-v2.ts +294 -1
  229. package/src/config/schemas/memory.ts +2 -1
  230. package/src/context/compactor.ts +60 -1
  231. package/src/context/token-estimator.ts +47 -4
  232. package/src/context/window-manager.ts +25 -0
  233. package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
  234. package/src/conversations/message-consolidation.ts +404 -0
  235. package/src/credential-health/credential-health-service.ts +34 -19
  236. package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
  237. package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
  238. package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
  239. package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
  240. package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
  241. package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
  242. package/src/daemon/conversation-agent-loop.ts +307 -88
  243. package/src/daemon/conversation-error.ts +31 -1
  244. package/src/daemon/conversation-lifecycle.ts +149 -118
  245. package/src/daemon/conversation-messaging.ts +3 -0
  246. package/src/daemon/conversation-process.ts +273 -0
  247. package/src/daemon/conversation-queue-manager.ts +14 -0
  248. package/src/daemon/conversation-runtime-assembly.ts +145 -84
  249. package/src/daemon/conversation-slash.ts +37 -5
  250. package/src/daemon/conversation-surfaces.ts +45 -2
  251. package/src/daemon/conversation-tool-setup.ts +70 -3
  252. package/src/daemon/conversation-usage.ts +2 -0
  253. package/src/daemon/conversation.ts +54 -32
  254. package/src/daemon/disk-pressure-guard.ts +14 -2
  255. package/src/daemon/first-greeting.ts +10 -0
  256. package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
  257. package/src/daemon/handlers/config-a2a.ts +160 -0
  258. package/src/daemon/handlers/config-model.test.ts +2 -0
  259. package/src/daemon/handlers/conversations.ts +90 -3
  260. package/src/daemon/handlers/shared.ts +92 -29
  261. package/src/daemon/host-bash-proxy.ts +1 -1
  262. package/src/daemon/host-browser-proxy.ts +5 -5
  263. package/src/daemon/host-cu-proxy.ts +5 -5
  264. package/src/daemon/host-file-proxy.ts +5 -5
  265. package/src/daemon/host-proxy-base.ts +4 -4
  266. package/src/daemon/host-transfer-proxy.ts +11 -11
  267. package/src/daemon/lifecycle.ts +40 -23
  268. package/src/daemon/meet-manifest-loader.ts +1 -7
  269. package/src/daemon/message-protocol.ts +4 -0
  270. package/src/daemon/message-types/conversations.ts +14 -9
  271. package/src/daemon/message-types/document-comments.ts +50 -0
  272. package/src/daemon/message-types/home.ts +1 -13
  273. package/src/daemon/message-types/messages.ts +66 -7
  274. package/src/daemon/message-types/surfaces.ts +3 -1
  275. package/src/daemon/message-types/sync.ts +14 -0
  276. package/src/daemon/message-types/web-activity.ts +57 -0
  277. package/src/daemon/plugin-source-watcher.ts +135 -3
  278. package/src/daemon/process-message.ts +69 -12
  279. package/src/daemon/shutdown-handlers.ts +24 -5
  280. package/src/daemon/switch-inference-profile-tool.ts +52 -0
  281. package/src/daemon/tool-setup-types.ts +13 -0
  282. package/src/daemon/trust-context.ts +6 -0
  283. package/src/documents/document-comments-store.test.ts +338 -0
  284. package/src/documents/document-comments-store.ts +237 -0
  285. package/src/documents/document-store.ts +202 -0
  286. package/src/events/relationship-state-updated.ts +25 -0
  287. package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
  288. package/src/heartbeat/heartbeat-service.ts +1 -0
  289. package/src/home/__tests__/suggested-prompts.test.ts +33 -2
  290. package/src/home/feed-types.ts +6 -1
  291. package/src/home/home-content-refresh.ts +52 -0
  292. package/src/home/home-greeting-cache.ts +69 -0
  293. package/src/home/home-greeting.ts +85 -0
  294. package/src/home/suggested-prompts.ts +168 -9
  295. package/src/ipc/gateway-flag-listener.ts +123 -0
  296. package/src/ipc/skill-routes/registries.ts +8 -12
  297. package/src/memory/__tests__/db-async-query.test.ts +165 -0
  298. package/src/memory/__tests__/db-maintenance.test.ts +115 -0
  299. package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
  300. package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
  301. package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +135 -2
  302. package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
  303. package/src/memory/auto-analysis-enqueue.ts +5 -1
  304. package/src/memory/conversation-crud.ts +191 -100
  305. package/src/memory/conversation-starters-cadence.ts +3 -1
  306. package/src/memory/conversation-title-service.ts +19 -3
  307. package/src/memory/db-async-query.ts +214 -0
  308. package/src/memory/db-init.ts +26 -0
  309. package/src/memory/db-maintenance.ts +30 -21
  310. package/src/memory/delivery-crud.ts +41 -0
  311. package/src/memory/delivery-status.ts +141 -15
  312. package/src/memory/external-conversation-store.ts +32 -1
  313. package/src/memory/graph/bootstrap.ts +8 -1
  314. package/src/memory/graph/capability-seed.ts +7 -3
  315. package/src/memory/graph/conversation-graph-memory.ts +100 -17
  316. package/src/memory/graph/extraction.ts +1 -5
  317. package/src/memory/graph/graph-search.ts +7 -1
  318. package/src/memory/indexer.ts +28 -18
  319. package/src/memory/job-handlers/cleanup.ts +76 -18
  320. package/src/memory/job-handlers/conversation-starters.ts +1 -4
  321. package/src/memory/jobs/embed-pkb-file.ts +6 -1
  322. package/src/memory/jobs-store.ts +14 -0
  323. package/src/memory/jobs-worker.ts +68 -15
  324. package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
  325. package/src/memory/llm-request-log-source-local.ts +7 -0
  326. package/src/memory/llm-request-log-source.ts +9 -2
  327. package/src/memory/llm-request-log-store.ts +43 -1
  328. package/src/memory/llm-usage-store.ts +24 -0
  329. package/src/memory/memory-retrospective-constants.ts +28 -0
  330. package/src/memory/memory-retrospective-enqueue.ts +11 -3
  331. package/src/memory/memory-retrospective-job.ts +413 -18
  332. package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
  333. package/src/memory/memory-v2-activation-log-store.ts +41 -14
  334. package/src/memory/migrations/100-core-tables.ts +1 -0
  335. package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
  336. package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
  337. package/src/memory/migrations/253-document-comments.ts +47 -0
  338. package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
  339. package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
  340. package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
  341. package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
  342. package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
  343. package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
  344. package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
  345. package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
  346. package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
  347. package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
  348. package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
  349. package/src/memory/migrations/index.ts +34 -0
  350. package/src/memory/migrations/registry.ts +58 -0
  351. package/src/memory/onboarding-events-store.ts +7 -0
  352. package/src/memory/schema/calls.ts +1 -0
  353. package/src/memory/schema/conversations.ts +3 -0
  354. package/src/memory/schema/infrastructure.ts +22 -0
  355. package/src/memory/tool-usage-store.ts +36 -8
  356. package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
  357. package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
  358. package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
  359. package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
  360. package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
  361. package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
  362. package/src/memory/v2/__tests__/injection-events.test.ts +318 -0
  363. package/src/memory/v2/__tests__/injection.test.ts +158 -112
  364. package/src/memory/v2/__tests__/page-index.test.ts +365 -1
  365. package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
  366. package/src/memory/v2/__tests__/router.test.ts +660 -4
  367. package/src/memory/v2/consolidation-job.ts +14 -0
  368. package/src/memory/v2/harness/compare.ts +57 -0
  369. package/src/memory/v2/harness/metrics.ts +124 -0
  370. package/src/memory/v2/harness/oracle.ts +145 -0
  371. package/src/memory/v2/harness/replay-input.ts +224 -0
  372. package/src/memory/v2/harness/retriever.ts +74 -0
  373. package/src/memory/v2/harness/router-retriever.ts +43 -0
  374. package/src/memory/v2/harness/runner.ts +106 -0
  375. package/src/memory/v2/harness/trace.ts +58 -0
  376. package/src/memory/v2/injection-events.ts +101 -0
  377. package/src/memory/v2/injection.ts +42 -25
  378. package/src/memory/v2/page-index.ts +209 -7
  379. package/src/memory/v2/page-store.ts +18 -0
  380. package/src/memory/v2/prompts/router.ts +26 -1
  381. package/src/memory/v2/qdrant.ts +14 -2
  382. package/src/memory/v2/router.ts +369 -62
  383. package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
  384. package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
  385. package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
  386. package/src/memory/v3/__tests__/edges.test.ts +563 -0
  387. package/src/memory/v3/__tests__/filter.test.ts +512 -0
  388. package/src/memory/v3/__tests__/gate.test.ts +574 -0
  389. package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
  390. package/src/memory/v3/__tests__/loop.test.ts +530 -0
  391. package/src/memory/v3/__tests__/retriever.test.ts +226 -0
  392. package/src/memory/v3/__tests__/scouts.test.ts +440 -0
  393. package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
  394. package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
  395. package/src/memory/v3/__tests__/traversal.test.ts +469 -0
  396. package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
  397. package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
  398. package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
  399. package/src/memory/v3/__tests__/validate.test.ts +245 -0
  400. package/src/memory/v3/auto-edges.ts +223 -0
  401. package/src/memory/v3/coactivation-store.ts +124 -0
  402. package/src/memory/v3/consolidation-job.ts +323 -0
  403. package/src/memory/v3/edge-learning-job.ts +160 -0
  404. package/src/memory/v3/edges.ts +249 -0
  405. package/src/memory/v3/filter.ts +281 -0
  406. package/src/memory/v3/gate.ts +334 -0
  407. package/src/memory/v3/index-composition.ts +113 -0
  408. package/src/memory/v3/llm-capture.ts +46 -0
  409. package/src/memory/v3/loop.ts +382 -0
  410. package/src/memory/v3/maintenance.ts +144 -0
  411. package/src/memory/v3/prompt-context.ts +33 -0
  412. package/src/memory/v3/prompts/consolidation.ts +458 -0
  413. package/src/memory/v3/prompts/system-prompts.ts +196 -0
  414. package/src/memory/v3/retriever.ts +33 -0
  415. package/src/memory/v3/scouts.ts +420 -0
  416. package/src/memory/v3/shadow-middleware.ts +305 -0
  417. package/src/memory/v3/traversal.ts +206 -0
  418. package/src/memory/v3/tree-index.ts +237 -0
  419. package/src/memory/v3/tree-store.ts +394 -0
  420. package/src/memory/v3/tree-walk.ts +351 -0
  421. package/src/memory/v3/types.ts +65 -0
  422. package/src/memory/v3/validate.ts +300 -0
  423. package/src/messaging/providers/index.ts +7 -1
  424. package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
  425. package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
  426. package/src/messaging/providers/slack/adapter.ts +178 -25
  427. package/src/messaging/providers/slack/api.test.ts +54 -0
  428. package/src/messaging/providers/slack/api.ts +119 -3
  429. package/src/messaging/providers/slack/client.ts +12 -0
  430. package/src/messaging/providers/slack/deep-link.ts +20 -1
  431. package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
  432. package/src/messaging/providers/slack/message-metadata.ts +156 -0
  433. package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
  434. package/src/messaging/providers/slack/render-transcript.ts +176 -49
  435. package/src/messaging/providers/slack/send.test.ts +77 -0
  436. package/src/messaging/providers/slack/send.ts +8 -2
  437. package/src/messaging/providers/slack/types.ts +14 -0
  438. package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
  439. package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
  440. package/src/notifications/adapters/macos.ts +18 -1
  441. package/src/notifications/adapters/platform.ts +1 -1
  442. package/src/notifications/conversation-seed-composer.ts +14 -2
  443. package/src/notifications/decision-engine.ts +1 -4
  444. package/src/notifications/deferred-emit.ts +135 -0
  445. package/src/notifications/emit-signal.ts +38 -50
  446. package/src/notifications/home-feed-side-effect.ts +60 -30
  447. package/src/oauth/connect-orchestrator.ts +3 -0
  448. package/src/oauth/credential-token-resolver.ts +2 -0
  449. package/src/oauth/manual-token-connection.ts +19 -0
  450. package/src/oauth/oauth-store.ts +12 -0
  451. package/src/oauth/seed-providers.ts +22 -0
  452. package/src/permissions/prompter.ts +8 -5
  453. package/src/permissions/question-prompter.ts +5 -2
  454. package/src/permissions/secret-prompter.ts +6 -3
  455. package/src/plugin-api/index.ts +4 -0
  456. package/src/plugin-api/types.ts +7 -33
  457. package/src/plugins/defaults/index.ts +6 -0
  458. package/src/plugins/defaults/injectors.ts +100 -20
  459. package/src/plugins/external-plugin-loader.ts +5 -68
  460. package/src/plugins/types.ts +11 -16
  461. package/src/proactive-artifact/aux-message-injector.ts +17 -4
  462. package/src/prompts/__tests__/system-prompt.test.ts +46 -2
  463. package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
  464. package/src/prompts/normalize-onboarding.ts +40 -0
  465. package/src/prompts/persona-resolver.ts +36 -21
  466. package/src/prompts/sections.ts +69 -19
  467. package/src/prompts/system-prompt.ts +118 -216
  468. package/src/prompts/template-detection.ts +37 -0
  469. package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
  470. package/src/prompts/templates/BOOTSTRAP.md +10 -2
  471. package/src/prompts/templates/VOICE.md +3 -0
  472. package/src/prompts/templates/system-sections.ts +281 -9
  473. package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
  474. package/src/providers/__tests__/retry-callsite.test.ts +85 -5
  475. package/src/providers/anthropic/client.ts +159 -66
  476. package/src/providers/call-site-routing.ts +14 -2
  477. package/src/providers/connection-model-compat.ts +38 -0
  478. package/src/providers/connection-resolution.ts +16 -2
  479. package/src/providers/fireworks/client.ts +20 -2
  480. package/src/providers/gemini/client.ts +49 -6
  481. package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
  482. package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
  483. package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
  484. package/src/providers/inference/adapter-factory.ts +18 -1
  485. package/src/providers/inference/auth.ts +3 -3
  486. package/src/providers/inference/codex-token-refresh.ts +128 -0
  487. package/src/providers/inference/resolve-auth.ts +49 -6
  488. package/src/providers/minimax/client.ts +106 -0
  489. package/src/providers/model-catalog.ts +91 -1
  490. package/src/providers/model-intents.ts +1 -1
  491. package/src/providers/openai/chat-completions-provider.ts +63 -23
  492. package/src/providers/openai/codex-models.ts +18 -0
  493. package/src/providers/openai/responses-provider.ts +86 -23
  494. package/src/providers/openrouter/client.ts +5 -1
  495. package/src/providers/provider-send-message.ts +7 -1
  496. package/src/providers/retry.ts +34 -3
  497. package/src/providers/thinking-config.ts +26 -1
  498. package/src/providers/types.ts +25 -0
  499. package/src/providers/usage-tracking.ts +2 -0
  500. package/src/runtime/AGENTS.md +2 -2
  501. package/src/runtime/__tests__/agent-wake.test.ts +214 -0
  502. package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
  503. package/src/runtime/agent-wake.ts +152 -56
  504. package/src/runtime/assistant-event-hub.ts +76 -6
  505. package/src/runtime/auth/route-policy.ts +43 -3
  506. package/src/runtime/background-job-runner.ts +26 -0
  507. package/src/runtime/btw-sidechain.ts +0 -6
  508. package/src/runtime/channel-reply-delivery.ts +182 -47
  509. package/src/runtime/channel-retry-sweep.ts +141 -16
  510. package/src/runtime/http-types.ts +7 -6
  511. package/src/runtime/migrations/vbundle-builder.ts +10 -3
  512. package/src/runtime/pending-interactions.ts +50 -8
  513. package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
  514. package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
  515. package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
  516. package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
  517. package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
  518. package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
  519. package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
  520. package/src/runtime/routes/acp-routes.test.ts +255 -6
  521. package/src/runtime/routes/acp-routes.ts +8 -1
  522. package/src/runtime/routes/approval-routes.ts +4 -1
  523. package/src/runtime/routes/avatar-routes.ts +10 -10
  524. package/src/runtime/routes/background-wake-routes.ts +188 -0
  525. package/src/runtime/routes/browser-tabs-routes.ts +200 -0
  526. package/src/runtime/routes/btw-routes.ts +0 -6
  527. package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +246 -0
  528. package/src/runtime/routes/content-source-routes.ts +78 -0
  529. package/src/runtime/routes/conversation-cli-routes.ts +147 -2
  530. package/src/runtime/routes/conversation-list-routes.ts +12 -4
  531. package/src/runtime/routes/conversation-management-routes.ts +77 -20
  532. package/src/runtime/routes/conversation-query-routes.ts +196 -31
  533. package/src/runtime/routes/conversation-routes.ts +472 -425
  534. package/src/runtime/routes/conversation-starter-routes.ts +6 -3
  535. package/src/runtime/routes/disk-pressure-routes.ts +1 -1
  536. package/src/runtime/routes/document-comments-routes.ts +287 -0
  537. package/src/runtime/routes/documents-routes.ts +33 -0
  538. package/src/runtime/routes/domain-routes.ts +60 -10
  539. package/src/runtime/routes/email-routes.ts +5 -2
  540. package/src/runtime/routes/events-routes.ts +54 -10
  541. package/src/runtime/routes/group-routes.ts +24 -8
  542. package/src/runtime/routes/home-feed-routes.ts +6 -3
  543. package/src/runtime/routes/host-app-control-routes.ts +1 -1
  544. package/src/runtime/routes/host-browser-routes.ts +17 -2
  545. package/src/runtime/routes/host-cu-routes.ts +2 -2
  546. package/src/runtime/routes/identity-routes.ts +21 -0
  547. package/src/runtime/routes/inbound-message-handler.ts +288 -58
  548. package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
  549. package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
  550. package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
  551. package/src/runtime/routes/index.ts +20 -4
  552. package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
  553. package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
  554. package/src/runtime/routes/inference-provider-connection-routes.ts +63 -7
  555. package/src/runtime/routes/integrations/a2a.ts +60 -1
  556. package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
  557. package/src/runtime/routes/log-export-routes.ts +39 -0
  558. package/src/runtime/routes/memory-item-routes.ts +8 -3
  559. package/src/runtime/routes/memory-v2-routes.ts +427 -0
  560. package/src/runtime/routes/memory-v3-routes.ts +316 -0
  561. package/src/runtime/routes/migration-routes.ts +21 -24
  562. package/src/runtime/routes/notification-routes.ts +19 -2
  563. package/src/runtime/routes/plugins-routes.ts +337 -0
  564. package/src/runtime/routes/question-routes.ts +4 -1
  565. package/src/runtime/routes/rename-conversation-routes.ts +6 -2
  566. package/src/runtime/routes/sanity-routes.ts +159 -0
  567. package/src/runtime/routes/secret-routes.ts +25 -5
  568. package/src/runtime/routes/settings-routes.ts +12 -11
  569. package/src/runtime/routes/slack-channel-routes.ts +188 -0
  570. package/src/runtime/routes/workspace-routes.ts +25 -10
  571. package/src/runtime/services/conversation-serializer.ts +30 -4
  572. package/src/runtime/sync/resource-sync-events.ts +106 -38
  573. package/src/runtime/sync/sync-publisher.test.ts +49 -0
  574. package/src/runtime/sync/sync-publisher.ts +2 -1
  575. package/src/runtime/verification-outbound-actions.ts +73 -1
  576. package/src/schedule/integration-status.ts +3 -1
  577. package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
  578. package/src/security/oauth2-device-code.ts +307 -0
  579. package/src/security/oauth2.ts +26 -9
  580. package/src/security/secure-keys.ts +5 -0
  581. package/src/skills/catalog-install.ts +6 -2
  582. package/src/telemetry/types.ts +12 -0
  583. package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
  584. package/src/telemetry/usage-telemetry-reporter.ts +1 -0
  585. package/src/tools/acp/spawn.test.ts +119 -0
  586. package/src/tools/acp/spawn.ts +15 -2
  587. package/src/tools/apps/definitions.ts +2 -8
  588. package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
  589. package/src/tools/ask-question/ask-question-tool.ts +38 -45
  590. package/src/tools/browser/__tests__/pinned-tabs.test.ts +150 -0
  591. package/src/tools/browser/browser-execution.ts +106 -0
  592. package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
  593. package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
  594. package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
  595. package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
  596. package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
  597. package/src/tools/browser/cdp-client/factory.ts +171 -4
  598. package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
  599. package/src/tools/browser/cdp-client/types.ts +101 -0
  600. package/src/tools/browser/pinned-tabs.ts +146 -0
  601. package/src/tools/computer-use/definitions.ts +22 -78
  602. package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
  603. package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
  604. package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
  605. package/src/tools/credentials/vault.ts +3 -9
  606. package/src/tools/document/document-comment-tool.test.ts +379 -0
  607. package/src/tools/document/document-comment-tool.ts +156 -0
  608. package/src/tools/document/document-tool.ts +187 -2
  609. package/src/tools/execution-target.ts +21 -23
  610. package/src/tools/executor.ts +6 -1
  611. package/src/tools/filesystem/edit.ts +3 -9
  612. package/src/tools/filesystem/list.ts +3 -9
  613. package/src/tools/filesystem/read.ts +3 -9
  614. package/src/tools/filesystem/write.ts +3 -9
  615. package/src/tools/host-filesystem/edit.ts +3 -9
  616. package/src/tools/host-filesystem/read.ts +3 -9
  617. package/src/tools/host-filesystem/transfer.ts +3 -9
  618. package/src/tools/host-filesystem/write.ts +3 -9
  619. package/src/tools/host-terminal/host-shell.ts +3 -9
  620. package/src/tools/mcp/mcp-tool-factory.ts +1 -8
  621. package/src/tools/memory/register.test.ts +1 -1
  622. package/src/tools/memory/register.ts +4 -9
  623. package/src/tools/network/__tests__/web-fetch-metadata.test.ts +229 -0
  624. package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
  625. package/src/tools/network/domain-normalize.ts +17 -0
  626. package/src/tools/network/web-fetch.ts +216 -73
  627. package/src/tools/network/web-search.ts +216 -98
  628. package/src/tools/registry.ts +7 -23
  629. package/src/tools/schema-transforms.ts +1 -1
  630. package/src/tools/skills/execute.ts +3 -9
  631. package/src/tools/skills/load.ts +3 -9
  632. package/src/tools/skills/skill-tool-factory.ts +1 -8
  633. package/src/tools/subagent/notify-parent.ts +3 -9
  634. package/src/tools/system/request-permission.ts +3 -9
  635. package/src/tools/terminal/safe-env.ts +3 -2
  636. package/src/tools/terminal/shell.ts +3 -9
  637. package/src/tools/tool-approval-handler.ts +19 -12
  638. package/src/tools/tool-defaults.ts +94 -0
  639. package/src/tools/types.ts +31 -98
  640. package/src/tools/ui-surface/definitions.ts +9 -23
  641. package/src/types/onboarding-context.ts +4 -0
  642. package/src/usage/pricing.ts +23 -0
  643. package/src/usage/types.ts +12 -0
  644. package/src/util/__tests__/favicon.test.ts +84 -0
  645. package/src/util/favicon.ts +40 -0
  646. package/src/util/logger.ts +16 -7
  647. package/src/util/platform.ts +7 -7
  648. package/src/util/sqlite3-runtime.ts +65 -0
  649. package/src/workspace/git-service.ts +75 -4
  650. package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
  651. package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
  652. package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
  653. package/src/workspace/migrations/registry.ts +4 -0
  654. package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
  655. package/src/__tests__/message-complete-display-id.test.ts +0 -175
  656. package/src/config/bundled-skills/document/SKILL.md +0 -54
  657. package/src/config/bundled-skills/document/TOOLS.json +0 -106
  658. package/src/daemon/seed-files.ts +0 -18
  659. package/src/prompts/cache-boundary.ts +0 -8
  660. package/src/runtime/routes/interface-routes.ts +0 -43
  661. /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
  662. /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
  663. /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
  664. /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
  665. /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
@@ -0,0 +1,563 @@
1
+ /**
2
+ * Tests for `assistant/src/memory/v3/edges.ts` — the curated edge-expansion
3
+ * lane.
4
+ *
5
+ * Coverage matrix:
6
+ * - 1-hop and 2-hop outgoing expansion from a single seed.
7
+ * - Default hops (2) when omitted.
8
+ * - Seed excluded from its own `pulled`.
9
+ * - Multiple seeds: top-level `pulled` is the union; per-seed expansions
10
+ * attribute correctly; duplicate seeds collapse.
11
+ * - `extraAdjacency` merges with the curated graph during traversal.
12
+ * - `extraAdjacency` bridges across hops (curated → extra → curated).
13
+ * - Cycles in the curated graph (and via extraAdjacency) terminate, bounded
14
+ * by hops + the visited set.
15
+ * - Empty seeds / orphan seed → empty result.
16
+ * - Provider-free: the only I/O is reading fixture concept pages.
17
+ *
18
+ * Tests live in temp workspaces (mkdtemp) and never touch `~/.vellum/`.
19
+ */
20
+
21
+ import { existsSync, mkdtempSync, rmSync } from "node:fs";
22
+ import { tmpdir } from "node:os";
23
+ import { join } from "node:path";
24
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
25
+
26
+ import { invalidateEdgeIndex } from "../../v2/edge-index.js";
27
+ import { writePage } from "../../v2/page-store.js";
28
+ import type { ConceptPage } from "../../v2/types.js";
29
+ import { expandEdges } from "../edges.js";
30
+
31
+ let workspaceDir: string;
32
+
33
+ beforeEach(() => {
34
+ workspaceDir = mkdtempSync(join(tmpdir(), "vellum-memory-v3-edges-"));
35
+ });
36
+
37
+ afterEach(() => {
38
+ // The v2 edge index caches module-locally; clear it so the next test's fresh
39
+ // workspace doesn't read a stale snapshot.
40
+ invalidateEdgeIndex();
41
+ if (existsSync(workspaceDir)) {
42
+ rmSync(workspaceDir, { recursive: true, force: true });
43
+ }
44
+ });
45
+
46
+ function makePage(slug: string, edges: string[] = []): ConceptPage {
47
+ return {
48
+ slug,
49
+ frontmatter: { edges, ref_files: [], ref_urls: [] },
50
+ body: "",
51
+ };
52
+ }
53
+
54
+ /** Write a small chain/graph of pages by `{ slug: edges }` map. */
55
+ async function writeGraph(graph: Record<string, string[]>): Promise<void> {
56
+ for (const [slug, edges] of Object.entries(graph)) {
57
+ await writePage(workspaceDir, makePage(slug, edges));
58
+ }
59
+ }
60
+
61
+ // ---------------------------------------------------------------------------
62
+ // Single-seed expansion
63
+ // ---------------------------------------------------------------------------
64
+
65
+ describe("expandEdges — single seed", () => {
66
+ test("1-hop expansion pulls only direct out-neighbors", async () => {
67
+ // alice -> bob -> carol
68
+ await writeGraph({ alice: ["bob"], bob: ["carol"], carol: [] });
69
+
70
+ const { pulled, expansions } = await expandEdges({
71
+ workspaceDir,
72
+ seeds: ["alice"],
73
+ hops: 1,
74
+ });
75
+
76
+ expect([...pulled].sort()).toEqual(["bob"]);
77
+ expect(expansions).toEqual([{ from: "alice", pulled: ["bob"] }]);
78
+ });
79
+
80
+ test("2-hop expansion pulls the 2-hop frontier", async () => {
81
+ // alice -> bob -> carol
82
+ await writeGraph({ alice: ["bob"], bob: ["carol"], carol: [] });
83
+
84
+ const { pulled, expansions } = await expandEdges({
85
+ workspaceDir,
86
+ seeds: ["alice"],
87
+ hops: 2,
88
+ });
89
+
90
+ expect([...pulled].sort()).toEqual(["bob", "carol"]);
91
+ expect(expansions).toEqual([{ from: "alice", pulled: ["bob", "carol"] }]);
92
+ });
93
+
94
+ test("defaults to 2 hops when hops is omitted", async () => {
95
+ await writeGraph({ alice: ["bob"], bob: ["carol"], carol: ["dave"] });
96
+
97
+ const { pulled } = await expandEdges({
98
+ workspaceDir,
99
+ seeds: ["alice"],
100
+ });
101
+
102
+ // 2-hop reach from alice: bob (1) + carol (2); dave (3) is out of budget.
103
+ expect([...pulled].sort()).toEqual(["bob", "carol"]);
104
+ });
105
+
106
+ test("excludes the seed itself from pulled", async () => {
107
+ // Self-referential-ish: a -> b -> a would put `a` back in reach, but the
108
+ // seed must never appear in its own pulled set.
109
+ await writeGraph({ alice: ["bob"], bob: ["alice"] });
110
+
111
+ const { pulled, expansions } = await expandEdges({
112
+ workspaceDir,
113
+ seeds: ["alice"],
114
+ hops: 2,
115
+ });
116
+
117
+ expect(pulled.has("alice")).toBe(false);
118
+ expect([...pulled].sort()).toEqual(["bob"]);
119
+ expect(expansions[0]!.pulled).not.toContain("alice");
120
+ });
121
+
122
+ test("orphan seed (no outgoing edges) yields an empty expansion", async () => {
123
+ await writeGraph({ alice: [] });
124
+
125
+ const { pulled, expansions } = await expandEdges({
126
+ workspaceDir,
127
+ seeds: ["alice"],
128
+ });
129
+
130
+ expect(pulled.size).toBe(0);
131
+ expect(expansions).toEqual([{ from: "alice", pulled: [] }]);
132
+ });
133
+
134
+ test("edges are directed — incoming neighbors are never pulled", async () => {
135
+ // bob -> alice. Seeding alice must NOT pull bob (that's an in-edge).
136
+ await writeGraph({ bob: ["alice"], alice: [] });
137
+
138
+ const { pulled } = await expandEdges({
139
+ workspaceDir,
140
+ seeds: ["alice"],
141
+ hops: 2,
142
+ });
143
+
144
+ expect(pulled.size).toBe(0);
145
+ });
146
+ });
147
+
148
+ // ---------------------------------------------------------------------------
149
+ // Multiple seeds
150
+ // ---------------------------------------------------------------------------
151
+
152
+ describe("expandEdges — multiple seeds", () => {
153
+ test("top-level pulled is the union across seeds", async () => {
154
+ await writeGraph({
155
+ alice: ["bob"],
156
+ bob: [],
157
+ carol: ["dave"],
158
+ dave: [],
159
+ });
160
+
161
+ const { pulled, expansions } = await expandEdges({
162
+ workspaceDir,
163
+ seeds: ["alice", "carol"],
164
+ hops: 1,
165
+ });
166
+
167
+ expect([...pulled].sort()).toEqual(["bob", "dave"]);
168
+ expect(expansions).toEqual([
169
+ { from: "alice", pulled: ["bob"] },
170
+ { from: "carol", pulled: ["dave"] },
171
+ ]);
172
+ });
173
+
174
+ test("a slug pulled by two seeds appears once in pulled, once per expansion", async () => {
175
+ // alice -> shared, carol -> shared
176
+ await writeGraph({ alice: ["shared"], carol: ["shared"], shared: [] });
177
+
178
+ const { pulled, expansions } = await expandEdges({
179
+ workspaceDir,
180
+ seeds: ["alice", "carol"],
181
+ hops: 1,
182
+ });
183
+
184
+ expect([...pulled]).toEqual(["shared"]);
185
+ expect(expansions).toEqual([
186
+ { from: "alice", pulled: ["shared"] },
187
+ { from: "carol", pulled: ["shared"] },
188
+ ]);
189
+ });
190
+
191
+ test("duplicate seeds collapse to a single expansion entry", async () => {
192
+ await writeGraph({ alice: ["bob"], bob: [] });
193
+
194
+ const { expansions } = await expandEdges({
195
+ workspaceDir,
196
+ seeds: ["alice", "alice"],
197
+ hops: 1,
198
+ });
199
+
200
+ expect(expansions).toEqual([{ from: "alice", pulled: ["bob"] }]);
201
+ });
202
+
203
+ test("empty seed set yields an empty result", async () => {
204
+ await writeGraph({ alice: ["bob"], bob: [] });
205
+
206
+ const { pulled, expansions } = await expandEdges({
207
+ workspaceDir,
208
+ seeds: [],
209
+ });
210
+
211
+ expect(pulled.size).toBe(0);
212
+ expect(expansions).toEqual([]);
213
+ });
214
+ });
215
+
216
+ // ---------------------------------------------------------------------------
217
+ // extraAdjacency injection seam
218
+ // ---------------------------------------------------------------------------
219
+
220
+ describe("expandEdges — extraAdjacency", () => {
221
+ test("merges injected out-edges with the curated graph", async () => {
222
+ // Curated: alice -> bob. Injected: alice -> extra.
223
+ await writeGraph({ alice: ["bob"], bob: [], extra: [] });
224
+
225
+ const extraAdjacency = new Map<string, Set<string>>([
226
+ ["alice", new Set(["extra"])],
227
+ ]);
228
+
229
+ const { pulled, expansions } = await expandEdges({
230
+ workspaceDir,
231
+ seeds: ["alice"],
232
+ hops: 1,
233
+ extraAdjacency,
234
+ });
235
+
236
+ expect([...pulled].sort()).toEqual(["bob", "extra"]);
237
+ expect(expansions).toEqual([{ from: "alice", pulled: ["bob", "extra"] }]);
238
+ });
239
+
240
+ test("injected edges bridge across hops (curated -> extra -> curated)", async () => {
241
+ // Curated: alice -> bob, learned -> dave. Injected: bob -> learned.
242
+ // 2-hop reach: bob (curated, hop 1) -> learned (extra, hop 2)...
243
+ // and learned -> dave is hop 3, out of a 2-hop budget.
244
+ await writeGraph({
245
+ alice: ["bob"],
246
+ bob: [],
247
+ learned: ["dave"],
248
+ dave: [],
249
+ });
250
+
251
+ const extraAdjacency = new Map<string, Set<string>>([
252
+ ["bob", new Set(["learned"])],
253
+ ]);
254
+
255
+ const twoHop = await expandEdges({
256
+ workspaceDir,
257
+ seeds: ["alice"],
258
+ hops: 2,
259
+ extraAdjacency,
260
+ });
261
+ expect([...twoHop.pulled].sort()).toEqual(["bob", "learned"]);
262
+
263
+ const threeHop = await expandEdges({
264
+ workspaceDir,
265
+ seeds: ["alice"],
266
+ hops: 3,
267
+ extraAdjacency,
268
+ });
269
+ expect([...threeHop.pulled].sort()).toEqual(["bob", "dave", "learned"]);
270
+ });
271
+
272
+ test("absent extraAdjacency leaves the curated walk unchanged", async () => {
273
+ await writeGraph({ alice: ["bob"], bob: ["carol"], carol: [] });
274
+
275
+ const { pulled } = await expandEdges({
276
+ workspaceDir,
277
+ seeds: ["alice"],
278
+ hops: 2,
279
+ });
280
+
281
+ expect([...pulled].sort()).toEqual(["bob", "carol"]);
282
+ });
283
+ });
284
+
285
+ // ---------------------------------------------------------------------------
286
+ // Cycle safety
287
+ // ---------------------------------------------------------------------------
288
+
289
+ describe("expandEdges — cycle safety", () => {
290
+ test("a cycle in the curated graph terminates and does not loop", async () => {
291
+ // alice -> bob -> carol -> alice (3-cycle).
292
+ await writeGraph({
293
+ alice: ["bob"],
294
+ bob: ["carol"],
295
+ carol: ["alice"],
296
+ });
297
+
298
+ // A generous hop budget would loop forever without a visited set.
299
+ const { pulled, expansions } = await expandEdges({
300
+ workspaceDir,
301
+ seeds: ["alice"],
302
+ hops: 100,
303
+ });
304
+
305
+ // Reaches bob and carol; alice (the seed) is excluded even though the
306
+ // cycle points back at it.
307
+ expect([...pulled].sort()).toEqual(["bob", "carol"]);
308
+ expect(expansions[0]!.pulled).not.toContain("alice");
309
+ });
310
+
311
+ test("a cycle introduced via extraAdjacency also terminates", async () => {
312
+ // Curated: alice -> bob. Injected cycle: bob -> alice.
313
+ await writeGraph({ alice: ["bob"], bob: [] });
314
+
315
+ const extraAdjacency = new Map<string, Set<string>>([
316
+ ["bob", new Set(["alice"])],
317
+ ]);
318
+
319
+ const { pulled } = await expandEdges({
320
+ workspaceDir,
321
+ seeds: ["alice"],
322
+ hops: 100,
323
+ extraAdjacency,
324
+ });
325
+
326
+ expect([...pulled].sort()).toEqual(["bob"]);
327
+ });
328
+
329
+ test("a self-loop edge does not loop or pull the seed", async () => {
330
+ // alice -> alice (self-loop is dropped by the index, but guard anyway).
331
+ await writeGraph({ alice: ["alice", "bob"], bob: [] });
332
+
333
+ const { pulled } = await expandEdges({
334
+ workspaceDir,
335
+ seeds: ["alice"],
336
+ hops: 2,
337
+ });
338
+
339
+ expect(pulled.has("alice")).toBe(false);
340
+ expect([...pulled].sort()).toEqual(["bob"]);
341
+ });
342
+ });
343
+
344
+ // ---------------------------------------------------------------------------
345
+ // Expansion bounds
346
+ // ---------------------------------------------------------------------------
347
+ //
348
+ // These constants mirror the (intentionally module-private) caps in `edges.ts`.
349
+ // The seed set handed to this lane is the union of every upstream lane's
350
+ // candidates, so on a mature corpus it can run to thousands of slugs; expanding
351
+ // all of them to their full neighborhood balloons the downstream gate's pool.
352
+ // The lane caps the seeds expanded, the per-seed fan-out, and the total union.
353
+ const MAX_SEEDS_EXPANDED = 150;
354
+ const MAX_PULLS_PER_SEED = 32;
355
+ const MAX_TOTAL_PULLS = 400;
356
+
357
+ /** Zero-padded slug so lexical sort matches numeric order (`topics/000`..). */
358
+ function topicSlug(domain: string, i: number): string {
359
+ return `${domain}/${String(i).padStart(4, "0")}`;
360
+ }
361
+
362
+ describe("expandEdges — bounds", () => {
363
+ test("expands at most MAX_SEEDS_EXPANDED seeds, dropping the tail", async () => {
364
+ // More seeds than the cap, each a 1-hop edge to its own private target.
365
+ const seedCount = MAX_SEEDS_EXPANDED + 50;
366
+ const graph: Record<string, string[]> = {};
367
+ const seeds: string[] = [];
368
+ for (let i = 0; i < seedCount; i++) {
369
+ const seed = topicSlug("people", i);
370
+ const target = topicSlug("targets", i);
371
+ graph[seed] = [target];
372
+ graph[target] = [];
373
+ seeds.push(seed);
374
+ }
375
+ await writeGraph(graph);
376
+
377
+ const { pulled, expansions } = await expandEdges({
378
+ workspaceDir,
379
+ seeds,
380
+ hops: 1,
381
+ });
382
+
383
+ // Only the first MAX_SEEDS_EXPANDED seeds yield an expansion entry; the
384
+ // 200-seed input is truncated to the cap.
385
+ expect(expansions).toHaveLength(MAX_SEEDS_EXPANDED);
386
+ expect(expansions.map((e) => e.from)).toEqual(
387
+ seeds.slice(0, MAX_SEEDS_EXPANDED),
388
+ );
389
+ // Each surviving seed contributes exactly its one target, so the union is
390
+ // one slug per expanded seed and stays under the total ceiling.
391
+ expect(pulled.size).toBe(MAX_SEEDS_EXPANDED);
392
+ expect(pulled.size).toBeLessThanOrEqual(MAX_TOTAL_PULLS);
393
+ });
394
+
395
+ test("caps a single hub seed's fan-out at MAX_PULLS_PER_SEED", async () => {
396
+ // One hub seed pointing at far more 1-hop neighbors than the per-seed cap.
397
+ const fanOut = MAX_PULLS_PER_SEED + 40;
398
+ const graph: Record<string, string[]> = {};
399
+ const targets: string[] = [];
400
+ for (let i = 0; i < fanOut; i++) {
401
+ const target = topicSlug("topics", i);
402
+ targets.push(target);
403
+ graph[target] = [];
404
+ }
405
+ graph["hub"] = targets;
406
+ await writeGraph(graph);
407
+
408
+ const { pulled, expansions } = await expandEdges({
409
+ workspaceDir,
410
+ seeds: ["hub"],
411
+ hops: 1,
412
+ });
413
+
414
+ // The hub's neighborhood is truncated to the per-seed cap, deterministically
415
+ // keeping the lexicographically-first targets.
416
+ expect(expansions).toHaveLength(1);
417
+ expect(expansions[0]!.from).toBe("hub");
418
+ expect(expansions[0]!.pulled).toHaveLength(MAX_PULLS_PER_SEED);
419
+ expect(expansions[0]!.pulled).toEqual(
420
+ [...targets].sort().slice(0, MAX_PULLS_PER_SEED),
421
+ );
422
+ expect(pulled.size).toBe(MAX_PULLS_PER_SEED);
423
+ });
424
+
425
+ test("bounds the total pulled union at MAX_TOTAL_PULLS across many seeds", async () => {
426
+ // Enough seeds, each with a per-seed-cap-sized private fan-out, that the
427
+ // unbounded union would be seedCount * MAX_PULLS_PER_SEED slugs — far past
428
+ // the total ceiling (ceil(400/32) = 13 seeds fills it). Each seed's targets
429
+ // are disjoint, so nothing collapses via de-dup and the only thing holding
430
+ // the union down is the cap.
431
+ const seedCount = 20;
432
+ const graph: Record<string, string[]> = {};
433
+ const seeds: string[] = [];
434
+ for (let s = 0; s < seedCount; s++) {
435
+ const seed = topicSlug("people", s);
436
+ const targets: string[] = [];
437
+ for (let t = 0; t < MAX_PULLS_PER_SEED; t++) {
438
+ // Globally-unique target slug per (seed, target) pair.
439
+ const target = topicSlug("targets", s * MAX_PULLS_PER_SEED + t);
440
+ targets.push(target);
441
+ graph[target] = [];
442
+ }
443
+ graph[seed] = targets;
444
+ seeds.push(seed);
445
+ }
446
+ await writeGraph(graph);
447
+
448
+ const { pulled, expansions } = await expandEdges({
449
+ workspaceDir,
450
+ seeds,
451
+ hops: 1,
452
+ });
453
+
454
+ // The union is held at the ceiling exactly — never overshot — even though
455
+ // the seeds collectively reach far more slugs.
456
+ expect(pulled.size).toBe(MAX_TOTAL_PULLS);
457
+ // Every emitted expansion entry lists only slugs that made it into the
458
+ // bounded union, so the trace never claims an un-pulled slug.
459
+ for (const expansion of expansions) {
460
+ for (const slug of expansion.pulled) {
461
+ expect(pulled.has(slug)).toBe(true);
462
+ }
463
+ }
464
+ });
465
+
466
+ test("duplicate slugs across seeds don't waste the total budget", async () => {
467
+ // Two seeds, both pointing at the same shared target plus one private each.
468
+ // The shared slug is counted once, so the union is 3 — well under the cap,
469
+ // and both seeds still get a faithful expansion entry.
470
+ await writeGraph({
471
+ "people/alice": ["topics/shared", "topics/alice-only"],
472
+ "people/bob": ["topics/shared", "topics/bob-only"],
473
+ "topics/shared": [],
474
+ "topics/alice-only": [],
475
+ "topics/bob-only": [],
476
+ });
477
+
478
+ const { pulled, expansions } = await expandEdges({
479
+ workspaceDir,
480
+ seeds: ["people/alice", "people/bob"],
481
+ hops: 1,
482
+ });
483
+
484
+ expect([...pulled].sort()).toEqual([
485
+ "topics/alice-only",
486
+ "topics/bob-only",
487
+ "topics/shared",
488
+ ]);
489
+ expect(pulled.size).toBeLessThanOrEqual(MAX_TOTAL_PULLS);
490
+ expect(expansions).toEqual([
491
+ { from: "people/alice", pulled: ["topics/alice-only", "topics/shared"] },
492
+ { from: "people/bob", pulled: ["topics/bob-only", "topics/shared"] },
493
+ ]);
494
+ });
495
+ });
496
+
497
+ // ---------------------------------------------------------------------------
498
+ // Seed ranking by lane trust
499
+ // ---------------------------------------------------------------------------
500
+
501
+ describe("expandEdges — seed ranking", () => {
502
+ test("ranks tree/dense/sparse seeds ahead of hot before the seed cap", async () => {
503
+ // 200 seeds (> the 150 cap), each with a private 1-hop edge. In candidate
504
+ // order the first 150 are hot (recency) and the last 50 are tree (LLM-
505
+ // chosen). Without ranking the cap would expand all 150 hot and drop every
506
+ // tree seed; with laneBySlug the tree seeds must be expanded first.
507
+ const hotCount = 150;
508
+ const treeCount = 50;
509
+ const graph: Record<string, string[]> = {};
510
+ const seeds: string[] = [];
511
+ const laneBySlug = new Map<string, string>();
512
+ for (let i = 0; i < hotCount; i++) {
513
+ const seed = topicSlug("hot", i);
514
+ graph[seed] = [topicSlug("hot-targets", i)];
515
+ graph[topicSlug("hot-targets", i)] = [];
516
+ seeds.push(seed);
517
+ laneBySlug.set(seed, "hot");
518
+ }
519
+ for (let i = 0; i < treeCount; i++) {
520
+ const seed = topicSlug("tree", i);
521
+ graph[seed] = [topicSlug("tree-targets", i)];
522
+ graph[topicSlug("tree-targets", i)] = [];
523
+ seeds.push(seed);
524
+ laneBySlug.set(seed, "tree");
525
+ }
526
+ await writeGraph(graph);
527
+
528
+ const { expansions } = await expandEdges({
529
+ workspaceDir,
530
+ seeds,
531
+ hops: 1,
532
+ laneBySlug,
533
+ });
534
+
535
+ const expandedFrom = expansions.map((e) => e.from);
536
+ // The seed cap still holds.
537
+ expect(expandedFrom).toHaveLength(MAX_SEEDS_EXPANDED);
538
+ // Every tree seed survives the cap (ranked ahead of hot) and leads the
539
+ // order, keeping candidate order within the tier (stable sort).
540
+ const treeSeeds = seeds.slice(hotCount);
541
+ expect(expandedFrom.slice(0, treeCount)).toEqual(treeSeeds);
542
+ // The dropped seeds are the tail-end hot seeds, not any tree seed.
543
+ const droppedHot = seeds.slice(hotCount - treeCount, hotCount);
544
+ for (const h of droppedHot) expect(expandedFrom).not.toContain(h);
545
+ });
546
+
547
+ test("without laneBySlug, seeds keep candidate order", async () => {
548
+ const graph: Record<string, string[]> = {};
549
+ const seeds: string[] = [];
550
+ for (let i = 0; i < MAX_SEEDS_EXPANDED + 10; i++) {
551
+ const seed = topicSlug("people", i);
552
+ graph[seed] = [topicSlug("targets", i)];
553
+ graph[topicSlug("targets", i)] = [];
554
+ seeds.push(seed);
555
+ }
556
+ await writeGraph(graph);
557
+
558
+ const { expansions } = await expandEdges({ workspaceDir, seeds, hops: 1 });
559
+ expect(expansions.map((e) => e.from)).toEqual(
560
+ seeds.slice(0, MAX_SEEDS_EXPANDED),
561
+ );
562
+ });
563
+ });