@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,245 @@
1
+ /**
2
+ * Tests for `assistant/src/memory/v3/validate.ts`.
3
+ *
4
+ * Coverage matrix — one fixture per defect category plus a clean-tree control:
5
+ * - clean tree → every list empty, every count 0.
6
+ * - danglingChildRefs → a `node:` ref and a `page:` ref to absent targets.
7
+ * - orphanPages → a concept page on disk not wired into the tree; synthetic
8
+ * page-index entries (none here) and reachable pages excluded.
9
+ * - cycles → A → B → A back-edge detected during the full descent.
10
+ * - staleIndex → a parent node whose mtime predates a `node:` child's mtime.
11
+ * - unknownEdgeTargets → a page `edges:` entry pointing at a missing slug.
12
+ *
13
+ * Tests use temp workspaces under `os.tmpdir()`; they never touch `~/.vellum/`.
14
+ * mtimes are pinned with `utimes` so the freshness check is deterministic and
15
+ * independent of write ordering / filesystem timestamp granularity.
16
+ */
17
+
18
+ import { mkdtempSync, rmSync } from "node:fs";
19
+ import { utimes } from "node:fs/promises";
20
+ import { tmpdir } from "node:os";
21
+ import { join } from "node:path";
22
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
23
+
24
+ import { invalidateEdgeIndex } from "../../v2/edge-index.js";
25
+ import { invalidatePageIndex } from "../../v2/page-index.js";
26
+ import { writePage } from "../../v2/page-store.js";
27
+ import type { ConceptPage } from "../../v2/types.js";
28
+ import { invalidateTreeIndex } from "../tree-index.js";
29
+ import { getTreeDir, ROOT_NODE_ID, writeNode } from "../tree-store.js";
30
+ import type { TreeNode } from "../types.js";
31
+ import { validateTree } from "../validate.js";
32
+
33
+ let workspaceDir: string;
34
+
35
+ beforeEach(() => {
36
+ workspaceDir = mkdtempSync(join(tmpdir(), "vellum-tree-validate-test-"));
37
+ });
38
+
39
+ afterEach(() => {
40
+ invalidateTreeIndex();
41
+ invalidatePageIndex();
42
+ invalidateEdgeIndex();
43
+ rmSync(workspaceDir, { recursive: true, force: true });
44
+ });
45
+
46
+ function node(id: string, children: string[], body = `body ${id}`): TreeNode {
47
+ return { id, frontmatter: { children }, body };
48
+ }
49
+
50
+ function page(slug: string, edges: string[] = []): ConceptPage {
51
+ return {
52
+ slug,
53
+ frontmatter: { edges, ref_files: [], ref_urls: [] },
54
+ body: `body ${slug}`,
55
+ };
56
+ }
57
+
58
+ /** Pin a node file's mtime (and atime) to an explicit epoch-ms value. */
59
+ async function setNodeMtime(id: string, mtimeMs: number): Promise<void> {
60
+ const path = join(getTreeDir(workspaceDir), `${id}.md`);
61
+ const t = new Date(mtimeMs);
62
+ await utimes(path, t, t);
63
+ }
64
+
65
+ /**
66
+ * Invalidate every cached index after seeding so the first `validateTree` of a
67
+ * test body sees the on-disk fixture rather than a stale cache.
68
+ */
69
+ function resetCaches(): void {
70
+ invalidateTreeIndex();
71
+ invalidatePageIndex();
72
+ invalidateEdgeIndex();
73
+ }
74
+
75
+ describe("validateTree — clean tree", () => {
76
+ test("returns an empty report for a well-formed tree", async () => {
77
+ // _root → node:people → page:alice ; all refs resolve, alice reachable.
78
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, ["node:people"]));
79
+ await writeNode(workspaceDir, node("people", ["page:alice"]));
80
+ await writePage(workspaceDir, page("alice"));
81
+ // Parent newest so the freshness check never fires on a clean tree.
82
+ await setNodeMtime("people", 1_000);
83
+ await setNodeMtime(ROOT_NODE_ID, 2_000);
84
+ resetCaches();
85
+
86
+ const report = await validateTree(workspaceDir);
87
+
88
+ expect(report.danglingChildRefs).toEqual([]);
89
+ expect(report.danglingChildRefCount).toBe(0);
90
+ expect(report.orphanPages).toEqual([]);
91
+ expect(report.orphanPageCount).toBe(0);
92
+ expect(report.cycles).toEqual([]);
93
+ expect(report.cycleCount).toBe(0);
94
+ expect(report.staleIndex).toEqual([]);
95
+ expect(report.staleIndexCount).toBe(0);
96
+ expect(report.unknownEdgeTargets).toEqual([]);
97
+ expect(report.unknownEdgeTargetCount).toBe(0);
98
+ });
99
+ });
100
+
101
+ describe("validateTree — danglingChildRefs", () => {
102
+ test("flags node: and page: refs whose targets are missing", async () => {
103
+ await writeNode(
104
+ workspaceDir,
105
+ node(ROOT_NODE_ID, ["node:ghost", "page:missing-page"]),
106
+ );
107
+ resetCaches();
108
+
109
+ const report = await validateTree(workspaceDir);
110
+
111
+ expect(report.danglingChildRefs).toEqual([
112
+ { node: ROOT_NODE_ID, ref: "ghost", kind: "node" },
113
+ { node: ROOT_NODE_ID, ref: "missing-page", kind: "page" },
114
+ ]);
115
+ expect(report.danglingChildRefCount).toBe(2);
116
+ });
117
+
118
+ test("does not flag refs whose targets exist", async () => {
119
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, ["node:child"]));
120
+ await writeNode(workspaceDir, node("child", ["page:alice"]));
121
+ await writePage(workspaceDir, page("alice"));
122
+ resetCaches();
123
+
124
+ const report = await validateTree(workspaceDir);
125
+
126
+ expect(report.danglingChildRefs).toEqual([]);
127
+ });
128
+ });
129
+
130
+ describe("validateTree — orphanPages", () => {
131
+ test("flags concept pages not reachable from the root", async () => {
132
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, ["page:reached"]));
133
+ await writePage(workspaceDir, page("reached"));
134
+ await writePage(workspaceDir, page("orphan"));
135
+ resetCaches();
136
+
137
+ const report = await validateTree(workspaceDir);
138
+
139
+ expect(report.orphanPages).toEqual(["orphan"]);
140
+ expect(report.orphanPageCount).toBe(1);
141
+ });
142
+
143
+ test("a page hanging off an unreachable node is still an orphan", async () => {
144
+ // `floating` is not referenced by _root, so its page child is unreachable.
145
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, []));
146
+ await writeNode(workspaceDir, node("floating", ["page:detached"]));
147
+ await writePage(workspaceDir, page("detached"));
148
+ resetCaches();
149
+
150
+ const report = await validateTree(workspaceDir);
151
+
152
+ expect(report.orphanPages).toEqual(["detached"]);
153
+ });
154
+ });
155
+
156
+ describe("validateTree — cycles", () => {
157
+ test("detects an A → B → A node cycle as a back-edge", async () => {
158
+ // _root → node:a → node:b → node:a (cycle closes on the b → a edge).
159
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, ["node:a"]));
160
+ await writeNode(workspaceDir, node("a", ["node:b"]));
161
+ await writeNode(workspaceDir, node("b", ["node:a"]));
162
+ resetCaches();
163
+
164
+ const report = await validateTree(workspaceDir);
165
+
166
+ expect(report.cycles).toEqual([{ from: "b", to: "a" }]);
167
+ expect(report.cycleCount).toBe(1);
168
+ });
169
+
170
+ test("a shared DAG sub-node (two parents, no cycle) is not a cycle", async () => {
171
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, ["node:p1", "node:p2"]));
172
+ await writeNode(workspaceDir, node("p1", ["node:shared"]));
173
+ await writeNode(workspaceDir, node("p2", ["node:shared"]));
174
+ await writeNode(workspaceDir, node("shared", []));
175
+ resetCaches();
176
+
177
+ const report = await validateTree(workspaceDir);
178
+
179
+ expect(report.cycles).toEqual([]);
180
+ });
181
+ });
182
+
183
+ describe("validateTree — staleIndex", () => {
184
+ test("flags a node whose mtime predates a node: child's mtime", async () => {
185
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, ["node:child"]));
186
+ await writeNode(workspaceDir, node("child", []));
187
+ // Parent older than child → stale.
188
+ await setNodeMtime(ROOT_NODE_ID, 1_000);
189
+ await setNodeMtime("child", 5_000);
190
+ resetCaches();
191
+
192
+ const report = await validateTree(workspaceDir);
193
+
194
+ expect(report.staleIndex).toEqual([
195
+ {
196
+ node: ROOT_NODE_ID,
197
+ child: "child",
198
+ nodeMtimeMs: 1_000,
199
+ childMtimeMs: 5_000,
200
+ },
201
+ ]);
202
+ expect(report.staleIndexCount).toBe(1);
203
+ });
204
+
205
+ test("a parent newer than its child is not stale", async () => {
206
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, ["node:child"]));
207
+ await writeNode(workspaceDir, node("child", []));
208
+ await setNodeMtime("child", 1_000);
209
+ await setNodeMtime(ROOT_NODE_ID, 5_000);
210
+ resetCaches();
211
+
212
+ const report = await validateTree(workspaceDir);
213
+
214
+ expect(report.staleIndex).toEqual([]);
215
+ });
216
+ });
217
+
218
+ describe("validateTree — unknownEdgeTargets", () => {
219
+ test("flags a page edge pointing at a missing slug", async () => {
220
+ await writeNode(workspaceDir, node(ROOT_NODE_ID, ["page:alice"]));
221
+ await writePage(workspaceDir, page("alice", ["nonexistent"]));
222
+ resetCaches();
223
+
224
+ const report = await validateTree(workspaceDir);
225
+
226
+ expect(report.unknownEdgeTargets).toEqual([
227
+ { from: "alice", to: "nonexistent" },
228
+ ]);
229
+ expect(report.unknownEdgeTargetCount).toBe(1);
230
+ });
231
+
232
+ test("an edge to an existing page is not flagged", async () => {
233
+ await writeNode(
234
+ workspaceDir,
235
+ node(ROOT_NODE_ID, ["page:alice", "page:bob"]),
236
+ );
237
+ await writePage(workspaceDir, page("alice", ["bob"]));
238
+ await writePage(workspaceDir, page("bob"));
239
+ resetCaches();
240
+
241
+ const report = await validateTree(workspaceDir);
242
+
243
+ expect(report.unknownEdgeTargets).toEqual([]);
244
+ });
245
+ });
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Memory v3 — learned weighted auto-edge store.
3
+ *
4
+ * Read/write helpers over `memory_v3_auto_edges` (migration 263) — the
5
+ * **learned** association graph, a distinct class from the curated `edges:`
6
+ * frontmatter graph. Each row is a weighted directed pair `source → target`
7
+ * that the edge-learning job accrues from *used* co-activations and decays over
8
+ * wall-clock time.
9
+ *
10
+ * Three primitives:
11
+ * - {@link reinforce} — bump a pair's weight, but only for a *used*
12
+ * co-activation (we reinforce usefulness, not mere retrieval).
13
+ * - {@link decay} — multiplicatively decay all weights toward zero on a
14
+ * half-life schedule, so a pair that stops being reinforced fades. This is
15
+ * the rich-get-richer counterweight: weight is a leaky integrator, not a
16
+ * monotone counter.
17
+ * - {@link aboveThreshold} — project the learned graph to the
18
+ * `ReadonlyMap<source, ReadonlySet<target>>` adjacency that edge
19
+ * expansion's `extraAdjacency` seam consumes (only pairs at/above a weight
20
+ * threshold traverse).
21
+ *
22
+ * The decay model mirrors v2's injection-events EMA: `λ = ln 2 / halfLife`, and
23
+ * a pair decays by `exp(-λ × elapsed)` since its `last_reinforced_at`.
24
+ */
25
+
26
+ import { getLogger } from "../../util/logger.js";
27
+ import type { DrizzleDb } from "../db-connection.js";
28
+ import { getSqliteFrom } from "../db-connection.js";
29
+
30
+ const log = getLogger("memory-v3-auto-edges");
31
+
32
+ /** Weight added to a pair per *used* co-activation reinforcement. */
33
+ export const REINFORCE_INCREMENT = 1;
34
+
35
+ /** Weights below this after decay are pruned rather than kept as dead rows. */
36
+ export const PRUNE_FLOOR = 0.01;
37
+
38
+ /** A learned auto-edge, as read back from the table. */
39
+ export interface AutoEdgeRow {
40
+ sourceSlug: string;
41
+ targetSlug: string;
42
+ weight: number;
43
+ lastReinforcedAt: number;
44
+ }
45
+
46
+ /**
47
+ * Reinforce the directed pair `source → target`: bump its weight by
48
+ * {@link REINFORCE_INCREMENT} and stamp `last_reinforced_at = now`. **Only call
49
+ * this for a *used* co-activation** — the edge graph encodes which associations
50
+ * actually proved load-bearing for a turn, not which pages merely surfaced
51
+ * together. (The caller decides usedness from the co-activation's `used` flag;
52
+ * this primitive is unconditional so it stays composable.)
53
+ *
54
+ * UPSERT on the `(source, target)` primary key: a new pair starts at the
55
+ * increment; an existing pair accrues on top of its current weight (after the
56
+ * latest decay pass, since decay rewrites weight in place).
57
+ *
58
+ * Best-effort: a failed write must never abort the edge-learning job.
59
+ */
60
+ export function reinforce(
61
+ database: DrizzleDb,
62
+ source: string,
63
+ target: string,
64
+ now: number,
65
+ ): void {
66
+ try {
67
+ const raw = getSqliteFrom(database);
68
+ raw
69
+ .prepare(
70
+ `INSERT INTO memory_v3_auto_edges
71
+ (source_slug, target_slug, weight, last_reinforced_at)
72
+ VALUES (?, ?, ?, ?)
73
+ ON CONFLICT(source_slug, target_slug) DO UPDATE SET
74
+ weight = weight + ?,
75
+ last_reinforced_at = ?`,
76
+ )
77
+ .run(source, target, REINFORCE_INCREMENT, now, REINFORCE_INCREMENT, now);
78
+ } catch (err) {
79
+ log.warn(
80
+ { err, source, target },
81
+ "failed to reinforce auto-edge; continuing",
82
+ );
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Multiplicatively decay every auto-edge weight toward zero on a half-life
88
+ * schedule: `weight ← weight × exp(-λ × (now − last_reinforced_at))`, with
89
+ * `λ = ln 2 / halfLifeMs`. A pair last reinforced one half-life ago halves; two
90
+ * half-lives ago quarters; and so on. `last_reinforced_at` advances to `now`
91
+ * so successive decay passes don't double-count the same elapsed interval.
92
+ *
93
+ * Pairs whose decayed weight falls below {@link PRUNE_FLOOR} are deleted so the
94
+ * learned graph doesn't accumulate a long tail of effectively-dead edges.
95
+ *
96
+ * Returns the number of rows pruned, for the job's structured log.
97
+ */
98
+ export function decay(
99
+ database: DrizzleDb,
100
+ now: number,
101
+ halfLifeMs: number,
102
+ ): number {
103
+ if (halfLifeMs <= 0) return 0;
104
+ const lambda = Math.LN2 / halfLifeMs;
105
+ try {
106
+ const raw = getSqliteFrom(database);
107
+ const rows = raw
108
+ .query(
109
+ `SELECT source_slug, target_slug, weight, last_reinforced_at
110
+ FROM memory_v3_auto_edges`,
111
+ )
112
+ .all() as Array<{
113
+ source_slug: string;
114
+ target_slug: string;
115
+ weight: number;
116
+ last_reinforced_at: number;
117
+ }>;
118
+ if (rows.length === 0) return 0;
119
+
120
+ const update = raw.prepare(
121
+ `UPDATE memory_v3_auto_edges
122
+ SET weight = ?, last_reinforced_at = ?
123
+ WHERE source_slug = ? AND target_slug = ?`,
124
+ );
125
+ const prune = raw.prepare(
126
+ `DELETE FROM memory_v3_auto_edges
127
+ WHERE source_slug = ? AND target_slug = ?`,
128
+ );
129
+
130
+ let pruned = 0;
131
+ const apply = raw.transaction(() => {
132
+ for (const row of rows) {
133
+ // Future timestamps (clock skew) would amplify rather than decay — clamp
134
+ // elapsed at 0 so decay only ever shrinks weight.
135
+ const elapsed = Math.max(0, now - row.last_reinforced_at);
136
+ const decayed = row.weight * Math.exp(-lambda * elapsed);
137
+ if (decayed < PRUNE_FLOOR) {
138
+ prune.run(row.source_slug, row.target_slug);
139
+ pruned += 1;
140
+ } else {
141
+ update.run(decayed, now, row.source_slug, row.target_slug);
142
+ }
143
+ }
144
+ });
145
+ apply();
146
+ return pruned;
147
+ } catch (err) {
148
+ log.warn({ err }, "failed to decay auto-edges; continuing");
149
+ return 0;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Project the learned graph to the `extraAdjacency` shape edge expansion
155
+ * consumes: `source → Set<target>` for every pair whose weight is at or above
156
+ * `threshold`. Edge expansion thresholds nothing itself — it merges whatever
157
+ * adjacency it's handed — so this read is where the weight cutoff is applied.
158
+ *
159
+ * Returns an empty map on any read failure so the caller (a best-effort read
160
+ * lane) degrades to "no learned edges" rather than aborting retrieval.
161
+ */
162
+ export function aboveThreshold(
163
+ database: DrizzleDb,
164
+ threshold: number,
165
+ ): Map<string, Set<string>> {
166
+ const adjacency = new Map<string, Set<string>>();
167
+ try {
168
+ const raw = getSqliteFrom(database);
169
+ const rows = raw
170
+ .query(
171
+ `SELECT source_slug, target_slug
172
+ FROM memory_v3_auto_edges
173
+ WHERE weight >= ?
174
+ ORDER BY source_slug ASC, target_slug ASC`,
175
+ )
176
+ .all(threshold) as Array<{ source_slug: string; target_slug: string }>;
177
+ for (const row of rows) {
178
+ let targets = adjacency.get(row.source_slug);
179
+ if (!targets) {
180
+ targets = new Set<string>();
181
+ adjacency.set(row.source_slug, targets);
182
+ }
183
+ targets.add(row.target_slug);
184
+ }
185
+ } catch (err) {
186
+ log.warn({ err, threshold }, "failed to read auto-edges; continuing");
187
+ }
188
+ return adjacency;
189
+ }
190
+
191
+ /**
192
+ * Read the top-weight auto-edges, heaviest first, capped at `limit`. The
193
+ * edge-learning job surfaces these as advisory promotion candidates for the
194
+ * assistant to ratify into curated `edges:` during consolidation.
195
+ */
196
+ export function topByWeight(database: DrizzleDb, limit: number): AutoEdgeRow[] {
197
+ if (limit <= 0) return [];
198
+ try {
199
+ const raw = getSqliteFrom(database);
200
+ const rows = raw
201
+ .query(
202
+ `SELECT source_slug, target_slug, weight, last_reinforced_at
203
+ FROM memory_v3_auto_edges
204
+ ORDER BY weight DESC, source_slug ASC, target_slug ASC
205
+ LIMIT ?`,
206
+ )
207
+ .all(limit) as Array<{
208
+ source_slug: string;
209
+ target_slug: string;
210
+ weight: number;
211
+ last_reinforced_at: number;
212
+ }>;
213
+ return rows.map((r) => ({
214
+ sourceSlug: r.source_slug,
215
+ targetSlug: r.target_slug,
216
+ weight: r.weight,
217
+ lastReinforcedAt: r.last_reinforced_at,
218
+ }));
219
+ } catch (err) {
220
+ log.warn({ err, limit }, "failed to read top auto-edges; continuing");
221
+ return [];
222
+ }
223
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Memory v3 — co-activation store.
3
+ *
4
+ * Best-effort read/write helpers over `memory_v3_coactivation` (migration
5
+ * 262). Each row is a pass-1 → pass-N co-activation pair observed during a
6
+ * single v3 retrieval loop: a `target_slug` first surfaced on a later descent
7
+ * pass was co-selected alongside a `source_slug` that surfaced on pass 1,
8
+ * `pass_gap = passOf(target) − passOf(source)`.
9
+ *
10
+ * This is the raw gradient signal — edge-learning reconciles it into curated
11
+ * graph edge weights later. Writes are off the retrieval critical path: a
12
+ * failed insert here must never abort the turn on top of a successful
13
+ * retrieval the caller already depends on.
14
+ */
15
+
16
+ import { getLogger } from "../../util/logger.js";
17
+ import type { DrizzleDb } from "../db-connection.js";
18
+ import { getSqliteFrom } from "../db-connection.js";
19
+
20
+ const log = getLogger("memory-v3-coactivation");
21
+
22
+ /** One co-activation pair to persist. */
23
+ export interface CoactivationRow {
24
+ conversationId: string;
25
+ turn: number;
26
+ sourceSlug: string;
27
+ targetSlug: string;
28
+ passGap: number;
29
+ /** Usefulness flag. 0 at emit time; reconciled later by edge-learning. */
30
+ used: number;
31
+ createdAt: number;
32
+ }
33
+
34
+ /** A persisted co-activation row, as read back from the table. */
35
+ export interface PersistedCoactivationRow {
36
+ id: number;
37
+ conversationId: string;
38
+ turn: number;
39
+ sourceSlug: string;
40
+ targetSlug: string;
41
+ passGap: number;
42
+ used: number;
43
+ createdAt: number;
44
+ }
45
+
46
+ /**
47
+ * Append co-activation rows. Best-effort — a SQLite write must never abort the
48
+ * agent turn on top of a successful retrieval the rest of the caller depends
49
+ * on. Mirrors {@link recordInjectionEvents}.
50
+ */
51
+ export function recordCoactivations(
52
+ database: DrizzleDb,
53
+ rows: readonly CoactivationRow[],
54
+ ): void {
55
+ if (rows.length === 0) return;
56
+ try {
57
+ const raw = getSqliteFrom(database);
58
+ const insert = raw.prepare(
59
+ `INSERT INTO memory_v3_coactivation
60
+ (conversation_id, turn, source_slug, target_slug, pass_gap, used, created_at)
61
+ VALUES (?, ?, ?, ?, ?, ?, ?)`,
62
+ );
63
+ const append = raw.transaction((items: readonly CoactivationRow[]) => {
64
+ for (const r of items) {
65
+ insert.run(
66
+ r.conversationId,
67
+ r.turn,
68
+ r.sourceSlug,
69
+ r.targetSlug,
70
+ r.passGap,
71
+ r.used,
72
+ r.createdAt,
73
+ );
74
+ }
75
+ });
76
+ append(rows);
77
+ } catch (err) {
78
+ log.warn(
79
+ { err, rowCount: rows.length },
80
+ "failed to record co-activations; continuing",
81
+ );
82
+ }
83
+ }
84
+
85
+ /**
86
+ * Read co-activation rows, oldest first. When `since` is provided, only rows
87
+ * with `created_at >= since` are returned.
88
+ */
89
+ export function readCoactivations(
90
+ database: DrizzleDb,
91
+ since?: number,
92
+ ): PersistedCoactivationRow[] {
93
+ const raw = getSqliteFrom(database);
94
+ const where = since !== undefined ? `WHERE created_at >= ?` : ``;
95
+ const params = since !== undefined ? [since] : [];
96
+ const rows = raw
97
+ .query(
98
+ `SELECT id, conversation_id, turn, source_slug, target_slug,
99
+ pass_gap, used, created_at
100
+ FROM memory_v3_coactivation
101
+ ${where}
102
+ ORDER BY created_at ASC, id ASC`,
103
+ )
104
+ .all(...params) as Array<{
105
+ id: number;
106
+ conversation_id: string;
107
+ turn: number;
108
+ source_slug: string;
109
+ target_slug: string;
110
+ pass_gap: number;
111
+ used: number;
112
+ created_at: number;
113
+ }>;
114
+ return rows.map((r) => ({
115
+ id: r.id,
116
+ conversationId: r.conversation_id,
117
+ turn: r.turn,
118
+ sourceSlug: r.source_slug,
119
+ targetSlug: r.target_slug,
120
+ passGap: r.pass_gap,
121
+ used: r.used,
122
+ createdAt: r.created_at,
123
+ }));
124
+ }